Skip to content

Commit c396f47

Browse files
fix Audit mode fallback failure (#14155)
1 parent a6115f9 commit c396f47

File tree

1 file changed

+42
-10
lines changed

1 file changed

+42
-10
lines changed

FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)