@@ -22,6 +22,10 @@ import Foundation
2222@objc ( FIRPhoneAuthProvider) open class PhoneAuthProvider : NSObject {
2323 /// A string constant identifying the phone identity provider.
2424 @objc public static let id = " phone "
25+ @objc private static let kRecaptchaVersion = " RECAPTCHA_ENTERPRISE "
26+ @objc private static let kClientType = " CLIENT_TYPE_IOS "
27+ @objc private static let kFakeCaptchaResponse = " NO_RECAPTCHA "
28+
2529 #if os(iOS)
2630 /// Returns an instance of `PhoneAuthProvider` for the default `Auth` object.
2731 @objc ( provider) open class func provider( ) -> PhoneAuthProvider {
@@ -250,7 +254,8 @@ import Foundation
250254 phoneNumber: phoneNumber,
251255 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
252256 multiFactorSession: nil ,
253- uiDelegate: uiDelegate)
257+ uiDelegate: uiDelegate,
258+ auditFallback: true )
254259 }
255260 }
256261
@@ -262,13 +267,18 @@ import Foundation
262267 /// finished.
263268 func verifyClAndSendVerificationCode( toPhoneNumber phoneNumber: String ,
264269 retryOnInvalidAppCredential: Bool ,
265- uiDelegate: AuthUIDelegate ? ) async throws
270+ uiDelegate: AuthUIDelegate ? ,
271+ auditFallback: Bool = false ) async throws
266272 -> String ? {
267273 let codeIdentity = try await verifyClient ( withUIDelegate: uiDelegate)
268274 let request = SendVerificationCodeRequest ( phoneNumber: phoneNumber,
269275 codeIdentity: codeIdentity,
270276 requestConfiguration: auth
271277 . requestConfiguration)
278+ if auditFallback {
279+ request. injectRecaptchaFields ( recaptchaResponse: PhoneAuthProvider . kFakeCaptchaResponse, recaptchaVersion: PhoneAuthProvider . kRecaptchaVersion)
280+ }
281+ //TODO inject fake_token when .audit
272282 do {
273283 let response = try await auth. backend. call ( with: request)
274284 return response. verificationID
@@ -278,12 +288,13 @@ import Foundation
278288 phoneNumber: phoneNumber,
279289 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
280290 multiFactorSession: nil ,
281- uiDelegate: uiDelegate
291+ uiDelegate: uiDelegate,
292+ auditFallback: auditFallback
282293 )
283294 }
284295 }
285296
286- /// Starts the flow to verify the client via silent push notification.
297+ /// Starts the flow to verify the client via silent push notification. This is used in both .Audit and .Enforce mode
287298 /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an
288299 /// AuthErrorCodeInvalidAppCredential error is returned from the backend.
289300 /// - Parameter phoneNumber: The phone number to be verified.
@@ -339,24 +350,28 @@ import Foundation
339350 return response. responseInfo? . sessionInfo
340351 }
341352 } catch {
353+ // For Audit fallback only after rCE check failed
342354 return try await handleVerifyErrorWithRetry (
343355 error: error,
344356 phoneNumber: phoneNumber,
345357 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
346358 multiFactorSession: session,
347- uiDelegate: uiDelegate
359+ uiDelegate: uiDelegate,
360+ auditFallback: true
348361 )
349362 }
350363 }
351364
352365 /// Starts the flow to verify the client via silent push notification.
366+ /// This method is called in Audit fallback flow with "NO_RECAPTCHA" fake token and Off flow
353367 /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an
354368 /// AuthErrorCodeInvalidAppCredential error is returned from the backend.
355369 /// - Parameter phoneNumber: The phone number to be verified.
356370 private func verifyClAndSendVerificationCode( toPhoneNumber phoneNumber: String ,
357371 retryOnInvalidAppCredential: Bool ,
358372 multiFactorSession session: MultiFactorSession ? ,
359- uiDelegate: AuthUIDelegate ? ) async throws
373+ uiDelegate: AuthUIDelegate ? ,
374+ auditFallback: Bool = false ) async throws
360375 -> String ? {
361376 if let settings = auth. settings,
362377 settings. isAppVerificationDisabledForTesting {
@@ -370,20 +385,27 @@ import Foundation
370385 return response. verificationID
371386 }
372387 guard let session else {
388+ // Phone MFA flow
373389 return try await verifyClAndSendVerificationCode (
374390 toPhoneNumber: phoneNumber,
375391 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
376- uiDelegate: uiDelegate
392+ uiDelegate: uiDelegate,
393+ auditFallback: auditFallback
377394 )
378395 }
396+ // MFA flows
379397 let codeIdentity = try await verifyClient ( withUIDelegate: uiDelegate)
380398 let startMFARequestInfo = AuthProtoStartMFAPhoneRequestInfo ( phoneNumber: phoneNumber,
381399 codeIdentity: codeIdentity)
400+ if auditFallback {
401+ startMFARequestInfo. injectRecaptchaFields ( recaptchaResponse: PhoneAuthProvider . kFakeCaptchaResponse, recaptchaVersion: PhoneAuthProvider . kRecaptchaVersion, clientType: PhoneAuthProvider . kClientType)
402+ }
382403 do {
383404 if let idToken = session. idToken {
384405 let request = StartMFAEnrollmentRequest ( idToken: idToken,
385406 enrollmentInfo: startMFARequestInfo,
386407 requestConfiguration: auth. requestConfiguration)
408+ // TODO if mode is audit, inject recaptcha field with no_recaptcha
387409 let response = try await auth. backend. call ( with: request)
388410 return response. phoneSessionInfo? . sessionInfo
389411 } else {
@@ -401,23 +423,27 @@ import Foundation
401423 phoneNumber: phoneNumber,
402424 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
403425 multiFactorSession: session,
404- uiDelegate: uiDelegate
426+ uiDelegate: uiDelegate,
427+ auditFallback: auditFallback
405428 )
406429 }
407430 }
408431
432+ /// This method is only called when Audit failed on rCE on invalid-app-credential exception
409433 private func handleVerifyErrorWithRetry( error: Error ,
410434 phoneNumber: String ,
411435 retryOnInvalidAppCredential: Bool ,
412436 multiFactorSession session: MultiFactorSession ? ,
413- uiDelegate: AuthUIDelegate ? ) async throws -> String ? {
437+ uiDelegate: AuthUIDelegate ? ,
438+ auditFallback: Bool = false ) async throws -> String ? {
414439 if ( error as NSError ) . code == AuthErrorCode . invalidAppCredential. rawValue {
415440 if retryOnInvalidAppCredential {
416441 auth. appCredentialManager. clearCredential ( )
417442 return try await verifyClAndSendVerificationCode ( toPhoneNumber: phoneNumber,
418443 retryOnInvalidAppCredential: false ,
419444 multiFactorSession: session,
420- uiDelegate: uiDelegate)
445+ uiDelegate: uiDelegate,
446+ auditFallback: auditFallback)
421447 }
422448 throw AuthErrorUtils . unexpectedResponse ( deserializedResponse: nil , underlyingError: error)
423449 }
0 commit comments