@@ -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,20 @@ 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 (
280+ recaptchaResponse: PhoneAuthProvider . kFakeCaptchaResponse,
281+ recaptchaVersion: PhoneAuthProvider . kRecaptchaVersion
282+ )
283+ }
272284 do {
273285 let response = try await auth. backend. call ( with: request)
274286 return response. verificationID
@@ -278,12 +290,14 @@ import Foundation
278290 phoneNumber: phoneNumber,
279291 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
280292 multiFactorSession: nil ,
281- uiDelegate: uiDelegate
293+ uiDelegate: uiDelegate,
294+ auditFallback: auditFallback
282295 )
283296 }
284297 }
285298
286- /// Starts the flow to verify the client via silent push notification.
299+ /// Starts the flow to verify the client via silent push notification. This is used in both
300+ /// .Audit and .Enforce mode
287301 /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an
288302 /// AuthErrorCodeInvalidAppCredential error is returned from the backend.
289303 /// - Parameter phoneNumber: The phone number to be verified.
@@ -339,24 +353,28 @@ import Foundation
339353 return response. responseInfo? . sessionInfo
340354 }
341355 } catch {
356+ // For Audit fallback only after rCE check failed
342357 return try await handleVerifyErrorWithRetry (
343358 error: error,
344359 phoneNumber: phoneNumber,
345360 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
346361 multiFactorSession: session,
347- uiDelegate: uiDelegate
362+ uiDelegate: uiDelegate,
363+ auditFallback: true
348364 )
349365 }
350366 }
351367
352368 /// Starts the flow to verify the client via silent push notification.
369+ /// This method is called in Audit fallback flow with "NO_RECAPTCHA" fake token and Off flow
353370 /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an
354371 /// AuthErrorCodeInvalidAppCredential error is returned from the backend.
355372 /// - Parameter phoneNumber: The phone number to be verified.
356373 private func verifyClAndSendVerificationCode( toPhoneNumber phoneNumber: String ,
357374 retryOnInvalidAppCredential: Bool ,
358375 multiFactorSession session: MultiFactorSession ? ,
359- uiDelegate: AuthUIDelegate ? ) async throws
376+ uiDelegate: AuthUIDelegate ? ,
377+ auditFallback: Bool = false ) async throws
360378 -> String ? {
361379 if let settings = auth. settings,
362380 settings. isAppVerificationDisabledForTesting {
@@ -370,15 +388,25 @@ import Foundation
370388 return response. verificationID
371389 }
372390 guard let session else {
391+ // Phone MFA flow
373392 return try await verifyClAndSendVerificationCode (
374393 toPhoneNumber: phoneNumber,
375394 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
376- uiDelegate: uiDelegate
395+ uiDelegate: uiDelegate,
396+ auditFallback: auditFallback
377397 )
378398 }
399+ // MFA flows
379400 let codeIdentity = try await verifyClient ( withUIDelegate: uiDelegate)
380401 let startMFARequestInfo = AuthProtoStartMFAPhoneRequestInfo ( phoneNumber: phoneNumber,
381402 codeIdentity: codeIdentity)
403+ if auditFallback {
404+ startMFARequestInfo. injectRecaptchaFields (
405+ recaptchaResponse: PhoneAuthProvider . kFakeCaptchaResponse,
406+ recaptchaVersion: PhoneAuthProvider . kRecaptchaVersion,
407+ clientType: PhoneAuthProvider . kClientType
408+ )
409+ }
382410 do {
383411 if let idToken = session. idToken {
384412 let request = StartMFAEnrollmentRequest ( idToken: idToken,
@@ -401,23 +429,27 @@ import Foundation
401429 phoneNumber: phoneNumber,
402430 retryOnInvalidAppCredential: retryOnInvalidAppCredential,
403431 multiFactorSession: session,
404- uiDelegate: uiDelegate
432+ uiDelegate: uiDelegate,
433+ auditFallback: auditFallback
405434 )
406435 }
407436 }
408437
438+ /// This method is only called when Audit failed on rCE on invalid-app-credential exception
409439 private func handleVerifyErrorWithRetry( error: Error ,
410440 phoneNumber: String ,
411441 retryOnInvalidAppCredential: Bool ,
412442 multiFactorSession session: MultiFactorSession ? ,
413- uiDelegate: AuthUIDelegate ? ) async throws -> String ? {
443+ uiDelegate: AuthUIDelegate ? ,
444+ auditFallback: Bool = false ) async throws -> String ? {
414445 if ( error as NSError ) . code == AuthErrorCode . invalidAppCredential. rawValue {
415446 if retryOnInvalidAppCredential {
416447 auth. appCredentialManager. clearCredential ( )
417448 return try await verifyClAndSendVerificationCode ( toPhoneNumber: phoneNumber,
418449 retryOnInvalidAppCredential: false ,
419450 multiFactorSession: session,
420- uiDelegate: uiDelegate)
451+ uiDelegate: uiDelegate,
452+ auditFallback: auditFallback)
421453 }
422454 throw AuthErrorUtils . unexpectedResponse ( deserializedResponse: nil , underlyingError: error)
423455 }
0 commit comments