Files
firebase-ios-sdk/FirebaseAuth/Sources/Swift/Backend/RPC/VerifyAssertionRequest.swift
T
2024-07-18 15:05:15 -07:00

223 lines
7.4 KiB
Swift

// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
/// The "verifyAssertion" endpoint.
private let kVerifyAssertionEndpoint = "verifyAssertion"
/// The key for the "providerId" value in the request.
private let kProviderIDKey = "providerId"
/// The key for the "id_token" value in the request.
private let kProviderIDTokenKey = "id_token"
/// The key for the "nonce" value in the request.
private let kProviderNonceKey = "nonce"
/// The key for the "access_token" value in the request.
private let kProviderAccessTokenKey = "access_token"
/// The key for the "oauth_token_secret" value in the request.
private let kProviderOAuthTokenSecretKey = "oauth_token_secret"
/// The key for the "identifier" value in the request.
private let kIdentifierKey = "identifier"
/// The key for the "requestUri" value in the request.
private let kRequestURIKey = "requestUri"
/// The key for the "postBody" value in the request.
private let kPostBodyKey = "postBody"
/// The key for the "pendingToken" value in the request.
private let kPendingTokenKey = "pendingToken"
/// The key for the "autoCreate" value in the request.
private let kAutoCreateKey = "autoCreate"
/// The key for the "idToken" value in the request. This is actually the STS Access Token,
/// despite its confusing (backwards compatible) parameter name.
private let kIDTokenKey = "idToken"
/// The key for the "returnSecureToken" value in the request.
private let kReturnSecureTokenKey = "returnSecureToken"
/// The key for the "returnIdpCredential" value in the request.
private let kReturnIDPCredentialKey = "returnIdpCredential"
/// The key for the "sessionID" value in the request.
private let kSessionIDKey = "sessionId"
/// The key for the tenant id value in the request.
private let kTenantIDKey = "tenantId"
/// The key for the "user" value in the request. The value is a JSON object that contains the
/// name of the user.
private let kUserKey = "user"
/// The key for the "name" value in the request. The value is a JSON object that contains the
/// first and/or last name of the user.
private let kNameKey = "name"
/// The key for the "firstName" value in the request.
private let kFirstNameKey = "firstName"
/// The key for the "lastName" value in the request.
private let kLastNameKey = "lastName"
/// Represents the parameters for the verifyAssertion endpoint.
/// See https://developers.google.com/identity/toolkit/web/reference/relyingparty/verifyAssertion
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
class VerifyAssertionRequest: IdentityToolkitRequest, AuthRPCRequest {
typealias Response = VerifyAssertionResponse
/// The URI to which the IDP redirects the user back. It may contain federated login result
/// params added by the IDP.
var requestURI: String?
/// The Firebase ID Token for the IDP pending to be confirmed by the user.
var pendingToken: String?
/// The STS Access Token for the authenticated user, only needed for linking the user.
var accessToken: String?
/// Whether the response should return access token and refresh token directly.
/// The default value is `true` .
var returnSecureToken: Bool = true
// MARK: - Components of "postBody"
/// The ID of the IDP whose credentials are being presented to the endpoint.
let providerID: String
/// An access token from the IDP.
var providerAccessToken: String?
/// An ID Token from the IDP.
var providerIDToken: String?
/// An raw nonce from the IDP.
var providerRawNonce: String?
/// Whether the response should return the IDP credential directly.
var returnIDPCredential: Bool = true
/// A session ID used to map this request to a headful-lite flow.
var sessionID: String?
/// An OAuth client secret from the IDP.
var providerOAuthTokenSecret: String?
/// The originally entered email in the UI.
var inputEmail: String?
/// A flag that indicates whether or not the user should be automatically created.
var autoCreate: Bool = true
/// A full name from the IdP.
var fullName: PersonNameComponents?
init(providerID: String, requestConfiguration: AuthRequestConfiguration) {
self.providerID = providerID
super.init(endpoint: kVerifyAssertionEndpoint, requestConfiguration: requestConfiguration)
}
func unencodedHTTPRequestBody() throws -> [String: AnyHashable] {
var components = URLComponents()
var queryItems: [URLQueryItem] = [URLQueryItem(name: kProviderIDKey, value: providerID)]
if let providerIDToken = providerIDToken {
queryItems.append(URLQueryItem(name: kProviderIDTokenKey, value: providerIDToken))
}
if let providerRawNonce = providerRawNonce {
queryItems.append(URLQueryItem(name: kProviderNonceKey, value: providerRawNonce))
}
if let providerAccessToken = providerAccessToken {
queryItems
.append(URLQueryItem(name: kProviderAccessTokenKey, value: providerAccessToken))
}
guard providerIDToken != nil || providerAccessToken != nil || pendingToken != nil ||
requestURI != nil else {
fatalError("One of IDToken, accessToken, pendingToken, or requestURI must be supplied.")
}
if let providerOAuthTokenSecret = providerOAuthTokenSecret {
queryItems
.append(URLQueryItem(name: kProviderOAuthTokenSecretKey,
value: providerOAuthTokenSecret))
}
if let inputEmail = inputEmail {
queryItems.append(URLQueryItem(name: kIdentifierKey, value: inputEmail))
}
if fullName?.givenName != nil || fullName?.familyName != nil {
var nameDict = [String: String]()
if let given = fullName?.givenName {
nameDict[kFirstNameKey] = given
}
if let lastName = fullName?.familyName {
nameDict[kLastNameKey] = lastName
}
let userDict = [kNameKey: nameDict]
do {
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
let userJson = try encoder.encode(userDict)
let jsonString = String(data: userJson, encoding: .utf8)
queryItems.append(URLQueryItem(name: kUserKey, value: jsonString))
} catch {
fatalError("Auth Internal error: failed to serialize dictionary to json: \(error)")
}
}
components.queryItems = queryItems
var body: [String: AnyHashable] = [
kRequestURIKey: requestURI ?? "http://localhost", // Unused by server, but required
]
if let query = components.query {
body[kPostBodyKey] = query
}
if let pendingToken = pendingToken {
body[kPendingTokenKey] = pendingToken
}
if let accessToken = accessToken {
body[kIDTokenKey] = accessToken
}
if returnSecureToken {
body[kReturnSecureTokenKey] = true
}
if returnIDPCredential {
body[kReturnIDPCredentialKey] = true
}
if let sessionID = sessionID {
body[kSessionIDKey] = sessionID
}
if let tenantID = tenantID {
body[kTenantIDKey] = tenantID
}
body[kAutoCreateKey] = autoCreate
return body
}
}