|
|
@@ -22,82 +22,61 @@ import Foundation
|
|
|
#endif
|
|
|
|
|
|
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
|
|
|
-protocol AuthBackendRPCIssuer {
|
|
|
- /// Asynchronously send a HTTP request.
|
|
|
- /// - Parameter request: The request to be made.
|
|
|
- /// - Parameter body: Request body.
|
|
|
- /// - Parameter contentType: Content type of the body.
|
|
|
- /// - Parameter completionHandler: Handles HTTP response. Invoked asynchronously
|
|
|
- /// on the auth global work queue in the future.
|
|
|
- func asyncCallToURL<T: AuthRPCRequest>(with request: T,
|
|
|
- body: Data?,
|
|
|
- contentType: String) async -> (Data?, Error?)
|
|
|
-}
|
|
|
-
|
|
|
-@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
|
|
|
-class AuthBackendRPCIssuerImplementation: AuthBackendRPCIssuer {
|
|
|
- let fetcherService: GTMSessionFetcherService
|
|
|
-
|
|
|
- init() {
|
|
|
- fetcherService = GTMSessionFetcherService()
|
|
|
- fetcherService.userAgent = AuthBackend.authUserAgent()
|
|
|
- fetcherService.callbackQueue = kAuthGlobalWorkQueue
|
|
|
-
|
|
|
- // Avoid reusing the session to prevent
|
|
|
- // https://github.com/firebase/firebase-ios-sdk/issues/1261
|
|
|
- fetcherService.reuseSession = false
|
|
|
- }
|
|
|
-
|
|
|
- func asyncCallToURL<T: AuthRPCRequest>(with request: T,
|
|
|
- body: Data?,
|
|
|
- contentType: String) async -> (Data?, Error?) {
|
|
|
- let requestConfiguration = request.requestConfiguration()
|
|
|
- let request = await AuthBackend.request(withURL: request.requestURL(),
|
|
|
- contentType: contentType,
|
|
|
- requestConfiguration: requestConfiguration)
|
|
|
- let fetcher = fetcherService.fetcher(with: request)
|
|
|
- if let _ = requestConfiguration.emulatorHostAndPort {
|
|
|
- fetcher.allowLocalhostRequest = true
|
|
|
- fetcher.allowedInsecureSchemes = ["http"]
|
|
|
- }
|
|
|
- fetcher.bodyData = body
|
|
|
-
|
|
|
- return await withUnsafeContinuation { continuation in
|
|
|
- fetcher.beginFetch { data, error in
|
|
|
- continuation.resume(returning: (data, error))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+protocol AuthBackendProtocol {
|
|
|
+ func call<T: AuthRPCRequest>(with request: T) async throws -> T.Response
|
|
|
}
|
|
|
|
|
|
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
|
|
|
-class AuthBackend {
|
|
|
+class AuthBackend: AuthBackendProtocol {
|
|
|
static func authUserAgent() -> String {
|
|
|
return "FirebaseAuth.iOS/\(FirebaseVersion()) \(GTMFetcherStandardUserAgentString(nil))"
|
|
|
}
|
|
|
|
|
|
- private static var realRPCBackend = AuthBackendRPCImplementation()
|
|
|
- private static var gBackendImplementation = realRPCBackend
|
|
|
+ static func call<T: AuthRPCRequest>(with request: T) async throws -> T.Response {
|
|
|
+ return try await shared.call(with: request)
|
|
|
+ }
|
|
|
|
|
|
- class func setTestRPCIssuer(issuer: AuthBackendRPCIssuer) {
|
|
|
- gBackendImplementation.rpcIssuer = issuer
|
|
|
+ static func setTestRPCIssuer(issuer: AuthBackendRPCIssuer) {
|
|
|
+ shared.rpcIssuer = issuer
|
|
|
}
|
|
|
|
|
|
- class func resetRPCIssuer() {
|
|
|
- gBackendImplementation.rpcIssuer = realRPCBackend.rpcIssuer
|
|
|
+ static func resetRPCIssuer() {
|
|
|
+ shared.rpcIssuer = AuthBackendRPCIssuer()
|
|
|
}
|
|
|
|
|
|
- class func implementation() -> AuthBackendImplementation {
|
|
|
- return gBackendImplementation
|
|
|
+ private static let shared: AuthBackend = .init(rpcIssuer: AuthBackendRPCIssuer())
|
|
|
+
|
|
|
+ private var rpcIssuer: any AuthBackendRPCIssuerProtocol
|
|
|
+
|
|
|
+ init(rpcIssuer: any AuthBackendRPCIssuerProtocol) {
|
|
|
+ self.rpcIssuer = rpcIssuer
|
|
|
}
|
|
|
|
|
|
- class func call<T: AuthRPCRequest>(with request: T) async throws -> T.Response {
|
|
|
- return try await implementation().call(with: request)
|
|
|
+ /// Calls the RPC using HTTP request.
|
|
|
+ /// Possible error responses:
|
|
|
+ /// * See FIRAuthInternalErrorCodeRPCRequestEncodingError
|
|
|
+ /// * See FIRAuthInternalErrorCodeJSONSerializationError
|
|
|
+ /// * See FIRAuthInternalErrorCodeNetworkError
|
|
|
+ /// * See FIRAuthInternalErrorCodeUnexpectedErrorResponse
|
|
|
+ /// * See FIRAuthInternalErrorCodeUnexpectedResponse
|
|
|
+ /// * See FIRAuthInternalErrorCodeRPCResponseDecodingError
|
|
|
+ /// - Parameter request: The request.
|
|
|
+ /// - Returns: The response.
|
|
|
+ func call<T: AuthRPCRequest>(with request: T) async throws -> T.Response {
|
|
|
+ let response = try await callInternal(with: request)
|
|
|
+ if let auth = request.requestConfiguration().auth,
|
|
|
+ let mfaError = Self.generateMFAError(response: response, auth: auth) {
|
|
|
+ throw mfaError
|
|
|
+ } else if let error = Self.phoneCredentialInUse(response: response) {
|
|
|
+ throw error
|
|
|
+ } else {
|
|
|
+ return response
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- class func request(withURL url: URL,
|
|
|
- contentType: String,
|
|
|
- requestConfiguration: AuthRequestConfiguration) async -> URLRequest {
|
|
|
+ static func request(withURL url: URL,
|
|
|
+ contentType: String,
|
|
|
+ requestConfiguration: AuthRequestConfiguration) async -> URLRequest {
|
|
|
// Kick off tasks for the async header values.
|
|
|
async let heartbeatsHeaderValue = requestConfiguration.heartbeatLogger?.asyncHeaderValue()
|
|
|
async let appCheckTokenHeaderValue = requestConfiguration.appCheck?
|
|
|
@@ -132,41 +111,11 @@ class AuthBackend {
|
|
|
}
|
|
|
return request
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
|
|
|
-protocol AuthBackendImplementation {
|
|
|
- func call<T: AuthRPCRequest>(with request: T) async throws -> T.Response
|
|
|
-}
|
|
|
-
|
|
|
-@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
|
|
|
-private class AuthBackendRPCImplementation: AuthBackendImplementation {
|
|
|
- var rpcIssuer: AuthBackendRPCIssuer = AuthBackendRPCIssuerImplementation()
|
|
|
-
|
|
|
- /// Calls the RPC using HTTP request.
|
|
|
- /// Possible error responses:
|
|
|
- /// * See FIRAuthInternalErrorCodeRPCRequestEncodingError
|
|
|
- /// * See FIRAuthInternalErrorCodeJSONSerializationError
|
|
|
- /// * See FIRAuthInternalErrorCodeNetworkError
|
|
|
- /// * See FIRAuthInternalErrorCodeUnexpectedErrorResponse
|
|
|
- /// * See FIRAuthInternalErrorCodeUnexpectedResponse
|
|
|
- /// * See FIRAuthInternalErrorCodeRPCResponseDecodingError
|
|
|
- /// - Parameter request: The request.
|
|
|
- /// - Returns: The response.
|
|
|
- fileprivate func call<T: AuthRPCRequest>(with request: T) async throws -> T.Response {
|
|
|
- let response = try await callInternal(with: request)
|
|
|
- if let auth = request.requestConfiguration().auth,
|
|
|
- let mfaError = Self.generateMFAError(response: response, auth: auth) {
|
|
|
- throw mfaError
|
|
|
- } else if let error = Self.phoneCredentialInUse(response: response) {
|
|
|
- throw error
|
|
|
- } else {
|
|
|
- return response
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- #if os(iOS)
|
|
|
- private class func generateMFAError(response: AuthRPCResponse, auth: Auth) -> Error? {
|
|
|
+ private static func generateMFAError(response: AuthRPCResponse, auth: Auth) -> Error? {
|
|
|
+ #if !os(iOS)
|
|
|
+ return nil
|
|
|
+ #else
|
|
|
if let mfaResponse = response as? AuthMFAResponse,
|
|
|
mfaResponse.idToken == nil,
|
|
|
let enrollments = mfaResponse.mfaInfo {
|
|
|
@@ -189,17 +138,15 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
|
|
|
} else {
|
|
|
return nil
|
|
|
}
|
|
|
- }
|
|
|
- #else
|
|
|
- private class func generateMFAError(response: AuthRPCResponse, auth: Auth?) -> Error? {
|
|
|
- return nil
|
|
|
- }
|
|
|
- #endif
|
|
|
+ #endif // !os(iOS)
|
|
|
+ }
|
|
|
|
|
|
- #if os(iOS)
|
|
|
- // Check whether or not the successful response is actually the special case phone
|
|
|
- // auth flow that returns a temporary proof and phone number.
|
|
|
- private class func phoneCredentialInUse(response: AuthRPCResponse) -> Error? {
|
|
|
+ // Check whether or not the successful response is actually the special case phone
|
|
|
+ // auth flow that returns a temporary proof and phone number.
|
|
|
+ private static func phoneCredentialInUse(response: AuthRPCResponse) -> Error? {
|
|
|
+ #if !os(iOS)
|
|
|
+ return nil
|
|
|
+ #else
|
|
|
if let phoneAuthResponse = response as? VerifyPhoneNumberResponse,
|
|
|
let phoneNumber = phoneAuthResponse.phoneNumber,
|
|
|
phoneNumber.count > 0,
|
|
|
@@ -214,12 +161,8 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
|
|
|
} else {
|
|
|
return nil
|
|
|
}
|
|
|
- }
|
|
|
- #else
|
|
|
- private class func phoneCredentialInUse(response: AuthRPCResponse) -> Error? {
|
|
|
- return nil
|
|
|
- }
|
|
|
- #endif
|
|
|
+ #endif // !os(iOS)
|
|
|
+ }
|
|
|
|
|
|
/// Calls the RPC using HTTP request.
|
|
|
///
|
|
|
@@ -308,7 +251,7 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
|
|
|
}
|
|
|
dictionary = decodedDictionary
|
|
|
|
|
|
- let response = T.Response()
|
|
|
+ var response = T.Response()
|
|
|
|
|
|
// At this point we either have an error with successfully decoded
|
|
|
// details in the body, or we have a response which must pass further
|
|
|
@@ -318,7 +261,7 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
|
|
|
if error != nil {
|
|
|
if let errorDictionary = dictionary["error"] as? [String: AnyHashable] {
|
|
|
if let errorMessage = errorDictionary["message"] as? String {
|
|
|
- if let clientError = AuthBackendRPCImplementation.clientError(
|
|
|
+ if let clientError = Self.clientError(
|
|
|
withServerErrorMessage: errorMessage,
|
|
|
errorDictionary: errorDictionary,
|
|
|
response: response,
|
|
|
@@ -351,7 +294,7 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
|
|
|
if let verifyAssertionRequest = request as? VerifyAssertionRequest {
|
|
|
if verifyAssertionRequest.returnIDPCredential {
|
|
|
if let errorMessage = dictionary["errorMessage"] as? String {
|
|
|
- if let clientError = AuthBackendRPCImplementation.clientError(
|
|
|
+ if let clientError = Self.clientError(
|
|
|
withServerErrorMessage: errorMessage,
|
|
|
errorDictionary: dictionary,
|
|
|
response: response,
|
|
|
@@ -365,10 +308,10 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
|
|
|
return response
|
|
|
}
|
|
|
|
|
|
- private class func clientError(withServerErrorMessage serverErrorMessage: String,
|
|
|
- errorDictionary: [String: Any],
|
|
|
- response: AuthRPCResponse,
|
|
|
- error: Error?) -> Error? {
|
|
|
+ private static func clientError(withServerErrorMessage serverErrorMessage: String,
|
|
|
+ errorDictionary: [String: Any],
|
|
|
+ response: AuthRPCResponse,
|
|
|
+ error: Error?) -> Error? {
|
|
|
let split = serverErrorMessage.split(separator: ":")
|
|
|
let shortErrorMessage = split.first?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
let serverDetailErrorMessage = String(split.count > 1 ? split[1] : "")
|