@@ -22,82 +22,53 @@ import Foundation
2222#endif
2323
2424@available ( iOS 13 , tvOS 13 , macOS 10 . 15 , macCatalyst 13 , watchOS 7 , * )
25- protocol AuthBackendRPCIssuer {
26- /// Asynchronously send a HTTP request.
27- /// - Parameter request: The request to be made.
28- /// - Parameter body: Request body.
29- /// - Parameter contentType: Content type of the body.
30- /// - Parameter completionHandler: Handles HTTP response. Invoked asynchronously
31- /// on the auth global work queue in the future.
32- func asyncCallToURL< T: AuthRPCRequest > ( with request: T ,
33- body: Data ? ,
34- contentType: String ) async -> ( Data ? , Error ? )
35- }
36-
37- @available ( iOS 13 , tvOS 13 , macOS 10 . 15 , macCatalyst 13 , watchOS 7 , * )
38- class AuthBackendRPCIssuerImplementation : AuthBackendRPCIssuer {
39- let fetcherService : GTMSessionFetcherService
40-
41- init ( ) {
42- fetcherService = GTMSessionFetcherService ( )
43- fetcherService. userAgent = AuthBackend . authUserAgent ( )
44- fetcherService. callbackQueue = kAuthGlobalWorkQueue
45-
46- // Avoid reusing the session to prevent
47- // https://github.com/firebase/firebase-ios-sdk/issues/1261
48- fetcherService. reuseSession = false
49- }
50-
51- func asyncCallToURL< T: AuthRPCRequest > ( with request: T ,
52- body: Data ? ,
53- contentType: String ) async -> ( Data ? , Error ? ) {
54- let requestConfiguration = request. requestConfiguration ( )
55- let request = await AuthBackend . request ( withURL: request. requestURL ( ) ,
56- contentType: contentType,
57- requestConfiguration: requestConfiguration)
58- let fetcher = fetcherService. fetcher ( with: request)
59- if let _ = requestConfiguration. emulatorHostAndPort {
60- fetcher. allowLocalhostRequest = true
61- fetcher. allowedInsecureSchemes = [ " http " ]
62- }
63- fetcher. bodyData = body
64-
65- return await withUnsafeContinuation { continuation in
66- fetcher. beginFetch { data, error in
67- continuation. resume ( returning: ( data, error) )
68- }
69- }
70- }
25+ protocol AuthBackendProtocol {
26+ func call< T: AuthRPCRequest > ( with request: T ) async throws -> T . Response
7127}
7228
7329@available ( iOS 13 , tvOS 13 , macOS 10 . 15 , macCatalyst 13 , watchOS 7 , * )
74- class AuthBackend {
30+ class AuthBackend : AuthBackendProtocol {
7531 static func authUserAgent( ) -> String {
7632 return " FirebaseAuth.iOS/ \( FirebaseVersion ( ) ) \( GTMFetcherStandardUserAgentString ( nil ) ) "
7733 }
7834
79- private static var realRPCBackend = AuthBackendRPCImplementation ( )
80- private static var gBackendImplementation = realRPCBackend
81-
82- class func setTestRPCIssuer( issuer: AuthBackendRPCIssuer ) {
83- gBackendImplementation. rpcIssuer = issuer
35+ static func call< T: AuthRPCRequest > ( with request: T ) async throws -> T . Response {
36+ return try await shared. call ( with: request)
8437 }
8538
86- class func resetRPCIssuer( ) {
87- gBackendImplementation. rpcIssuer = realRPCBackend. rpcIssuer
88- }
39+ private static let shared : AuthBackend = . init( rpcIssuer: AuthBackendRPCIssuer ( ) )
8940
90- class func implementation( ) -> AuthBackendImplementation {
91- return gBackendImplementation
41+ private let rpcIssuer : any AuthBackendRPCIssuerProtocol
42+
43+ init ( rpcIssuer: any AuthBackendRPCIssuerProtocol ) {
44+ self . rpcIssuer = rpcIssuer
9245 }
9346
94- class func call< T: AuthRPCRequest > ( with request: T ) async throws -> T . Response {
95- return try await implementation ( ) . call ( with: request)
47+ /// Calls the RPC using HTTP request.
48+ /// Possible error responses:
49+ /// * See FIRAuthInternalErrorCodeRPCRequestEncodingError
50+ /// * See FIRAuthInternalErrorCodeJSONSerializationError
51+ /// * See FIRAuthInternalErrorCodeNetworkError
52+ /// * See FIRAuthInternalErrorCodeUnexpectedErrorResponse
53+ /// * See FIRAuthInternalErrorCodeUnexpectedResponse
54+ /// * See FIRAuthInternalErrorCodeRPCResponseDecodingError
55+ /// - Parameter request: The request.
56+ /// - Returns: The response.
57+ func call< T: AuthRPCRequest > ( with request: T ) async throws -> T . Response {
58+ let response = try await callInternal ( with: request)
59+ if let auth = request. requestConfiguration ( ) . auth,
60+ let mfaError = Self . generateMFAError ( response: response, auth: auth) {
61+ throw mfaError
62+ } else if let error = Self . phoneCredentialInUse ( response: response) {
63+ throw error
64+ } else {
65+ return response
66+ }
9667 }
9768
98- class func request( withURL url: URL ,
99- contentType: String ,
100- requestConfiguration: AuthRequestConfiguration ) async -> URLRequest {
69+ static func request( withURL url: URL ,
70+ contentType: String ,
71+ requestConfiguration: AuthRequestConfiguration ) async -> URLRequest {
10172 // Kick off tasks for the async header values.
10273 async let heartbeatsHeaderValue = requestConfiguration. heartbeatLogger? . asyncHeaderValue ( )
10374 async let appCheckTokenHeaderValue = requestConfiguration. appCheck?
@@ -132,94 +103,56 @@ class AuthBackend {
132103 }
133104 return request
134105 }
135- }
136106
137- @available( iOS 13 , tvOS 13 , macOS 10.15 , macCatalyst 13 , watchOS 7 , * )
138- protocol AuthBackendImplementation {
139- func call< T: AuthRPCRequest > ( with request: T ) async throws -> T . Response
140- }
141-
142- @available ( iOS 13 , tvOS 13 , macOS 10 . 15 , macCatalyst 13 , watchOS 7 , * )
143- private class AuthBackendRPCImplementation: AuthBackendImplementation {
144- var rpcIssuer : AuthBackendRPCIssuer = AuthBackendRPCIssuerImplementation ( )
145-
146- /// Calls the RPC using HTTP request.
147- /// Possible error responses:
148- /// * See FIRAuthInternalErrorCodeRPCRequestEncodingError
149- /// * See FIRAuthInternalErrorCodeJSONSerializationError
150- /// * See FIRAuthInternalErrorCodeNetworkError
151- /// * See FIRAuthInternalErrorCodeUnexpectedErrorResponse
152- /// * See FIRAuthInternalErrorCodeUnexpectedResponse
153- /// * See FIRAuthInternalErrorCodeRPCResponseDecodingError
154- /// - Parameter request: The request.
155- /// - Returns: The response.
156- fileprivate func call< T: AuthRPCRequest > ( with request: T ) async throws -> T . Response {
157- let response = try await callInternal ( with: request)
158- if let auth = request. requestConfiguration ( ) . auth,
159- let mfaError = Self . generateMFAError ( response: response, auth: auth) {
160- throw mfaError
161- } else if let error = Self . phoneCredentialInUse ( response: response) {
162- throw error
163- } else {
164- return response
165- }
166- }
167-
168- #if os(iOS)
169- private class func generateMFAError( response: AuthRPCResponse , auth: Auth ) -> Error ? {
170- if let mfaResponse = response as? AuthMFAResponse ,
171- mfaResponse. idToken == nil ,
172- let enrollments = mfaResponse. mfaInfo {
173- var info : [ MultiFactorInfo ] = [ ]
174- for enrollment in enrollments {
175- // check which MFA factors are enabled.
176- if let _ = enrollment. phoneInfo {
177- info. append ( PhoneMultiFactorInfo ( proto: enrollment) )
178- } else if let _ = enrollment. totpInfo {
179- info. append ( TOTPMultiFactorInfo ( proto: enrollment) )
180- } else {
181- AuthLog . logError ( code: " I-AUT000021 " , message: " Multifactor type is not supported " )
182- }
107+ private static func generateMFAError( response: AuthRPCResponse, auth: Auth) - > Error? {
108+ #if !os(iOS)
109+ return nil
110+ #endif // !os(iOS)
111+ if let mfaResponse = response as? AuthMFAResponse ,
112+ mfaResponse. idToken == nil ,
113+ let enrollments = mfaResponse. mfaInfo {
114+ var info : [ MultiFactorInfo ] = [ ]
115+ for enrollment in enrollments {
116+ // check which MFA factors are enabled.
117+ if let _ = enrollment. phoneInfo {
118+ info. append ( PhoneMultiFactorInfo ( proto: enrollment) )
119+ } else if let _ = enrollment. totpInfo {
120+ info. append ( TOTPMultiFactorInfo ( proto: enrollment) )
121+ } else {
122+ AuthLog . logError ( code: " I-AUT000021 " , message: " Multifactor type is not supported " )
183123 }
184- return AuthErrorUtils . secondFactorRequiredError (
185- pendingCredential: mfaResponse. mfaPendingCredential,
186- hints: info,
187- auth: auth
188- )
189- } else {
190- return nil
191124 }
192- }
193- #else
194- private class func generateMFAError( response: AuthRPCResponse , auth: Auth ? ) -> Error ? {
125+ return AuthErrorUtils . secondFactorRequiredError (
126+ pendingCredential: mfaResponse. mfaPendingCredential,
127+ hints: info,
128+ auth: auth
129+ )
130+ } else {
195131 return nil
196132 }
197- #endif
133+ }
198134
199- #if os(iOS)
200- // Check whether or not the successful response is actually the special case phone
201- // auth flow that returns a temporary proof and phone number.
202- private class func phoneCredentialInUse( response: AuthRPCResponse ) -> Error ? {
203- if let phoneAuthResponse = response as? VerifyPhoneNumberResponse ,
204- let phoneNumber = phoneAuthResponse. phoneNumber,
205- phoneNumber. count > 0 ,
206- let temporaryProof = phoneAuthResponse. temporaryProof,
207- temporaryProof. count > 0 {
208- let credential = PhoneAuthCredential ( withTemporaryProof: temporaryProof,
209- phoneNumber: phoneNumber,
210- providerID: PhoneAuthProvider . id)
211- return AuthErrorUtils . credentialAlreadyInUseError ( message: nil ,
212- credential: credential,
213- email: nil )
214- } else {
215- return nil
216- }
217- }
218- #else
219- private class func phoneCredentialInUse( response: AuthRPCResponse ) -> Error ? {
135+ // Check whether or not the successful response is actually the special case phone
136+ // auth flow that returns a temporary proof and phone number.
137+ private static func phoneCredentialInUse( response: AuthRPCResponse ) -> Error ? {
138+ #if !os(iOS)
139+ return nil
140+ #endif // !os(iOS)
141+ if let phoneAuthResponse = response as? VerifyPhoneNumberResponse ,
142+ let phoneNumber = phoneAuthResponse. phoneNumber,
143+ phoneNumber. count > 0 ,
144+ let temporaryProof = phoneAuthResponse. temporaryProof,
145+ temporaryProof. count > 0 {
146+ let credential = PhoneAuthCredential ( withTemporaryProof: temporaryProof,
147+ phoneNumber: phoneNumber,
148+ providerID: PhoneAuthProvider . id)
149+ return AuthErrorUtils . credentialAlreadyInUseError ( message: nil ,
150+ credential: credential,
151+ email: nil )
152+ } else {
220153 return nil
221154 }
222- #endif
155+ }
223156
224157 /// Calls the RPC using HTTP request.
225158 ///
@@ -318,7 +251,7 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
318251 if error != nil {
319252 if let errorDictionary = dictionary [ " error " ] as? [ String : AnyHashable ] {
320253 if let errorMessage = errorDictionary [ " message " ] as? String {
321- if let clientError = AuthBackendRPCImplementation . clientError (
254+ if let clientError = Self . clientError (
322255 withServerErrorMessage: errorMessage,
323256 errorDictionary: errorDictionary,
324257 response: response,
@@ -351,7 +284,7 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
351284 if let verifyAssertionRequest = request as? VerifyAssertionRequest {
352285 if verifyAssertionRequest. returnIDPCredential {
353286 if let errorMessage = dictionary [ " errorMessage " ] as? String {
354- if let clientError = AuthBackendRPCImplementation . clientError (
287+ if let clientError = Self . clientError (
355288 withServerErrorMessage: errorMessage,
356289 errorDictionary: dictionary,
357290 response: response,
@@ -365,10 +298,10 @@ private class AuthBackendRPCImplementation: AuthBackendImplementation {
365298 return response
366299 }
367300
368- private class func clientError( withServerErrorMessage serverErrorMessage: String ,
369- errorDictionary: [ String : Any ] ,
370- response: AuthRPCResponse ,
371- error: Error ? ) -> Error ? {
301+ private static func clientError( withServerErrorMessage serverErrorMessage: String ,
302+ errorDictionary: [ String : Any ] ,
303+ response: AuthRPCResponse ,
304+ error: Error ? ) -> Error ? {
372305 let split = serverErrorMessage. split ( separator: " : " )
373306 let shortErrorMessage = split. first? . trimmingCharacters ( in: . whitespacesAndNewlines)
374307 let serverDetailErrorMessage = String ( split. count > 1 ? split [ 1 ] : " " )
0 commit comments