From 7665630785930dda0fd2215d2b8209554b15afe7 Mon Sep 17 00:00:00 2001 From: Pragati Date: Sat, 15 Jun 2024 14:15:36 -0700 Subject: [PATCH 01/43] fix phone auth login flow to swift methods and inject recaptcha fields --- .../AuthProvider/PhoneAuthProvider.swift | 39 +++++----- .../RPC/SendVerificationTokenRequest.swift | 28 ++++++- .../Utilities/AuthRecaptchaVerifier.swift | 74 +++++++++---------- 3 files changed, 83 insertions(+), 58 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 61a78271347..496f475f365 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -107,17 +107,21 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession? = nil) async throws -> String { - return try await withCheckedThrowingContinuation { continuation in - self.verifyPhoneNumber(phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) { result, error in - if let error { - continuation.resume(throwing: error) - } else if let result { - continuation.resume(returning: result) + guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, + urlTypes: auth.mainBundleUrlTypes) else { + fatalError( + "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." + ) + } + do { + if let verificationID = try await internalVerify(phoneNumber: phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) { + return verificationID + } else { + throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") } + } catch { + throw error } - } } /// Verify ownership of the second factor phone number by the current user. @@ -152,16 +156,12 @@ import Foundation open func verifyPhoneNumber(with multiFactorInfo: PhoneMultiFactorInfo, uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession?) async throws -> String { - return try await withCheckedThrowingContinuation { continuation in - self.verifyPhoneNumber(with: multiFactorInfo, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) { result, error in - if let error { - continuation.resume(throwing: error) - } else if let result { - continuation.resume(returning: result) - } - } + do { + return try await self.verifyPhoneNumber(with: multiFactorInfo, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) + } catch { + throw error } } @@ -244,7 +244,6 @@ import Foundation codeIdentity: CodeIdentity.empty, requestConfiguration: auth.requestConfiguration ) - let response = try await AuthBackend.call(with: request) return response.verificationID } diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift index af090fdd3b3..5ec5abaf4a9 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift @@ -29,6 +29,15 @@ private let kSecretKey = "iosSecret" /// The key for the reCAPTCHAToken parameter in the request. private let kreCAPTCHATokenKey = "recaptchaToken" +/// The key for the "clientType" value in the request. +private let kClientType = "clientType" + +/// The key for the "captchaResponse" value in the request. +private let kCaptchaResponseKey = "captchaResponse" + +/// The key for the "recaptchaVersion" value in the request. +private let kRecaptchaVersion = "recaptchaVersion" + /// The key for the tenant id value in the request. private let kTenantIDKey = "tenantId" @@ -49,6 +58,12 @@ class SendVerificationCodeRequest: IdentityToolkitRequest, AuthRPCRequest { /// The credential or reCAPTCHA token to prove the identity of the app in order to send the /// verification code. let codeIdentity: CodeIdentity + + /// Response to the captcha. + var captchaResponse: String? + + /// The reCAPTCHA version. + var recaptchaVersion: String? init(phoneNumber: String, codeIdentity: CodeIdentity, requestConfiguration: AuthRequestConfiguration) { @@ -71,10 +86,21 @@ class SendVerificationCodeRequest: IdentityToolkitRequest, AuthRPCRequest { postBody[kreCAPTCHATokenKey] = reCAPTCHAToken case .empty: break } - + if let captchaResponse { + postBody[kCaptchaResponseKey] = captchaResponse + } + if let recaptchaVersion { + postBody[kRecaptchaVersion] = recaptchaVersion + } if let tenantID { postBody[kTenantIDKey] = tenantID } + postBody[kClientType] = clientType return postBody } + + func injectRecaptchaFields(recaptchaResponse: String?, recaptchaVersion: String) { + captchaResponse = recaptchaResponse + self.recaptchaVersion = recaptchaVersion + } } diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 98d837d1d83..0f2d3d6e796 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -28,16 +28,24 @@ } } - enum AuthRecaptchaProvider { - case password - } - - enum AuthRecaptchaAction { - case defaultAction - case signInWithPassword - case getOobCode - case signUpPassword - } +enum AuthRecaptchaProvider: String, CaseIterable { + case password = "EMAIL_PASSWORD_PROVIDER" + case phone = "PHONE_PROVIDER" // Add phone provider + + // Convenience property for mapping values + var stringValue: String { self.rawValue } +} + +enum AuthRecaptchaAction: String { + case defaultAction = "defaultAction" + case signInWithPassword = "signInWithPassword" + case getOobCode = "getOobCode" + case signUpPassword = "signUpPassword" + case sendVerificationCode = "sendVerificationCode" + + // Convenience property for mapping values + var stringValue: String { self.rawValue } +} @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) class AuthRecaptchaVerifier { @@ -47,10 +55,6 @@ private(set) var recaptchaClient: RCARecaptchaClientProtocol? private static let _shared = AuthRecaptchaVerifier() - private let providerToStringMap = [AuthRecaptchaProvider.password: "EMAIL_PASSWORD_PROVIDER"] - private let actionToStringMap = [AuthRecaptchaAction.signInWithPassword: "signInWithPassword", - AuthRecaptchaAction.getOobCode: "getOobCode", - AuthRecaptchaAction.signUpPassword: "signUpPassword"] private let kRecaptchaVersion = "RECAPTCHA_ENTERPRISE" private init() {} @@ -74,21 +78,15 @@ } func enablementStatus(forProvider provider: AuthRecaptchaProvider) -> Bool { - guard let providerString = providerToStringMap[provider] else { - return false - } - if let tenantID = auth?.tenantID { - guard let tenantConfig = tenantConfigs[tenantID], - let status = tenantConfig.enablementStatus[providerString] else { - return false - } + if let tenantID = auth?.tenantID, + let tenantConfig = tenantConfigs[tenantID], + let status = tenantConfig.enablementStatus[provider.stringValue] { return status - } else { - guard let agentConfig, - let status = agentConfig.enablementStatus[providerString] else { - return false - } + } else if let agentConfig = agentConfig, + let status = agentConfig.enablementStatus[provider.stringValue] { return status + } else { + return false } } @@ -106,8 +104,8 @@ } func retrieveRecaptchaToken(withAction action: AuthRecaptchaAction) async throws -> String { - guard let actionString = actionToStringMap[action], - let RecaptchaActionClass = NSClassFromString("RecaptchaAction"), + let actionString = action.stringValue + guard let RecaptchaActionClass = NSClassFromString("RecaptchaAction"), let actionClass = RecaptchaActionClass as? any RCAActionProtocol.Type else { throw AuthErrorUtils.recaptchaSDKNotLinkedError() } @@ -157,14 +155,16 @@ var enablementStatus: [String: Bool] = [:] if let enforcementState = response.enforcementState { for state in enforcementState { - if let provider = state["provider"], - provider == providerToStringMap[AuthRecaptchaProvider.password] { - if let enforcement = state["enforcementState"] { - if enforcement == "ENFORCE" || enforcement == "AUDIT" { - enablementStatus[provider] = true - } else if enforcement == "OFF" { - enablementStatus[provider] = false - } + if let providerString = state["provider"], + let enforcement = state["enforcementState"], + let provider = AuthRecaptchaProvider(rawValue: providerString) { // Try to get enum from raw value + switch enforcement { + case "ENFORCE", "AUDIT": + enablementStatus[provider.stringValue] = true + case "OFF": + enablementStatus[provider.stringValue] = false + default: + break // Handle unknown enforcement states if necessary } } } From d93c078d2e2037149f46096716fda05f61cc766f Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 18 Jun 2024 09:28:57 -0700 Subject: [PATCH 02/43] add recaptcha enterprise dependencies and fix flows to use swift methods --- FirebaseAuth/Sources/Swift/Auth/Auth.swift | 2 +- .../AuthProvider/PhoneAuthProvider.swift | 53 ++++++++++++- .../RPC/GetRecaptchaConfigResponse.swift | 2 +- .../Utilities/AuthRecaptchaVerifier.swift | 77 ++++++++++--------- .../AuthenticationExample/AppDelegate.swift | 1 + .../SwiftApplication.plist | 5 +- .../PhoneAuthViewController.swift | 36 ++++++--- 7 files changed, 123 insertions(+), 53 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/Auth/Auth.swift b/FirebaseAuth/Sources/Swift/Auth/Auth.swift index 693e7257503..cb125ca131a 100644 --- a/FirebaseAuth/Sources/Swift/Auth/Auth.swift +++ b/FirebaseAuth/Sources/Swift/Auth/Auth.swift @@ -2287,7 +2287,7 @@ extension Auth: AuthInterop { action: AuthRecaptchaAction) async throws -> T .Response { let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: self) - if recaptchaVerifier.enablementStatus(forProvider: AuthRecaptchaProvider.password) { + if recaptchaVerifier.enablementStatus(forProvider: AuthRecaptchaProvider.password) != .off{ try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: AuthRecaptchaProvider.password, action: action) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 496f475f365..2ae3ca059e0 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -14,6 +14,7 @@ import FirebaseCore import Foundation +import RecaptchaEnterprise /// A concrete implementation of `AuthProvider` for phone auth providers. /// @@ -114,6 +115,9 @@ import Foundation ) } do { +// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) +// let enablementStatus = recaptchaVerifier.enablementStatus(forProvider: .phone) + // regular phone number login if let verificationID = try await internalVerify(phoneNumber: phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) { return verificationID } else { @@ -123,6 +127,24 @@ import Foundation throw error } } + +// @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) +// open func verifyPhoneNumberWithRecaptcha(_ phoneNumber: String, +// uiDelegate: AuthUIDelegate? = nil, +// multiFactorSession: MultiFactorSession? = nil) async throws +// -> String { +// guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, +// urlTypes: auth.mainBundleUrlTypes) else { +// fatalError( +// "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." +// ) +// } +// do { +// +// } catch { +// throw error +// } +// } /// Verify ownership of the second factor phone number by the current user. /// - Parameter multiFactorInfo: The phone multi factor whose number need to be verified. @@ -157,9 +179,10 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession?) async throws -> String { do { - return try await self.verifyPhoneNumber(with: multiFactorInfo, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) + multiFactorSession?.multiFactorInfo = multiFactorInfo + return try await verifyPhoneNumber(multiFactorInfo.phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) } catch { throw error } @@ -199,6 +222,25 @@ import Foundation multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) } + +// private func internalVerifyWithRecaptcha(phoneNumber: String, +// uiDelegate: AuthUIDelegate?, +// multiFactorSession: MultiFactorSession? = nil) async throws +// -> String? { +// guard phoneNumber.count > 0 else { +// throw AuthErrorUtils.missingPhoneNumberError(message: nil) +// } +// guard let manager = auth.notificationManager else { +// throw AuthErrorUtils.notificationNotForwardedError() +// } +// guard await manager.checkNotificationForwarding() else { +// throw AuthErrorUtils.notificationNotForwardedError() +// } +// return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, +// retryOnInvalidAppCredential: true, +// multiFactorSession: multiFactorSession, +// uiDelegate: uiDelegate) +// } /// Starts the flow to verify the client via silent push notification. /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an @@ -244,6 +286,11 @@ import Foundation codeIdentity: CodeIdentity.empty, requestConfiguration: auth.requestConfiguration ) +// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) +// let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) +// if(enforcement != .off) { +// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) +// } let response = try await AuthBackend.call(with: request) return response.verificationID } diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/GetRecaptchaConfigResponse.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/GetRecaptchaConfigResponse.swift index 0d80a135613..9331670db86 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/GetRecaptchaConfigResponse.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/GetRecaptchaConfigResponse.swift @@ -22,6 +22,6 @@ class GetRecaptchaConfigResponse: AuthRPCResponse { func setFields(dictionary: [String: AnyHashable]) throws { recaptchaKey = dictionary["recaptchaKey"] as? String - enforcementState = dictionary["enforcementState"] as? [[String: String]] + enforcementState = dictionary["recaptchaEnforcementState"] as? [[String: String]] } } diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 0f2d3d6e796..82ae0a61286 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -14,30 +14,41 @@ #if os(iOS) - import Foundation - import RecaptchaInterop +import Foundation +import RecaptchaInterop +import RecaptchaEnterprise.Recaptcha +import RecaptchaEnterprise.RecaptchaAction @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) class AuthRecaptchaConfig { let siteKey: String - let enablementStatus: [String: Bool] + let enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] - init(siteKey: String, enablementStatus: [String: Bool]) { + init(siteKey: String, enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus]) { self.siteKey = siteKey self.enablementStatus = enablementStatus } } +enum AuthRecaptchaEnablementStatus: String, CaseIterable { + case enforce = "ENFORCE" + case audit = "AUDIT" + case off = "OFF" + + // Convenience property for mapping values + var stringValue: String { self.rawValue } +} + enum AuthRecaptchaProvider: String, CaseIterable { case password = "EMAIL_PASSWORD_PROVIDER" - case phone = "PHONE_PROVIDER" // Add phone provider + case phone = "PHONE_PROVIDER" // Convenience property for mapping values var stringValue: String { self.rawValue } } enum AuthRecaptchaAction: String { - case defaultAction = "defaultAction" + case defaultAction case signInWithPassword = "signInWithPassword" case getOobCode = "getOobCode" case signUpPassword = "signUpPassword" @@ -77,39 +88,37 @@ enum AuthRecaptchaAction: String { return agentConfig?.siteKey } - func enablementStatus(forProvider provider: AuthRecaptchaProvider) -> Bool { + func enablementStatus(forProvider provider: AuthRecaptchaProvider) -> AuthRecaptchaEnablementStatus { if let tenantID = auth?.tenantID, let tenantConfig = tenantConfigs[tenantID], - let status = tenantConfig.enablementStatus[provider.stringValue] { + let status = tenantConfig.enablementStatus[provider] { return status } else if let agentConfig = agentConfig, - let status = agentConfig.enablementStatus[provider.stringValue] { + let status = agentConfig.enablementStatus[provider] { return status } else { - return false + return AuthRecaptchaEnablementStatus.off } } func verify(forceRefresh: Bool, action: AuthRecaptchaAction) async throws -> String { - try await retrieveRecaptchaConfig(forceRefresh: forceRefresh) - if recaptchaClient == nil { - guard let siteKey = siteKey(), - let RecaptchaClass = NSClassFromString("Recaptcha"), - let recaptcha = RecaptchaClass as? any RCARecaptchaProtocol.Type else { - throw AuthErrorUtils.recaptchaSDKNotLinkedError() + do { + try await retrieveRecaptchaConfig(forceRefresh: forceRefresh) + if recaptchaClient == nil { + guard let siteKey = siteKey() else { + throw AuthErrorUtils.error(code: .internalError, message: "Invalid sitekey") + } + recaptchaClient = try await Recaptcha.getClient(withSiteKey: siteKey) } - recaptchaClient = try await recaptcha.getClient(withSiteKey: siteKey) + return try await retrieveRecaptchaToken(withAction: action) + } catch { + throw error } - return try await retrieveRecaptchaToken(withAction: action) } func retrieveRecaptchaToken(withAction action: AuthRecaptchaAction) async throws -> String { let actionString = action.stringValue - guard let RecaptchaActionClass = NSClassFromString("RecaptchaAction"), - let actionClass = RecaptchaActionClass as? any RCAActionProtocol.Type else { - throw AuthErrorUtils.recaptchaSDKNotLinkedError() - } - let customAction = actionClass.init(customAction: actionString) + let customAction = RecaptchaAction.init(customAction: actionString) do { let token = try await recaptchaClient?.execute(withAction: customAction) AuthLog.logInfo(code: "I-AUT000100", message: "reCAPTCHA token retrieval succeeded.") @@ -145,6 +154,7 @@ enum AuthRecaptchaAction: String { } let request = GetRecaptchaConfigRequest(requestConfiguration: requestConfiguration) let response = try await AuthBackend.call(with: request) + print(response) AuthLog.logInfo(code: "I-AUT000103", message: "reCAPTCHA config retrieval succeeded.") // Response's site key is of the format projects//keys/' guard let keys = response.recaptchaKey?.components(separatedBy: "/"), @@ -152,21 +162,16 @@ enum AuthRecaptchaAction: String { throw AuthErrorUtils.error(code: .recaptchaNotEnabled, message: "Invalid siteKey") } let siteKey = keys[3] - var enablementStatus: [String: Bool] = [:] + var enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] = [:] if let enforcementState = response.enforcementState { for state in enforcementState { - if let providerString = state["provider"], - let enforcement = state["enforcementState"], - let provider = AuthRecaptchaProvider(rawValue: providerString) { // Try to get enum from raw value - switch enforcement { - case "ENFORCE", "AUDIT": - enablementStatus[provider.stringValue] = true - case "OFF": - enablementStatus[provider.stringValue] = false - default: - break // Handle unknown enforcement states if necessary - } + guard let providerString = state["provider"] as? String, + let enforcementString = state["enforcementState"] as? String, + let provider = AuthRecaptchaProvider(rawValue: providerString), + let enforcement = AuthRecaptchaEnablementStatus(rawValue: enforcementString) else { + continue // Skip to the next state in the loop } + enablementStatus[provider] = enforcement } } let config = AuthRecaptchaConfig(siteKey: siteKey, enablementStatus: enablementStatus) @@ -182,7 +187,7 @@ enum AuthRecaptchaAction: String { provider: AuthRecaptchaProvider, action: AuthRecaptchaAction) async throws { try await retrieveRecaptchaConfig(forceRefresh: false) - if enablementStatus(forProvider: provider) { + if enablementStatus(forProvider: provider) != AuthRecaptchaEnablementStatus.off { let token = try await verify(forceRefresh: false, action: action) request.injectRecaptchaFields(recaptchaResponse: token, recaptchaVersion: kRecaptchaVersion) } else { diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift index 12efe53800d..cfe070a8bbd 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift @@ -15,6 +15,7 @@ import FBSDKCoreKit import FirebaseCore import GoogleSignIn +import RecaptchaEnterprise import UIKit @UIApplicationMain diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist index 50378fe58b0..b44dd94c267 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist @@ -24,7 +24,10 @@ CFBundleURLName CFBundleURLSchemes - + + app-1-585304629422-ios-cb0e5761816443a9d98d3f + com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b + CFBundleVersion diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift index a4e19077aad..62dc311b144 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift @@ -27,20 +27,21 @@ class PhoneAuthViewController: OtherAuthViewController { } // MARK: - Firebase 🔥 - + private func phoneAuthLogin(_ phoneNumber: String) { let phoneNumber = String(format: "+%@", phoneNumber) - PhoneAuthProvider.provider() - .verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in - guard error == nil else { return self.displayError(error) } - - guard let verificationID = verificationID else { return } - self.presentPhoneAuthController { verificationCode in - let credential = PhoneAuthProvider.provider() - .credential(withVerificationID: verificationID, verificationCode: verificationCode) - self.signin(with: credential) - } + Task { + do { + let phoneAuthProvider = PhoneAuthProvider.provider() + let verificationID = try await phoneAuthProvider.verifyPhoneNumber(phoneNumber) + let verificationCode = try await getVerificationCode() + let credential = phoneAuthProvider.credential(withVerificationID: verificationID, + verificationCode: verificationCode) + self.signin(with: credential) + } catch { + self.displayError(error) } + } } private func signin(with credential: PhoneAuthCredential) { @@ -74,4 +75,17 @@ class PhoneAuthViewController: OtherAuthViewController { present(phoneAuthController, animated: true, completion: nil) } + + private func getVerificationCode() async throws -> String { + return try await withCheckedThrowingContinuation { continuation in + self.presentPhoneAuthController{ code in + if code != "" { + continuation.resume(returning: code) + } else { + // Cancelled + continuation.resume(throwing: NSError()) + } + } + } + } } From 150d805192aad65c80f51da2ac808bfe15e6e914 Mon Sep 17 00:00:00 2001 From: Pragati Date: Thu, 20 Jun 2024 12:29:50 -0700 Subject: [PATCH 03/43] undo explicit package invocation --- .../Sources/Swift/AuthProvider/PhoneAuthProvider.swift | 1 - .../Sources/Swift/Utilities/AuthRecaptchaVerifier.swift | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 2ae3ca059e0..9df7073150e 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -14,7 +14,6 @@ import FirebaseCore import Foundation -import RecaptchaEnterprise /// A concrete implementation of `AuthProvider` for phone auth providers. /// diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 82ae0a61286..47c64adc9e1 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -105,8 +105,9 @@ enum AuthRecaptchaAction: String { do { try await retrieveRecaptchaConfig(forceRefresh: forceRefresh) if recaptchaClient == nil { - guard let siteKey = siteKey() else { - throw AuthErrorUtils.error(code: .internalError, message: "Invalid sitekey") + guard let siteKey = siteKey(), + let recaptcha: AnyClass = NSClassFromString("Recaptcha") else { + throw AuthErrorUtils.recaptchaSDKNotLinkedError() } recaptchaClient = try await Recaptcha.getClient(withSiteKey: siteKey) } From 7fda3a440e43a538f9ff6cf751da11d2968d9211 Mon Sep 17 00:00:00 2001 From: Pragati Date: Wed, 26 Jun 2024 18:16:57 -0700 Subject: [PATCH 04/43] Barebones e2e flow for phone auth login --- .../AuthProvider/PhoneAuthProvider.swift | 19 +++++++++---------- .../Utilities/AuthRecaptchaVerifier.swift | 2 ++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 9df7073150e..20a1149cc9b 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -114,9 +114,6 @@ import Foundation ) } do { -// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) -// let enablementStatus = recaptchaVerifier.enablementStatus(forProvider: .phone) - // regular phone number login if let verificationID = try await internalVerify(phoneNumber: phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) { return verificationID } else { @@ -251,11 +248,14 @@ import Foundation retryOnInvalidAppCredential: Bool, uiDelegate: AuthUIDelegate?) async throws -> String? { - let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) + //let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, - codeIdentity: codeIdentity, + codeIdentity: CodeIdentity.empty, requestConfiguration: auth .requestConfiguration) + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) + try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) do { let response = try await AuthBackend.call(with: request) @@ -285,12 +285,11 @@ import Foundation codeIdentity: CodeIdentity.empty, requestConfiguration: auth.requestConfiguration ) -// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) -// let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) -// if(enforcement != .off) { -// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) -// } + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + //let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) + try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) let response = try await AuthBackend.call(with: request) + print(response) return response.verificationID } guard let session else { diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 47c64adc9e1..a44def2ddc4 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -53,6 +53,8 @@ enum AuthRecaptchaAction: String { case getOobCode = "getOobCode" case signUpPassword = "signUpPassword" case sendVerificationCode = "sendVerificationCode" + case startMfaSignin = "startMfaSignin" + case startMfaEnrollment = "startMfaEnrollment" // Convenience property for mapping values var stringValue: String { self.rawValue } From 2fbc0011d578a960bd581e9c89dffe5fa76b1ab2 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:21:08 -0700 Subject: [PATCH 05/43] add logic for verify rce --- .../AuthProvider/PhoneAuthProvider.swift | 161 ++++++++++++------ .../Utilities/AuthRecaptchaVerifier.swift | 2 +- 2 files changed, 106 insertions(+), 57 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 20a1149cc9b..90961e83d4a 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -123,24 +123,6 @@ import Foundation throw error } } - -// @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) -// open func verifyPhoneNumberWithRecaptcha(_ phoneNumber: String, -// uiDelegate: AuthUIDelegate? = nil, -// multiFactorSession: MultiFactorSession? = nil) async throws -// -> String { -// guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, -// urlTypes: auth.mainBundleUrlTypes) else { -// fatalError( -// "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." -// ) -// } -// do { -// -// } catch { -// throw error -// } -// } /// Verify ownership of the second factor phone number by the current user. /// - Parameter multiFactorInfo: The phone multi factor whose number need to be verified. @@ -203,40 +185,58 @@ import Foundation private func internalVerify(phoneNumber: String, uiDelegate: AuthUIDelegate?, multiFactorSession: MultiFactorSession? = nil) async throws - -> String? { - guard phoneNumber.count > 0 else { - throw AuthErrorUtils.missingPhoneNumberError(message: nil) - } - guard let manager = auth.notificationManager else { - throw AuthErrorUtils.notificationNotForwardedError() - } - guard await manager.checkNotificationForwarding() else { - throw AuthErrorUtils.notificationNotForwardedError() - } + -> String? { + guard phoneNumber.count > 0 else { + throw AuthErrorUtils.missingPhoneNumberError(message: nil) + } + guard let manager = auth.notificationManager else { + throw AuthErrorUtils.notificationNotForwardedError() + } + guard await manager.checkNotificationForwarding() else { + throw AuthErrorUtils.notificationNotForwardedError() + } + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) + let enablement = recaptchaVerifier.enablementStatus(forProvider: .phone) + if(enablement == .off) { return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + } else { + do { + if(enablement == .audit) { + return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + } else { + return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: false, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + } + } catch { + throw error + } } + } -// private func internalVerifyWithRecaptcha(phoneNumber: String, -// uiDelegate: AuthUIDelegate?, -// multiFactorSession: MultiFactorSession? = nil) async throws -// -> String? { -// guard phoneNumber.count > 0 else { -// throw AuthErrorUtils.missingPhoneNumberError(message: nil) -// } -// guard let manager = auth.notificationManager else { -// throw AuthErrorUtils.notificationNotForwardedError() -// } -// guard await manager.checkNotificationForwarding() else { -// throw AuthErrorUtils.notificationNotForwardedError() -// } -// return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, -// retryOnInvalidAppCredential: true, -// multiFactorSession: multiFactorSession, -// uiDelegate: uiDelegate) -// } + private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, + retryOnInvalidAppCredential: Bool, + uiDelegate: AuthUIDelegate?) async throws + -> String? { + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, + codeIdentity: CodeIdentity.empty, + requestConfiguration: auth + .requestConfiguration) + do { + try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) + let response = try await AuthBackend.call(with: request) + return response.verificationID + } catch { + return try await handleVerifyErrorWithRetry(error: error, + phoneNumber: phoneNumber, + retryOnInvalidAppCredential: retryOnInvalidAppCredential, + multiFactorSession: nil, + uiDelegate: uiDelegate) + } + } /// Starts the flow to verify the client via silent push notification. /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an @@ -248,15 +248,11 @@ import Foundation retryOnInvalidAppCredential: Bool, uiDelegate: AuthUIDelegate?) async throws -> String? { - //let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) + let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, - codeIdentity: CodeIdentity.empty, + codeIdentity: codeIdentity, requestConfiguration: auth .requestConfiguration) - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) - try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) - do { let response = try await AuthBackend.call(with: request) return response.verificationID @@ -268,6 +264,63 @@ import Foundation uiDelegate: uiDelegate) } } + + /// Starts the flow to verify the client via silent push notification. + /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an + /// AuthErrorCodeInvalidAppCredential error is returned from the backend. + /// - Parameter phoneNumber: The phone number to be verified. + private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, + retryOnInvalidAppCredential: Bool, + multiFactorSession session: MultiFactorSession?, + uiDelegate: AuthUIDelegate?) async throws + -> String? { +// if let settings = auth.settings, +// settings.isAppVerificationDisabledForTesting { +// let request = SendVerificationCodeRequest( +// phoneNumber: phoneNumber, +// codeIdentity: CodeIdentity.empty, +// requestConfiguration: auth.requestConfiguration +// ) +// let response = try await AuthBackend.call(with: request) +// return response.verificationID +// } + guard let session else { + return try await verifyClAndSendVerificationCodeWithRecaptcha( + toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: retryOnInvalidAppCredential, + uiDelegate: uiDelegate + ) + } + let startMFARequestInfo = AuthProtoStartMFAPhoneRequestInfo(phoneNumber: phoneNumber, + codeIdentity: CodeIdentity.empty) + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + do { + if let idToken = session.idToken { + let request = StartMFAEnrollmentRequest(idToken: idToken, + enrollmentInfo: startMFARequestInfo, + requestConfiguration: auth.requestConfiguration) + try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .startMfaEnrollment) + let response = try await AuthBackend.call(with: request) + return response.phoneSessionInfo?.sessionInfo + } else { + let request = StartMFASignInRequest(MFAPendingCredential: session.mfaPendingCredential, + MFAEnrollmentID: session.multiFactorInfo?.uid, + signInInfo: startMFARequestInfo, + requestConfiguration: auth.requestConfiguration) + try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .startMfaSignin) + let response = try await AuthBackend.call(with: request) + return response.responseInfo?.sessionInfo + } + } catch { + return try await handleVerifyErrorWithRetry( + error: error, + phoneNumber: phoneNumber, + retryOnInvalidAppCredential: retryOnInvalidAppCredential, + multiFactorSession: session, + uiDelegate: uiDelegate + ) + } + } /// Starts the flow to verify the client via silent push notification. /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an @@ -285,11 +338,7 @@ import Foundation codeIdentity: CodeIdentity.empty, requestConfiguration: auth.requestConfiguration ) - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - //let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) - try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) let response = try await AuthBackend.call(with: request) - print(response) return response.verificationID } guard let session else { diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index a44def2ddc4..4e9b082e0d9 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -190,7 +190,7 @@ enum AuthRecaptchaAction: String { provider: AuthRecaptchaProvider, action: AuthRecaptchaAction) async throws { try await retrieveRecaptchaConfig(forceRefresh: false) - if enablementStatus(forProvider: provider) != AuthRecaptchaEnablementStatus.off { + if enablementStatus(forProvider: provider) != .off { let token = try await verify(forceRefresh: false, action: action) request.injectRecaptchaFields(recaptchaResponse: token, recaptchaVersion: kRecaptchaVersion) } else { From 5477d9dde20cde5397887c9e3dac05777f74e4ea Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:35:06 -0700 Subject: [PATCH 06/43] lint formatting --- FirebaseAuth/Sources/Swift/Auth/Auth.swift | 2 +- .../AuthProvider/PhoneAuthProvider.swift | 47 +++++++++------- .../RPC/SendVerificationTokenRequest.swift | 10 ++-- .../Utilities/AuthRecaptchaVerifier.swift | 55 ++++++++++--------- 4 files changed, 60 insertions(+), 54 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/Auth/Auth.swift b/FirebaseAuth/Sources/Swift/Auth/Auth.swift index cb125ca131a..5404d52fa8d 100644 --- a/FirebaseAuth/Sources/Swift/Auth/Auth.swift +++ b/FirebaseAuth/Sources/Swift/Auth/Auth.swift @@ -2287,7 +2287,7 @@ extension Auth: AuthInterop { action: AuthRecaptchaAction) async throws -> T .Response { let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: self) - if recaptchaVerifier.enablementStatus(forProvider: AuthRecaptchaProvider.password) != .off{ + if recaptchaVerifier.enablementStatus(forProvider: AuthRecaptchaProvider.password) != .off { try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: AuthRecaptchaProvider.password, action: action) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 9df7073150e..c00e618b8c7 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -107,26 +107,30 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession? = nil) async throws -> String { - guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, - urlTypes: auth.mainBundleUrlTypes) else { - fatalError( - "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." - ) - } - do { + guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, + urlTypes: auth.mainBundleUrlTypes) else { + fatalError( + "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." + ) + } + do { // let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) // let enablementStatus = recaptchaVerifier.enablementStatus(forProvider: .phone) - // regular phone number login - if let verificationID = try await internalVerify(phoneNumber: phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) { - return verificationID - } else { - throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") - } - } catch { - throw error + // regular phone number login + if let verificationID = try await internalVerify( + phoneNumber: phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession + ) { + return verificationID + } else { + throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") } + } catch { + throw error + } } - + // @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) // open func verifyPhoneNumberWithRecaptcha(_ phoneNumber: String, // uiDelegate: AuthUIDelegate? = nil, @@ -139,7 +143,7 @@ import Foundation // ) // } // do { -// +// // } catch { // throw error // } @@ -180,8 +184,8 @@ import Foundation do { multiFactorSession?.multiFactorInfo = multiFactorInfo return try await verifyPhoneNumber(multiFactorInfo.phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) } catch { throw error } @@ -221,7 +225,7 @@ import Foundation multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) } - + // private func internalVerifyWithRecaptcha(phoneNumber: String, // uiDelegate: AuthUIDelegate?, // multiFactorSession: MultiFactorSession? = nil) async throws @@ -288,7 +292,8 @@ import Foundation // let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) // let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) // if(enforcement != .off) { -// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) +// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, +// action: .sendVerificationCode) // } let response = try await AuthBackend.call(with: request) return response.verificationID diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift index 5ec5abaf4a9..c599c6955c1 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/SendVerificationTokenRequest.swift @@ -58,11 +58,11 @@ class SendVerificationCodeRequest: IdentityToolkitRequest, AuthRPCRequest { /// The credential or reCAPTCHA token to prove the identity of the app in order to send the /// verification code. let codeIdentity: CodeIdentity - - /// Response to the captcha. + + /// Response to the captcha. var captchaResponse: String? - - /// The reCAPTCHA version. + + /// The reCAPTCHA version. var recaptchaVersion: String? init(phoneNumber: String, codeIdentity: CodeIdentity, @@ -98,7 +98,7 @@ class SendVerificationCodeRequest: IdentityToolkitRequest, AuthRPCRequest { postBody[kClientType] = clientType return postBody } - + func injectRecaptchaFields(recaptchaResponse: String?, recaptchaVersion: String) { captchaResponse = recaptchaResponse self.recaptchaVersion = recaptchaVersion diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 6529cc00c61..67b92b86197 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -21,45 +21,45 @@ #endif import RecaptchaInterop - @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) class AuthRecaptchaConfig { let siteKey: String let enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] - init(siteKey: String, enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus]) { + init(siteKey: String, + enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus]) { self.siteKey = siteKey self.enablementStatus = enablementStatus } } -enum AuthRecaptchaEnablementStatus: String, CaseIterable { - case enforce = "ENFORCE" - case audit = "AUDIT" - case off = "OFF" - + enum AuthRecaptchaEnablementStatus: String, CaseIterable { + case enforce = "ENFORCE" + case audit = "AUDIT" + case off = "OFF" + // Convenience property for mapping values - var stringValue: String { self.rawValue } -} + var stringValue: String { rawValue } + } + + enum AuthRecaptchaProvider: String, CaseIterable { + case password = "EMAIL_PASSWORD_PROVIDER" + case phone = "PHONE_PROVIDER" -enum AuthRecaptchaProvider: String, CaseIterable { - case password = "EMAIL_PASSWORD_PROVIDER" - case phone = "PHONE_PROVIDER" - // Convenience property for mapping values - var stringValue: String { self.rawValue } -} - -enum AuthRecaptchaAction: String { - case defaultAction - case signInWithPassword = "signInWithPassword" - case getOobCode = "getOobCode" - case signUpPassword = "signUpPassword" - case sendVerificationCode = "sendVerificationCode" - + var stringValue: String { rawValue } + } + + enum AuthRecaptchaAction: String { + case defaultAction + case signInWithPassword + case getOobCode + case signUpPassword + case sendVerificationCode + // Convenience property for mapping values - var stringValue: String { self.rawValue } -} + var stringValue: String { rawValue } + } @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) class AuthRecaptchaVerifier { @@ -91,9 +91,10 @@ enum AuthRecaptchaAction: String { return agentConfig?.siteKey } - func enablementStatus(forProvider provider: AuthRecaptchaProvider) -> AuthRecaptchaEnablementStatus { + func enablementStatus(forProvider provider: AuthRecaptchaProvider) + -> AuthRecaptchaEnablementStatus { if let tenantID = auth?.tenantID, - let tenantConfig = tenantConfigs[tenantID], + let tenantConfig = tenantConfigs[tenantID], let status = tenantConfig.enablementStatus[provider] { return status } else if let agentConfig = agentConfig, From 2e2d67f18340d6d54e40aab214d93e1b7aa4378b Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:36:57 -0700 Subject: [PATCH 07/43] lint formatting --- .../PhoneAuthViewController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift index 62dc311b144..cbb86430685 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift @@ -27,7 +27,7 @@ class PhoneAuthViewController: OtherAuthViewController { } // MARK: - Firebase 🔥 - + private func phoneAuthLogin(_ phoneNumber: String) { let phoneNumber = String(format: "+%@", phoneNumber) Task { @@ -75,14 +75,14 @@ class PhoneAuthViewController: OtherAuthViewController { present(phoneAuthController, animated: true, completion: nil) } - + private func getVerificationCode() async throws -> String { return try await withCheckedThrowingContinuation { continuation in - self.presentPhoneAuthController{ code in + self.presentPhoneAuthController { code in if code != "" { continuation.resume(returning: code) } else { - // Cancelled + // Cancelled continuation.resume(throwing: NSError()) } } From 4551a264d97d94968440b2cf48423d5fb295d4d4 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:46:49 -0700 Subject: [PATCH 08/43] lint fixes --- .../Utilities/AuthRecaptchaVerifier.swift | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 5ceae626d6d..cd03ccd1c9c 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -47,18 +47,18 @@ case phone = "PHONE_PROVIDER" // Convenience property for mapping values - var stringValue: String { self.rawValue } -} - -enum AuthRecaptchaAction: String { - case defaultAction - case signInWithPassword = "signInWithPassword" - case getOobCode = "getOobCode" - case signUpPassword = "signUpPassword" - case sendVerificationCode = "sendVerificationCode" - case startMfaSignin = "startMfaSignin" - case startMfaEnrollment = "startMfaEnrollment" - + var stringValue: String { rawValue } + } + + enum AuthRecaptchaAction: String { + case defaultAction + case signInWithPassword + case getOobCode + case signUpPassword + case sendVerificationCode + case startMfaSignin + case startMfaEnrollment + // Convenience property for mapping values var stringValue: String { rawValue } } From 6159e24af91953c19625e591478891821b966965 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:55:57 -0700 Subject: [PATCH 09/43] whitespace --- .../Sources/Swift/AuthProvider/PhoneAuthProvider.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 90f885c88ca..dbb6192a4ea 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -218,7 +218,7 @@ import Foundation } } } - + private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, uiDelegate: AuthUIDelegate?) async throws @@ -267,7 +267,7 @@ import Foundation uiDelegate: uiDelegate) } } - + /// Starts the flow to verify the client via silent push notification. /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an /// AuthErrorCodeInvalidAppCredential error is returned from the backend. From abaa93067075a43887a88a49631e251b7b435652 Mon Sep 17 00:00:00 2001 From: Pragati Date: Mon, 8 Jul 2024 08:27:12 -0700 Subject: [PATCH 10/43] remove comments --- .../SampleSwift/AuthenticationExample/AppDelegate.swift | 1 - .../SampleSwift/AuthenticationExample/SwiftApplication.plist | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift index cfe070a8bbd..12efe53800d 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift @@ -15,7 +15,6 @@ import FBSDKCoreKit import FirebaseCore import GoogleSignIn -import RecaptchaEnterprise import UIKit @UIApplicationMain diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist index b44dd94c267..50378fe58b0 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist @@ -24,10 +24,7 @@ CFBundleURLName CFBundleURLSchemes - - app-1-585304629422-ios-cb0e5761816443a9d98d3f - com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b - + CFBundleVersion From 442fbb32de09dba1b4c6551b6758a0625cb43e2b Mon Sep 17 00:00:00 2001 From: Pragati Date: Wed, 26 Jun 2024 18:16:57 -0700 Subject: [PATCH 11/43] Barebones e2e flow for phone auth login --- .../Sources/Swift/AuthProvider/PhoneAuthProvider.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index dbb6192a4ea..8f42cd4c992 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -251,9 +251,9 @@ import Foundation retryOnInvalidAppCredential: Bool, uiDelegate: AuthUIDelegate?) async throws -> String? { - let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) + //let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, - codeIdentity: codeIdentity, + codeIdentity: CodeIdentity.empty, requestConfiguration: auth .requestConfiguration) do { @@ -342,6 +342,7 @@ import Foundation requestConfiguration: auth.requestConfiguration ) let response = try await AuthBackend.call(with: request) + print(response) return response.verificationID } guard let session else { From 8df9d02e4c45a37ec3fe22e1467db7890beb8847 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:21:08 -0700 Subject: [PATCH 12/43] add logic for verify rce --- .../Sources/Swift/AuthProvider/PhoneAuthProvider.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 8f42cd4c992..dbb6192a4ea 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -251,9 +251,9 @@ import Foundation retryOnInvalidAppCredential: Bool, uiDelegate: AuthUIDelegate?) async throws -> String? { - //let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) + let codeIdentity = try await verifyClient(withUIDelegate: uiDelegate) let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, - codeIdentity: CodeIdentity.empty, + codeIdentity: codeIdentity, requestConfiguration: auth .requestConfiguration) do { @@ -342,7 +342,6 @@ import Foundation requestConfiguration: auth.requestConfiguration ) let response = try await AuthBackend.call(with: request) - print(response) return response.verificationID } guard let session else { From 3370b9db8f820bbdfb44d0806f30727559c8f509 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:46:49 -0700 Subject: [PATCH 13/43] lint fixes --- .../Sources/Swift/Utilities/AuthRecaptchaVerifier.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index cd03ccd1c9c..469c4a4b60e 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -49,6 +49,8 @@ // Convenience property for mapping values var stringValue: String { rawValue } } + var stringValue: String { rawValue } + } enum AuthRecaptchaAction: String { case defaultAction From 40f4f742c24cdf4a00b230ee24f66dfe048d08e4 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 6 Aug 2024 15:59:01 -0700 Subject: [PATCH 14/43] temp changes --- .../AuthProvider/PhoneAuthProvider.swift | 3 -- .../Utilities/AuthRecaptchaVerifier.swift | 2 -- .../Tests/Unit/PhoneAuthProviderTests.swift | 30 ++++++++++--------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index dbb6192a4ea..8e8eda91371 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -122,9 +122,6 @@ import Foundation } catch { throw error } - } catch { - throw error - } } /// Verify ownership of the second factor phone number by the current user. diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 469c4a4b60e..cd03ccd1c9c 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -49,8 +49,6 @@ // Convenience property for mapping values var stringValue: String { rawValue } } - var stringValue: String { rawValue } - } enum AuthRecaptchaAction: String { case defaultAction diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 4cc6955a05b..62cc60f756a 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -622,20 +622,22 @@ let uiDelegate = reCAPTCHAfallback ? FakeUIDelegate() : nil // 2. After setting up the parameters, call `verifyPhoneNumber`. - provider - .verifyPhoneNumber(kTestPhoneNumber, uiDelegate: uiDelegate) { verificationID, error in - - // 8. After the response triggers the callback in the FakePresenter, verify the callback. - XCTAssertTrue(Thread.isMainThread) - if errorCode != 0 { - XCTAssertNil(verificationID) - XCTAssertEqual((error as? NSError)?.code, errorCode) - } else { - XCTAssertNil(error) - XCTAssertEqual(verificationID, self.kTestVerificationID) - } - expectation.fulfill() - } + do { + // Call the async function to verify the phone number + let verificationID = try await provider.verifyPhoneNumber(kTestPhoneNumber, uiDelegate: uiDelegate) + + // Check that we are on the main thread + XCTAssertTrue(Thread.isMainThread) + + // Assert that the verificationID matches the expected value + XCTAssertEqual(verificationID, self.kTestVerificationID) + } catch { + // If an error occurs, assert that verificationID is nil and the error code matches the expected value + XCTAssertNil(verificationID) + XCTAssertEqual((error as? NSError)?.code, errorCode) + } + + // Make sure the test waits for expectations to be fulfilled waitForExpectations(timeout: 5) } From 49995957e231a48e1d54dee39ed4f086674cb4a4 Mon Sep 17 00:00:00 2001 From: Paul Beusterien Date: Tue, 20 Aug 2024 10:05:22 -0700 Subject: [PATCH 15/43] Version 11.2.0 (#13522) --- Firebase.podspec | 48 +++++++++---------- FirebaseABTesting.podspec | 2 +- FirebaseAnalytics.podspec | 6 +-- FirebaseAnalyticsOnDeviceConversion.podspec | 4 +- FirebaseAppCheck.podspec | 2 +- FirebaseAppCheckInterop.podspec | 2 +- FirebaseAppDistribution.podspec | 2 +- FirebaseAuth.podspec | 2 +- FirebaseAuthInterop.podspec | 2 +- FirebaseCore.podspec | 2 +- FirebaseCoreExtension.podspec | 2 +- FirebaseCoreInternal.podspec | 2 +- FirebaseCrashlytics.podspec | 2 +- FirebaseDatabase.podspec | 2 +- FirebaseDynamicLinks.podspec | 2 +- FirebaseFirestore.podspec | 4 +- FirebaseFirestoreInternal.podspec | 2 +- FirebaseFunctions.podspec | 2 +- FirebaseInAppMessaging.podspec | 2 +- FirebaseInstallations.podspec | 2 +- FirebaseMLModelDownloader.podspec | 2 +- FirebaseMessaging.podspec | 2 +- FirebaseMessagingInterop.podspec | 2 +- FirebasePerformance.podspec | 2 +- FirebaseRemoteConfig.podspec | 2 +- FirebaseRemoteConfigInterop.podspec | 2 +- FirebaseSessions.podspec | 2 +- FirebaseSharedSwift.podspec | 2 +- FirebaseStorage.podspec | 2 +- GoogleAppMeasurement.podspec | 4 +- ...leAppMeasurementOnDeviceConversion.podspec | 2 +- Package.swift | 2 +- .../FirebaseManifest/FirebaseManifest.swift | 2 +- 33 files changed, 61 insertions(+), 61 deletions(-) diff --git a/Firebase.podspec b/Firebase.podspec index c4f763803c8..78a0093eef2 100644 --- a/Firebase.podspec +++ b/Firebase.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Firebase' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase' s.description = <<-DESC @@ -36,14 +36,14 @@ Simplify your app development, grow your user base, and monetize more effectivel ss.ios.deployment_target = '12.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' - ss.ios.dependency 'FirebaseAnalytics', '~> 11.1.0' - ss.osx.dependency 'FirebaseAnalytics', '~> 11.1.0' - ss.tvos.dependency 'FirebaseAnalytics', '~> 11.1.0' + ss.ios.dependency 'FirebaseAnalytics', '~> 11.2.0' + ss.osx.dependency 'FirebaseAnalytics', '~> 11.2.0' + ss.tvos.dependency 'FirebaseAnalytics', '~> 11.2.0' ss.dependency 'Firebase/CoreOnly' end s.subspec 'CoreOnly' do |ss| - ss.dependency 'FirebaseCore', '11.1.0' + ss.dependency 'FirebaseCore', '11.2.0' ss.source_files = 'CoreOnly/Sources/Firebase.h' ss.preserve_paths = 'CoreOnly/Sources/module.modulemap' if ENV['FIREBASE_POD_REPO_FOR_DEV_POD'] then @@ -79,13 +79,13 @@ Simplify your app development, grow your user base, and monetize more effectivel ss.ios.deployment_target = '12.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' - ss.dependency 'FirebaseAnalytics/WithoutAdIdSupport', '~> 11.1.0' + ss.dependency 'FirebaseAnalytics/WithoutAdIdSupport', '~> 11.2.0' ss.dependency 'Firebase/CoreOnly' end s.subspec 'ABTesting' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseABTesting', '~> 11.1.0' + ss.dependency 'FirebaseABTesting', '~> 11.2.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -95,13 +95,13 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'AppDistribution' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebaseAppDistribution', '~> 11.1.0-beta' + ss.ios.dependency 'FirebaseAppDistribution', '~> 11.2.0-beta' ss.ios.deployment_target = '13.0' end s.subspec 'AppCheck' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseAppCheck', '~> 11.1.0' + ss.dependency 'FirebaseAppCheck', '~> 11.2.0' ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' @@ -110,7 +110,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Auth' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseAuth', '~> 11.1.0' + ss.dependency 'FirebaseAuth', '~> 11.2.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -120,7 +120,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Crashlytics' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseCrashlytics', '~> 11.1.0' + ss.dependency 'FirebaseCrashlytics', '~> 11.2.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -130,7 +130,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Database' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseDatabase', '~> 11.1.0' + ss.dependency 'FirebaseDatabase', '~> 11.2.0' # Standard platforms PLUS watchOS 7. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -140,13 +140,13 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'DynamicLinks' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebaseDynamicLinks', '~> 11.1.0' + ss.ios.dependency 'FirebaseDynamicLinks', '~> 11.2.0' ss.ios.deployment_target = '13.0' end s.subspec 'Firestore' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseFirestore', '~> 11.1.0' + ss.dependency 'FirebaseFirestore', '~> 11.2.0' ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' @@ -154,7 +154,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Functions' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseFunctions', '~> 11.1.0' + ss.dependency 'FirebaseFunctions', '~> 11.2.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -164,20 +164,20 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'InAppMessaging' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebaseInAppMessaging', '~> 11.1.0-beta' - ss.tvos.dependency 'FirebaseInAppMessaging', '~> 11.1.0-beta' + ss.ios.dependency 'FirebaseInAppMessaging', '~> 11.2.0-beta' + ss.tvos.dependency 'FirebaseInAppMessaging', '~> 11.2.0-beta' ss.ios.deployment_target = '13.0' ss.tvos.deployment_target = '13.0' end s.subspec 'Installations' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseInstallations', '~> 11.1.0' + ss.dependency 'FirebaseInstallations', '~> 11.2.0' end s.subspec 'Messaging' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseMessaging', '~> 11.1.0' + ss.dependency 'FirebaseMessaging', '~> 11.2.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -187,7 +187,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'MLModelDownloader' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseMLModelDownloader', '~> 11.1.0-beta' + ss.dependency 'FirebaseMLModelDownloader', '~> 11.2.0-beta' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -197,15 +197,15 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Performance' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebasePerformance', '~> 11.1.0' - ss.tvos.dependency 'FirebasePerformance', '~> 11.1.0' + ss.ios.dependency 'FirebasePerformance', '~> 11.2.0' + ss.tvos.dependency 'FirebasePerformance', '~> 11.2.0' ss.ios.deployment_target = '13.0' ss.tvos.deployment_target = '13.0' end s.subspec 'RemoteConfig' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseRemoteConfig', '~> 11.1.0' + ss.dependency 'FirebaseRemoteConfig', '~> 11.2.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -215,7 +215,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Storage' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseStorage', '~> 11.1.0' + ss.dependency 'FirebaseStorage', '~> 11.2.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' diff --git a/FirebaseABTesting.podspec b/FirebaseABTesting.podspec index 759e7659a02..caedc46bab5 100644 --- a/FirebaseABTesting.podspec +++ b/FirebaseABTesting.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseABTesting' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase ABTesting' s.description = <<-DESC diff --git a/FirebaseAnalytics.podspec b/FirebaseAnalytics.podspec index 9bea2f40088..2daa7ed2935 100644 --- a/FirebaseAnalytics.podspec +++ b/FirebaseAnalytics.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAnalytics' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Analytics for iOS' s.description = <<-DESC @@ -37,12 +37,12 @@ Pod::Spec.new do |s| s.default_subspecs = 'AdIdSupport' s.subspec 'AdIdSupport' do |ss| - ss.dependency 'GoogleAppMeasurement', '11.1.0' + ss.dependency 'GoogleAppMeasurement', '11.2.0' ss.vendored_frameworks = 'Frameworks/FirebaseAnalytics.xcframework' end s.subspec 'WithoutAdIdSupport' do |ss| - ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.1.0' + ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.2.0' ss.vendored_frameworks = 'Frameworks/FirebaseAnalytics.xcframework' end diff --git a/FirebaseAnalyticsOnDeviceConversion.podspec b/FirebaseAnalyticsOnDeviceConversion.podspec index fc64b69e8ea..a36e59f766a 100644 --- a/FirebaseAnalyticsOnDeviceConversion.podspec +++ b/FirebaseAnalyticsOnDeviceConversion.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAnalyticsOnDeviceConversion' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'On device conversion measurement plugin for FirebaseAnalytics. Not intended for direct use.' s.description = <<-DESC @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.cocoapods_version = '>= 1.12.0' - s.dependency 'GoogleAppMeasurementOnDeviceConversion', '11.1.0' + s.dependency 'GoogleAppMeasurementOnDeviceConversion', '11.2.0' s.static_framework = true diff --git a/FirebaseAppCheck.podspec b/FirebaseAppCheck.podspec index 5a53a7a3e0e..125f6ab9bfb 100644 --- a/FirebaseAppCheck.podspec +++ b/FirebaseAppCheck.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAppCheck' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase App Check SDK.' s.description = <<-DESC diff --git a/FirebaseAppCheckInterop.podspec b/FirebaseAppCheckInterop.podspec index 3d8cb017a55..8d17d1e6480 100644 --- a/FirebaseAppCheckInterop.podspec +++ b/FirebaseAppCheckInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAppCheckInterop' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Interfaces that allow other Firebase SDKs to use AppCheck functionality.' s.description = <<-DESC diff --git a/FirebaseAppDistribution.podspec b/FirebaseAppDistribution.podspec index 6fd7f61aa44..9199346e624 100644 --- a/FirebaseAppDistribution.podspec +++ b/FirebaseAppDistribution.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAppDistribution' - s.version = '11.1.0-beta' + s.version = '11.2.0-beta' s.summary = 'App Distribution for Firebase iOS SDK.' s.description = <<-DESC diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index dd8c230a090..70454dde33e 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAuth' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Apple platform client for Firebase Authentication' s.description = <<-DESC diff --git a/FirebaseAuthInterop.podspec b/FirebaseAuthInterop.podspec index fa318c00872..388171a16ff 100644 --- a/FirebaseAuthInterop.podspec +++ b/FirebaseAuthInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAuthInterop' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Interfaces that allow other Firebase SDKs to use Auth functionality.' s.description = <<-DESC diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index fee2fc8983a..35ef886de13 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Core' s.description = <<-DESC diff --git a/FirebaseCoreExtension.podspec b/FirebaseCoreExtension.podspec index a65aa1ba8a0..e882c286d17 100644 --- a/FirebaseCoreExtension.podspec +++ b/FirebaseCoreExtension.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCoreExtension' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Extended FirebaseCore APIs for Firebase product SDKs' s.description = <<-DESC diff --git a/FirebaseCoreInternal.podspec b/FirebaseCoreInternal.podspec index 36366a26fa4..f15563f7d1b 100644 --- a/FirebaseCoreInternal.podspec +++ b/FirebaseCoreInternal.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCoreInternal' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'APIs for internal FirebaseCore usage.' s.description = <<-DESC diff --git a/FirebaseCrashlytics.podspec b/FirebaseCrashlytics.podspec index 2ee7d83bc48..94bb7fc3499 100644 --- a/FirebaseCrashlytics.podspec +++ b/FirebaseCrashlytics.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCrashlytics' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Best and lightest-weight crash reporting for mobile, desktop and tvOS.' s.description = 'Firebase Crashlytics helps you track, prioritize, and fix stability issues that erode app quality.' s.homepage = 'https://firebase.google.com/' diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index 5c100d42ea5..85034b98650 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDatabase' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Realtime Database' s.description = <<-DESC diff --git a/FirebaseDynamicLinks.podspec b/FirebaseDynamicLinks.podspec index 52cbd28c3db..89ad46e4baa 100644 --- a/FirebaseDynamicLinks.podspec +++ b/FirebaseDynamicLinks.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDynamicLinks' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Dynamic Links' s.description = <<-DESC diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 5673c02c75a..782a4341b2b 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestore' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Google Cloud Firestore' s.description = <<-DESC Google Cloud Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development. @@ -37,7 +37,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.dependency 'FirebaseCore', '~> 11.0' s.dependency 'FirebaseCoreExtension', '~> 11.0' - s.dependency 'FirebaseFirestoreInternal', '11.1.0' + s.dependency 'FirebaseFirestoreInternal', '11.2.0' s.dependency 'FirebaseSharedSwift', '~> 11.0' end diff --git a/FirebaseFirestoreInternal.podspec b/FirebaseFirestoreInternal.podspec index 3c5d120a18f..98e5d8e055e 100644 --- a/FirebaseFirestoreInternal.podspec +++ b/FirebaseFirestoreInternal.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestoreInternal' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Google Cloud Firestore' s.description = <<-DESC diff --git a/FirebaseFunctions.podspec b/FirebaseFunctions.podspec index 0da10261b20..ac6be385709 100644 --- a/FirebaseFunctions.podspec +++ b/FirebaseFunctions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFunctions' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Cloud Functions for Firebase' s.description = <<-DESC diff --git a/FirebaseInAppMessaging.podspec b/FirebaseInAppMessaging.podspec index 83f693142c1..ac29ca4a8d3 100644 --- a/FirebaseInAppMessaging.podspec +++ b/FirebaseInAppMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInAppMessaging' - s.version = '11.1.0-beta' + s.version = '11.2.0-beta' s.summary = 'Firebase In-App Messaging for iOS' s.description = <<-DESC diff --git a/FirebaseInstallations.podspec b/FirebaseInstallations.podspec index 259f6c6a074..39e856a0370 100644 --- a/FirebaseInstallations.podspec +++ b/FirebaseInstallations.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInstallations' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Installations' s.description = <<-DESC diff --git a/FirebaseMLModelDownloader.podspec b/FirebaseMLModelDownloader.podspec index 08cf186069a..f58d79a3322 100644 --- a/FirebaseMLModelDownloader.podspec +++ b/FirebaseMLModelDownloader.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMLModelDownloader' - s.version = '11.1.0-beta' + s.version = '11.2.0-beta' s.summary = 'Firebase ML Model Downloader' s.description = <<-DESC diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index 56f08c7a157..7da5f1d61ca 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMessaging' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Messaging' s.description = <<-DESC diff --git a/FirebaseMessagingInterop.podspec b/FirebaseMessagingInterop.podspec index e1085c97b65..5f7017b1cab 100644 --- a/FirebaseMessagingInterop.podspec +++ b/FirebaseMessagingInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMessagingInterop' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Interfaces that allow other Firebase SDKs to use Messaging functionality.' s.description = <<-DESC diff --git a/FirebasePerformance.podspec b/FirebasePerformance.podspec index 2e9832eeeeb..68060017351 100644 --- a/FirebasePerformance.podspec +++ b/FirebasePerformance.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebasePerformance' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Performance' s.description = <<-DESC diff --git a/FirebaseRemoteConfig.podspec b/FirebaseRemoteConfig.podspec index 11bba03374b..fcd404fa48f 100644 --- a/FirebaseRemoteConfig.podspec +++ b/FirebaseRemoteConfig.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseRemoteConfig' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Remote Config' s.description = <<-DESC diff --git a/FirebaseRemoteConfigInterop.podspec b/FirebaseRemoteConfigInterop.podspec index 790b652039a..7d6e2870829 100644 --- a/FirebaseRemoteConfigInterop.podspec +++ b/FirebaseRemoteConfigInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseRemoteConfigInterop' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Interfaces that allow other Firebase SDKs to use Remote Config functionality.' s.description = <<-DESC diff --git a/FirebaseSessions.podspec b/FirebaseSessions.podspec index e0b84c6826a..0bb3c1b096c 100644 --- a/FirebaseSessions.podspec +++ b/FirebaseSessions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseSessions' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Sessions' s.description = <<-DESC diff --git a/FirebaseSharedSwift.podspec b/FirebaseSharedSwift.podspec index f6536cd29fa..786ca4b7a32 100644 --- a/FirebaseSharedSwift.podspec +++ b/FirebaseSharedSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseSharedSwift' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Shared Swift Extensions for Firebase' s.description = <<-DESC diff --git a/FirebaseStorage.podspec b/FirebaseStorage.podspec index 129bfafae40..9fcf1641751 100644 --- a/FirebaseStorage.podspec +++ b/FirebaseStorage.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseStorage' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Firebase Storage' s.description = <<-DESC diff --git a/GoogleAppMeasurement.podspec b/GoogleAppMeasurement.podspec index c06a9437e13..f12fe582f44 100644 --- a/GoogleAppMeasurement.podspec +++ b/GoogleAppMeasurement.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleAppMeasurement' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = 'Shared measurement methods for Google libraries. Not intended for direct use.' s.description = <<-DESC @@ -37,7 +37,7 @@ Pod::Spec.new do |s| s.default_subspecs = 'AdIdSupport' s.subspec 'AdIdSupport' do |ss| - ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.1.0' + ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.2.0' ss.vendored_frameworks = 'Frameworks/GoogleAppMeasurementIdentitySupport.xcframework' end diff --git a/GoogleAppMeasurementOnDeviceConversion.podspec b/GoogleAppMeasurementOnDeviceConversion.podspec index 39f77038f7b..e9c8fe8e599 100644 --- a/GoogleAppMeasurementOnDeviceConversion.podspec +++ b/GoogleAppMeasurementOnDeviceConversion.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleAppMeasurementOnDeviceConversion' - s.version = '11.1.0' + s.version = '11.2.0' s.summary = <<-SUMMARY On device conversion measurement plugin for Google App Measurement. Not intended for direct use. diff --git a/Package.swift b/Package.swift index 8647898aba5..f7c6a12a846 100644 --- a/Package.swift +++ b/Package.swift @@ -19,7 +19,7 @@ import class Foundation.ProcessInfo import PackageDescription -let firebaseVersion = "11.1.0" +let firebaseVersion = "11.2.0" let package = Package( name: "Firebase", diff --git a/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift b/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift index 62bb423a5af..805eaa6045d 100755 --- a/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift +++ b/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift @@ -21,7 +21,7 @@ import Foundation /// The version and releasing fields of the non-Firebase pods should be reviewed every release. /// The array should be ordered so that any pod's dependencies precede it in the list. public let shared = Manifest( - version: "11.1.0", + version: "11.2.0", pods: [ Pod("FirebaseSharedSwift"), Pod("FirebaseCoreInternal"), From e9cfad6013b39db6db68602505fad0df40df888a Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 18 Jun 2024 09:28:57 -0700 Subject: [PATCH 16/43] add recaptcha enterprise dependencies and fix flows to use swift methods --- .../AuthProvider/PhoneAuthProvider.swift | 27 +++++++++++++++++++ .../Utilities/AuthRecaptchaVerifier.swift | 9 +++++++ .../AuthenticationExample/AppDelegate.swift | 1 + .../SwiftApplication.plist | 5 +++- .../PhoneAuthViewController.swift | 2 +- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 8e8eda91371..e2dfd61f96b 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -14,6 +14,7 @@ import FirebaseCore import Foundation +import RecaptchaEnterprise /// A concrete implementation of `AuthProvider` for phone auth providers. /// @@ -114,6 +115,9 @@ import Foundation ) } do { +// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) +// let enablementStatus = recaptchaVerifier.enablementStatus(forProvider: .phone) + // regular phone number login if let verificationID = try await internalVerify(phoneNumber: phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) { return verificationID } else { @@ -123,6 +127,24 @@ import Foundation throw error } } + +// @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) +// open func verifyPhoneNumberWithRecaptcha(_ phoneNumber: String, +// uiDelegate: AuthUIDelegate? = nil, +// multiFactorSession: MultiFactorSession? = nil) async throws +// -> String { +// guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, +// urlTypes: auth.mainBundleUrlTypes) else { +// fatalError( +// "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." +// ) +// } +// do { +// +// } catch { +// throw error +// } +// } /// Verify ownership of the second factor phone number by the current user. /// - Parameter multiFactorInfo: The phone multi factor whose number need to be verified. @@ -338,6 +360,11 @@ import Foundation codeIdentity: CodeIdentity.empty, requestConfiguration: auth.requestConfiguration ) +// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) +// let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) +// if(enforcement != .off) { +// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) +// } let response = try await AuthBackend.call(with: request) return response.verificationID } diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index e73e4f13373..8861aa83fa1 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -33,6 +33,15 @@ } } +enum AuthRecaptchaEnablementStatus: String, CaseIterable { + case enforce = "ENFORCE" + case audit = "AUDIT" + case off = "OFF" + + // Convenience property for mapping values + var stringValue: String { self.rawValue } +} + enum AuthRecaptchaEnablementStatus: String, CaseIterable { case enforce = "ENFORCE" case audit = "AUDIT" diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift index 12efe53800d..cfe070a8bbd 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift @@ -15,6 +15,7 @@ import FBSDKCoreKit import FirebaseCore import GoogleSignIn +import RecaptchaEnterprise import UIKit @UIApplicationMain diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist index 50378fe58b0..b44dd94c267 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist @@ -24,7 +24,10 @@ CFBundleURLName CFBundleURLSchemes - + + app-1-585304629422-ios-cb0e5761816443a9d98d3f + com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b + CFBundleVersion diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift index cbb86430685..5c51425beb8 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift @@ -27,7 +27,7 @@ class PhoneAuthViewController: OtherAuthViewController { } // MARK: - Firebase 🔥 - + private func phoneAuthLogin(_ phoneNumber: String) { let phoneNumber = String(format: "+%@", phoneNumber) Task { From 3855e457b0fe81708e7b3cd55d066b63106c108f Mon Sep 17 00:00:00 2001 From: Pragati Date: Thu, 20 Jun 2024 12:29:50 -0700 Subject: [PATCH 17/43] undo explicit package invocation --- FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index e2dfd61f96b..abf4fcf84f4 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -14,7 +14,6 @@ import FirebaseCore import Foundation -import RecaptchaEnterprise /// A concrete implementation of `AuthProvider` for phone auth providers. /// From 8cf57db189c778add5adb87427282ecb33d34299 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 2 Jul 2024 16:35:06 -0700 Subject: [PATCH 18/43] lint formatting --- .../Swift/Utilities/AuthRecaptchaVerifier.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 8861aa83fa1..4048b589f07 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -33,11 +33,11 @@ } } -enum AuthRecaptchaEnablementStatus: String, CaseIterable { - case enforce = "ENFORCE" - case audit = "AUDIT" - case off = "OFF" - + enum AuthRecaptchaEnablementStatus: String, CaseIterable { + case enforce = "ENFORCE" + case audit = "AUDIT" + case off = "OFF" + // Convenience property for mapping values var stringValue: String { self.rawValue } } From facfa9ac6724668d3f4c6647ba6ad04ac5fa34da Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 20 Aug 2024 14:19:05 -0700 Subject: [PATCH 19/43] use action enum --- .../Sources/Swift/Utilities/AuthRecaptchaVerifier.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 4048b589f07..2d6142c3527 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -121,7 +121,7 @@ guard let siteKey = siteKey() else { throw AuthErrorUtils.recaptchaSiteKeyMissing() } - let actionString = actionToStringMap[action] ?? "" + let actionString = action.stringValue #if !(COCOAPODS || SWIFT_PACKAGE) // No recaptcha on internal build system. return actionString From 90e866e85f9def3dde8e1173d660db039757b2da Mon Sep 17 00:00:00 2001 From: pragatimodi <110490169+pragatimodi@users.noreply.github.com> Date: Tue, 20 Aug 2024 23:02:06 +0000 Subject: [PATCH 20/43] revert pod changes --- Firebase.podspec | 48 +++++++++---------- FirebaseABTesting.podspec | 2 +- FirebaseAnalytics.podspec | 6 +-- FirebaseAnalyticsOnDeviceConversion.podspec | 4 +- FirebaseAppCheck.podspec | 2 +- FirebaseAppCheckInterop.podspec | 2 +- FirebaseAppDistribution.podspec | 2 +- FirebaseAuth.podspec | 2 +- .../AuthenticationExample/AppDelegate.swift | 1 - .../SwiftApplication.plist | 5 +- FirebaseAuthInterop.podspec | 2 +- FirebaseCore.podspec | 2 +- FirebaseCoreExtension.podspec | 2 +- FirebaseCoreInternal.podspec | 2 +- FirebaseCrashlytics.podspec | 2 +- FirebaseDatabase.podspec | 2 +- FirebaseDynamicLinks.podspec | 2 +- FirebaseFirestore.podspec | 2 +- FirebaseFirestoreInternal.podspec | 2 +- FirebaseFunctions.podspec | 2 +- FirebaseInAppMessaging.podspec | 2 +- FirebaseInstallations.podspec | 2 +- FirebaseMLModelDownloader.podspec | 2 +- FirebaseMessaging.podspec | 2 +- FirebaseMessagingInterop.podspec | 2 +- FirebasePerformance.podspec | 2 +- FirebaseRemoteConfig.podspec | 2 +- FirebaseRemoteConfigInterop.podspec | 2 +- FirebaseSessions.podspec | 2 +- FirebaseStorage.podspec | 2 +- GoogleAppMeasurement.podspec | 4 +- ...leAppMeasurementOnDeviceConversion.podspec | 2 +- Package.swift | 2 +- .../FirebaseManifest/FirebaseManifest.swift | 2 +- 34 files changed, 60 insertions(+), 64 deletions(-) diff --git a/Firebase.podspec b/Firebase.podspec index 78a0093eef2..c4f763803c8 100644 --- a/Firebase.podspec +++ b/Firebase.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Firebase' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase' s.description = <<-DESC @@ -36,14 +36,14 @@ Simplify your app development, grow your user base, and monetize more effectivel ss.ios.deployment_target = '12.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' - ss.ios.dependency 'FirebaseAnalytics', '~> 11.2.0' - ss.osx.dependency 'FirebaseAnalytics', '~> 11.2.0' - ss.tvos.dependency 'FirebaseAnalytics', '~> 11.2.0' + ss.ios.dependency 'FirebaseAnalytics', '~> 11.1.0' + ss.osx.dependency 'FirebaseAnalytics', '~> 11.1.0' + ss.tvos.dependency 'FirebaseAnalytics', '~> 11.1.0' ss.dependency 'Firebase/CoreOnly' end s.subspec 'CoreOnly' do |ss| - ss.dependency 'FirebaseCore', '11.2.0' + ss.dependency 'FirebaseCore', '11.1.0' ss.source_files = 'CoreOnly/Sources/Firebase.h' ss.preserve_paths = 'CoreOnly/Sources/module.modulemap' if ENV['FIREBASE_POD_REPO_FOR_DEV_POD'] then @@ -79,13 +79,13 @@ Simplify your app development, grow your user base, and monetize more effectivel ss.ios.deployment_target = '12.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' - ss.dependency 'FirebaseAnalytics/WithoutAdIdSupport', '~> 11.2.0' + ss.dependency 'FirebaseAnalytics/WithoutAdIdSupport', '~> 11.1.0' ss.dependency 'Firebase/CoreOnly' end s.subspec 'ABTesting' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseABTesting', '~> 11.2.0' + ss.dependency 'FirebaseABTesting', '~> 11.1.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -95,13 +95,13 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'AppDistribution' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebaseAppDistribution', '~> 11.2.0-beta' + ss.ios.dependency 'FirebaseAppDistribution', '~> 11.1.0-beta' ss.ios.deployment_target = '13.0' end s.subspec 'AppCheck' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseAppCheck', '~> 11.2.0' + ss.dependency 'FirebaseAppCheck', '~> 11.1.0' ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' @@ -110,7 +110,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Auth' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseAuth', '~> 11.2.0' + ss.dependency 'FirebaseAuth', '~> 11.1.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -120,7 +120,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Crashlytics' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseCrashlytics', '~> 11.2.0' + ss.dependency 'FirebaseCrashlytics', '~> 11.1.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -130,7 +130,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Database' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseDatabase', '~> 11.2.0' + ss.dependency 'FirebaseDatabase', '~> 11.1.0' # Standard platforms PLUS watchOS 7. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -140,13 +140,13 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'DynamicLinks' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebaseDynamicLinks', '~> 11.2.0' + ss.ios.dependency 'FirebaseDynamicLinks', '~> 11.1.0' ss.ios.deployment_target = '13.0' end s.subspec 'Firestore' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseFirestore', '~> 11.2.0' + ss.dependency 'FirebaseFirestore', '~> 11.1.0' ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' ss.tvos.deployment_target = '13.0' @@ -154,7 +154,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Functions' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseFunctions', '~> 11.2.0' + ss.dependency 'FirebaseFunctions', '~> 11.1.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -164,20 +164,20 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'InAppMessaging' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebaseInAppMessaging', '~> 11.2.0-beta' - ss.tvos.dependency 'FirebaseInAppMessaging', '~> 11.2.0-beta' + ss.ios.dependency 'FirebaseInAppMessaging', '~> 11.1.0-beta' + ss.tvos.dependency 'FirebaseInAppMessaging', '~> 11.1.0-beta' ss.ios.deployment_target = '13.0' ss.tvos.deployment_target = '13.0' end s.subspec 'Installations' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseInstallations', '~> 11.2.0' + ss.dependency 'FirebaseInstallations', '~> 11.1.0' end s.subspec 'Messaging' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseMessaging', '~> 11.2.0' + ss.dependency 'FirebaseMessaging', '~> 11.1.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -187,7 +187,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'MLModelDownloader' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseMLModelDownloader', '~> 11.2.0-beta' + ss.dependency 'FirebaseMLModelDownloader', '~> 11.1.0-beta' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -197,15 +197,15 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Performance' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.ios.dependency 'FirebasePerformance', '~> 11.2.0' - ss.tvos.dependency 'FirebasePerformance', '~> 11.2.0' + ss.ios.dependency 'FirebasePerformance', '~> 11.1.0' + ss.tvos.dependency 'FirebasePerformance', '~> 11.1.0' ss.ios.deployment_target = '13.0' ss.tvos.deployment_target = '13.0' end s.subspec 'RemoteConfig' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseRemoteConfig', '~> 11.2.0' + ss.dependency 'FirebaseRemoteConfig', '~> 11.1.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' @@ -215,7 +215,7 @@ Simplify your app development, grow your user base, and monetize more effectivel s.subspec 'Storage' do |ss| ss.dependency 'Firebase/CoreOnly' - ss.dependency 'FirebaseStorage', '~> 11.2.0' + ss.dependency 'FirebaseStorage', '~> 11.1.0' # Standard platforms PLUS watchOS. ss.ios.deployment_target = '13.0' ss.osx.deployment_target = '10.15' diff --git a/FirebaseABTesting.podspec b/FirebaseABTesting.podspec index caedc46bab5..759e7659a02 100644 --- a/FirebaseABTesting.podspec +++ b/FirebaseABTesting.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseABTesting' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase ABTesting' s.description = <<-DESC diff --git a/FirebaseAnalytics.podspec b/FirebaseAnalytics.podspec index 2daa7ed2935..9bea2f40088 100644 --- a/FirebaseAnalytics.podspec +++ b/FirebaseAnalytics.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAnalytics' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Analytics for iOS' s.description = <<-DESC @@ -37,12 +37,12 @@ Pod::Spec.new do |s| s.default_subspecs = 'AdIdSupport' s.subspec 'AdIdSupport' do |ss| - ss.dependency 'GoogleAppMeasurement', '11.2.0' + ss.dependency 'GoogleAppMeasurement', '11.1.0' ss.vendored_frameworks = 'Frameworks/FirebaseAnalytics.xcframework' end s.subspec 'WithoutAdIdSupport' do |ss| - ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.2.0' + ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.1.0' ss.vendored_frameworks = 'Frameworks/FirebaseAnalytics.xcframework' end diff --git a/FirebaseAnalyticsOnDeviceConversion.podspec b/FirebaseAnalyticsOnDeviceConversion.podspec index a36e59f766a..fc64b69e8ea 100644 --- a/FirebaseAnalyticsOnDeviceConversion.podspec +++ b/FirebaseAnalyticsOnDeviceConversion.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAnalyticsOnDeviceConversion' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'On device conversion measurement plugin for FirebaseAnalytics. Not intended for direct use.' s.description = <<-DESC @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.cocoapods_version = '>= 1.12.0' - s.dependency 'GoogleAppMeasurementOnDeviceConversion', '11.2.0' + s.dependency 'GoogleAppMeasurementOnDeviceConversion', '11.1.0' s.static_framework = true diff --git a/FirebaseAppCheck.podspec b/FirebaseAppCheck.podspec index 125f6ab9bfb..5a53a7a3e0e 100644 --- a/FirebaseAppCheck.podspec +++ b/FirebaseAppCheck.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAppCheck' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase App Check SDK.' s.description = <<-DESC diff --git a/FirebaseAppCheckInterop.podspec b/FirebaseAppCheckInterop.podspec index 8d17d1e6480..3d8cb017a55 100644 --- a/FirebaseAppCheckInterop.podspec +++ b/FirebaseAppCheckInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAppCheckInterop' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Interfaces that allow other Firebase SDKs to use AppCheck functionality.' s.description = <<-DESC diff --git a/FirebaseAppDistribution.podspec b/FirebaseAppDistribution.podspec index 9199346e624..6fd7f61aa44 100644 --- a/FirebaseAppDistribution.podspec +++ b/FirebaseAppDistribution.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAppDistribution' - s.version = '11.2.0-beta' + s.version = '11.1.0-beta' s.summary = 'App Distribution for Firebase iOS SDK.' s.description = <<-DESC diff --git a/FirebaseAuth.podspec b/FirebaseAuth.podspec index 70454dde33e..dd8c230a090 100644 --- a/FirebaseAuth.podspec +++ b/FirebaseAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAuth' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Apple platform client for Firebase Authentication' s.description = <<-DESC diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift index cfe070a8bbd..12efe53800d 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/AppDelegate.swift @@ -15,7 +15,6 @@ import FBSDKCoreKit import FirebaseCore import GoogleSignIn -import RecaptchaEnterprise import UIKit @UIApplicationMain diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist index b44dd94c267..50378fe58b0 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist @@ -24,10 +24,7 @@ CFBundleURLName CFBundleURLSchemes - - app-1-585304629422-ios-cb0e5761816443a9d98d3f - com.googleusercontent.apps.585304629422-ab795evno6vsrj7i7o1a3gi6eo01508b - + CFBundleVersion diff --git a/FirebaseAuthInterop.podspec b/FirebaseAuthInterop.podspec index 388171a16ff..fa318c00872 100644 --- a/FirebaseAuthInterop.podspec +++ b/FirebaseAuthInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseAuthInterop' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Interfaces that allow other Firebase SDKs to use Auth functionality.' s.description = <<-DESC diff --git a/FirebaseCore.podspec b/FirebaseCore.podspec index 35ef886de13..fee2fc8983a 100644 --- a/FirebaseCore.podspec +++ b/FirebaseCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCore' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Core' s.description = <<-DESC diff --git a/FirebaseCoreExtension.podspec b/FirebaseCoreExtension.podspec index e882c286d17..a65aa1ba8a0 100644 --- a/FirebaseCoreExtension.podspec +++ b/FirebaseCoreExtension.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCoreExtension' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Extended FirebaseCore APIs for Firebase product SDKs' s.description = <<-DESC diff --git a/FirebaseCoreInternal.podspec b/FirebaseCoreInternal.podspec index f15563f7d1b..36366a26fa4 100644 --- a/FirebaseCoreInternal.podspec +++ b/FirebaseCoreInternal.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCoreInternal' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'APIs for internal FirebaseCore usage.' s.description = <<-DESC diff --git a/FirebaseCrashlytics.podspec b/FirebaseCrashlytics.podspec index 94bb7fc3499..2ee7d83bc48 100644 --- a/FirebaseCrashlytics.podspec +++ b/FirebaseCrashlytics.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseCrashlytics' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Best and lightest-weight crash reporting for mobile, desktop and tvOS.' s.description = 'Firebase Crashlytics helps you track, prioritize, and fix stability issues that erode app quality.' s.homepage = 'https://firebase.google.com/' diff --git a/FirebaseDatabase.podspec b/FirebaseDatabase.podspec index 85034b98650..5c100d42ea5 100644 --- a/FirebaseDatabase.podspec +++ b/FirebaseDatabase.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDatabase' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Realtime Database' s.description = <<-DESC diff --git a/FirebaseDynamicLinks.podspec b/FirebaseDynamicLinks.podspec index 89ad46e4baa..52cbd28c3db 100644 --- a/FirebaseDynamicLinks.podspec +++ b/FirebaseDynamicLinks.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseDynamicLinks' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Dynamic Links' s.description = <<-DESC diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index 782a4341b2b..cd4a074af2b 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestore' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Google Cloud Firestore' s.description = <<-DESC Google Cloud Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development. diff --git a/FirebaseFirestoreInternal.podspec b/FirebaseFirestoreInternal.podspec index 98e5d8e055e..3c5d120a18f 100644 --- a/FirebaseFirestoreInternal.podspec +++ b/FirebaseFirestoreInternal.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFirestoreInternal' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Google Cloud Firestore' s.description = <<-DESC diff --git a/FirebaseFunctions.podspec b/FirebaseFunctions.podspec index ac6be385709..0da10261b20 100644 --- a/FirebaseFunctions.podspec +++ b/FirebaseFunctions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseFunctions' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Cloud Functions for Firebase' s.description = <<-DESC diff --git a/FirebaseInAppMessaging.podspec b/FirebaseInAppMessaging.podspec index ac29ca4a8d3..83f693142c1 100644 --- a/FirebaseInAppMessaging.podspec +++ b/FirebaseInAppMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInAppMessaging' - s.version = '11.2.0-beta' + s.version = '11.1.0-beta' s.summary = 'Firebase In-App Messaging for iOS' s.description = <<-DESC diff --git a/FirebaseInstallations.podspec b/FirebaseInstallations.podspec index 39e856a0370..259f6c6a074 100644 --- a/FirebaseInstallations.podspec +++ b/FirebaseInstallations.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseInstallations' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Installations' s.description = <<-DESC diff --git a/FirebaseMLModelDownloader.podspec b/FirebaseMLModelDownloader.podspec index f58d79a3322..08cf186069a 100644 --- a/FirebaseMLModelDownloader.podspec +++ b/FirebaseMLModelDownloader.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMLModelDownloader' - s.version = '11.2.0-beta' + s.version = '11.1.0-beta' s.summary = 'Firebase ML Model Downloader' s.description = <<-DESC diff --git a/FirebaseMessaging.podspec b/FirebaseMessaging.podspec index 7da5f1d61ca..56f08c7a157 100644 --- a/FirebaseMessaging.podspec +++ b/FirebaseMessaging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMessaging' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Messaging' s.description = <<-DESC diff --git a/FirebaseMessagingInterop.podspec b/FirebaseMessagingInterop.podspec index 5f7017b1cab..e1085c97b65 100644 --- a/FirebaseMessagingInterop.podspec +++ b/FirebaseMessagingInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseMessagingInterop' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Interfaces that allow other Firebase SDKs to use Messaging functionality.' s.description = <<-DESC diff --git a/FirebasePerformance.podspec b/FirebasePerformance.podspec index 68060017351..2e9832eeeeb 100644 --- a/FirebasePerformance.podspec +++ b/FirebasePerformance.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebasePerformance' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Performance' s.description = <<-DESC diff --git a/FirebaseRemoteConfig.podspec b/FirebaseRemoteConfig.podspec index fcd404fa48f..11bba03374b 100644 --- a/FirebaseRemoteConfig.podspec +++ b/FirebaseRemoteConfig.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseRemoteConfig' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Remote Config' s.description = <<-DESC diff --git a/FirebaseRemoteConfigInterop.podspec b/FirebaseRemoteConfigInterop.podspec index 7d6e2870829..790b652039a 100644 --- a/FirebaseRemoteConfigInterop.podspec +++ b/FirebaseRemoteConfigInterop.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseRemoteConfigInterop' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Interfaces that allow other Firebase SDKs to use Remote Config functionality.' s.description = <<-DESC diff --git a/FirebaseSessions.podspec b/FirebaseSessions.podspec index 0bb3c1b096c..e0b84c6826a 100644 --- a/FirebaseSessions.podspec +++ b/FirebaseSessions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseSessions' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Sessions' s.description = <<-DESC diff --git a/FirebaseStorage.podspec b/FirebaseStorage.podspec index 9fcf1641751..129bfafae40 100644 --- a/FirebaseStorage.podspec +++ b/FirebaseStorage.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseStorage' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Firebase Storage' s.description = <<-DESC diff --git a/GoogleAppMeasurement.podspec b/GoogleAppMeasurement.podspec index f12fe582f44..c06a9437e13 100644 --- a/GoogleAppMeasurement.podspec +++ b/GoogleAppMeasurement.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleAppMeasurement' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Shared measurement methods for Google libraries. Not intended for direct use.' s.description = <<-DESC @@ -37,7 +37,7 @@ Pod::Spec.new do |s| s.default_subspecs = 'AdIdSupport' s.subspec 'AdIdSupport' do |ss| - ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.2.0' + ss.dependency 'GoogleAppMeasurement/WithoutAdIdSupport', '11.1.0' ss.vendored_frameworks = 'Frameworks/GoogleAppMeasurementIdentitySupport.xcframework' end diff --git a/GoogleAppMeasurementOnDeviceConversion.podspec b/GoogleAppMeasurementOnDeviceConversion.podspec index e9c8fe8e599..39f77038f7b 100644 --- a/GoogleAppMeasurementOnDeviceConversion.podspec +++ b/GoogleAppMeasurementOnDeviceConversion.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'GoogleAppMeasurementOnDeviceConversion' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = <<-SUMMARY On device conversion measurement plugin for Google App Measurement. Not intended for direct use. diff --git a/Package.swift b/Package.swift index f7c6a12a846..8647898aba5 100644 --- a/Package.swift +++ b/Package.swift @@ -19,7 +19,7 @@ import class Foundation.ProcessInfo import PackageDescription -let firebaseVersion = "11.2.0" +let firebaseVersion = "11.1.0" let package = Package( name: "Firebase", diff --git a/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift b/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift index 805eaa6045d..62bb423a5af 100755 --- a/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift +++ b/ReleaseTooling/Sources/FirebaseManifest/FirebaseManifest.swift @@ -21,7 +21,7 @@ import Foundation /// The version and releasing fields of the non-Firebase pods should be reviewed every release. /// The array should be ordered so that any pod's dependencies precede it in the list. public let shared = Manifest( - version: "11.2.0", + version: "11.1.0", pods: [ Pod("FirebaseSharedSwift"), Pod("FirebaseCoreInternal"), From f9bcce40241e4174dbb2a7c2a22f6e81c57baac5 Mon Sep 17 00:00:00 2001 From: pragatimodi <110490169+pragatimodi@users.noreply.github.com> Date: Tue, 20 Aug 2024 23:03:48 +0000 Subject: [PATCH 21/43] undo pod changes --- FirebaseFirestore.podspec | 2 +- FirebaseSharedSwift.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FirebaseFirestore.podspec b/FirebaseFirestore.podspec index cd4a074af2b..5673c02c75a 100644 --- a/FirebaseFirestore.podspec +++ b/FirebaseFirestore.podspec @@ -37,7 +37,7 @@ Google Cloud Firestore is a NoSQL document database built for automatic scaling, s.dependency 'FirebaseCore', '~> 11.0' s.dependency 'FirebaseCoreExtension', '~> 11.0' - s.dependency 'FirebaseFirestoreInternal', '11.2.0' + s.dependency 'FirebaseFirestoreInternal', '11.1.0' s.dependency 'FirebaseSharedSwift', '~> 11.0' end diff --git a/FirebaseSharedSwift.podspec b/FirebaseSharedSwift.podspec index 786ca4b7a32..f6536cd29fa 100644 --- a/FirebaseSharedSwift.podspec +++ b/FirebaseSharedSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'FirebaseSharedSwift' - s.version = '11.2.0' + s.version = '11.1.0' s.summary = 'Shared Swift Extensions for Firebase' s.description = <<-DESC From 4c9df9694f99d6e3837c52d17851b1567a17663f Mon Sep 17 00:00:00 2001 From: Pragati Date: Wed, 21 Aug 2024 14:50:02 -0700 Subject: [PATCH 22/43] lint fixes --- .../AuthProvider/PhoneAuthProvider.swift | 239 ++++++++++-------- .../Utilities/AuthRecaptchaVerifier.swift | 24 +- .../project.pbxproj | 42 ++- .../SwiftApplication.plist | 4 +- .../PhoneAuthViewController.swift | 2 +- 5 files changed, 176 insertions(+), 135 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index abf4fcf84f4..9f551a0fec7 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -107,26 +107,30 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession? = nil) async throws -> String { - guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, - urlTypes: auth.mainBundleUrlTypes) else { - fatalError( - "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." - ) - } - do { + guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, + urlTypes: auth.mainBundleUrlTypes) else { + fatalError( + "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." + ) + } + do { // let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) // let enablementStatus = recaptchaVerifier.enablementStatus(forProvider: .phone) - // regular phone number login - if let verificationID = try await internalVerify(phoneNumber: phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) { - return verificationID - } else { - throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") - } - } catch { - throw error + // regular phone number login + if let verificationID = try await internalVerify( + phoneNumber: phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession + ) { + return verificationID + } else { + throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") } + } catch { + throw error + } } - + // @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) // open func verifyPhoneNumberWithRecaptcha(_ phoneNumber: String, // uiDelegate: AuthUIDelegate? = nil, @@ -139,7 +143,7 @@ import Foundation // ) // } // do { -// +// // } catch { // throw error // } @@ -206,58 +210,73 @@ import Foundation private func internalVerify(phoneNumber: String, uiDelegate: AuthUIDelegate?, multiFactorSession: MultiFactorSession? = nil) async throws - -> String? { - guard phoneNumber.count > 0 else { - throw AuthErrorUtils.missingPhoneNumberError(message: nil) - } - guard let manager = auth.notificationManager else { - throw AuthErrorUtils.notificationNotForwardedError() - } - guard await manager.checkNotificationForwarding() else { - throw AuthErrorUtils.notificationNotForwardedError() - } - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) - let enablement = recaptchaVerifier.enablementStatus(forProvider: .phone) - if(enablement == .off) { - return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, - retryOnInvalidAppCredential: true, - multiFactorSession: multiFactorSession, - uiDelegate: uiDelegate) - } else { - do { - if(enablement == .audit) { - return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) - } else { - return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: false, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + -> String? { + guard phoneNumber.count > 0 else { + throw AuthErrorUtils.missingPhoneNumberError(message: nil) + } + guard let manager = auth.notificationManager else { + throw AuthErrorUtils.notificationNotForwardedError() + } + guard await manager.checkNotificationForwarding() else { + throw AuthErrorUtils.notificationNotForwardedError() + } + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) + let enablement = recaptchaVerifier.enablementStatus(forProvider: .phone) + if enablement == .off { + return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: true, + multiFactorSession: multiFactorSession, + uiDelegate: uiDelegate) + } else { + do { + if enablement == .audit { + return try await verifyClAndSendVerificationCodeWithRecaptcha( + toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: true, + multiFactorSession: multiFactorSession, + uiDelegate: uiDelegate + ) + } else { + return try await verifyClAndSendVerificationCodeWithRecaptcha( + toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: false, + multiFactorSession: multiFactorSession, + uiDelegate: uiDelegate + ) + } + } catch { + print("ERROR: ", error) + throw error } - } catch { - throw error } } - } - private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, - retryOnInvalidAppCredential: Bool, - uiDelegate: AuthUIDelegate?) async throws - -> String? { - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, - codeIdentity: CodeIdentity.empty, - requestConfiguration: auth - .requestConfiguration) - do { - try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) - let response = try await AuthBackend.call(with: request) - return response.verificationID - } catch { - return try await handleVerifyErrorWithRetry(error: error, - phoneNumber: phoneNumber, - retryOnInvalidAppCredential: retryOnInvalidAppCredential, - multiFactorSession: nil, - uiDelegate: uiDelegate) + private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, + retryOnInvalidAppCredential: Bool, + uiDelegate: AuthUIDelegate?) async throws + -> String? { + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, + codeIdentity: CodeIdentity.empty, + requestConfiguration: auth + .requestConfiguration) + do { + try await recaptchaVerifier.injectRecaptchaFields( + request: request, + provider: .phone, + action: .sendVerificationCode + ) + let response = try await AuthBackend.call(with: request) + return response.verificationID + } catch { + return try await handleVerifyErrorWithRetry(error: error, + phoneNumber: phoneNumber, + retryOnInvalidAppCredential: retryOnInvalidAppCredential, + multiFactorSession: nil, + uiDelegate: uiDelegate) + } } - } /// Starts the flow to verify the client via silent push notification. /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an @@ -290,11 +309,11 @@ import Foundation /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an /// AuthErrorCodeInvalidAppCredential error is returned from the backend. /// - Parameter phoneNumber: The phone number to be verified. - private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, - retryOnInvalidAppCredential: Bool, - multiFactorSession session: MultiFactorSession?, - uiDelegate: AuthUIDelegate?) async throws - -> String? { + private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, + retryOnInvalidAppCredential: Bool, + multiFactorSession session: MultiFactorSession?, + uiDelegate: AuthUIDelegate?) async throws + -> String? { // if let settings = auth.settings, // settings.isAppVerificationDisabledForTesting { // let request = SendVerificationCodeRequest( @@ -305,43 +324,52 @@ import Foundation // let response = try await AuthBackend.call(with: request) // return response.verificationID // } - guard let session else { - return try await verifyClAndSendVerificationCodeWithRecaptcha( - toPhoneNumber: phoneNumber, - retryOnInvalidAppCredential: retryOnInvalidAppCredential, - uiDelegate: uiDelegate - ) - } - let startMFARequestInfo = AuthProtoStartMFAPhoneRequestInfo(phoneNumber: phoneNumber, - codeIdentity: CodeIdentity.empty) - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - do { - if let idToken = session.idToken { - let request = StartMFAEnrollmentRequest(idToken: idToken, - enrollmentInfo: startMFARequestInfo, - requestConfiguration: auth.requestConfiguration) - try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .startMfaEnrollment) - let response = try await AuthBackend.call(with: request) - return response.phoneSessionInfo?.sessionInfo - } else { - let request = StartMFASignInRequest(MFAPendingCredential: session.mfaPendingCredential, - MFAEnrollmentID: session.multiFactorInfo?.uid, - signInInfo: startMFARequestInfo, - requestConfiguration: auth.requestConfiguration) - try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .startMfaSignin) - let response = try await AuthBackend.call(with: request) - return response.responseInfo?.sessionInfo + guard let session else { + return try await verifyClAndSendVerificationCodeWithRecaptcha( + toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: retryOnInvalidAppCredential, + uiDelegate: uiDelegate + ) + } + let startMFARequestInfo = AuthProtoStartMFAPhoneRequestInfo(phoneNumber: phoneNumber, + codeIdentity: CodeIdentity.empty) + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + do { + if let idToken = session.idToken { + let request = StartMFAEnrollmentRequest(idToken: idToken, + enrollmentInfo: startMFARequestInfo, + requestConfiguration: auth.requestConfiguration) + try await recaptchaVerifier.injectRecaptchaFields( + request: request, + provider: .phone, + action: .startMfaEnrollment + ) + let response = try await AuthBackend.call(with: request) + return response.phoneSessionInfo?.sessionInfo + } else { + let request = StartMFASignInRequest(MFAPendingCredential: session.mfaPendingCredential, + MFAEnrollmentID: session.multiFactorInfo?.uid, + signInInfo: startMFARequestInfo, + requestConfiguration: auth.requestConfiguration) + try await recaptchaVerifier.injectRecaptchaFields( + request: request, + provider: .phone, + action: .startMfaSignin + ) + let response = try await AuthBackend.call(with: request) + return response.responseInfo?.sessionInfo + } + } catch { + print("Error= ", error) + return try await handleVerifyErrorWithRetry( + error: error, + phoneNumber: phoneNumber, + retryOnInvalidAppCredential: retryOnInvalidAppCredential, + multiFactorSession: session, + uiDelegate: uiDelegate + ) } - } catch { - return try await handleVerifyErrorWithRetry( - error: error, - phoneNumber: phoneNumber, - retryOnInvalidAppCredential: retryOnInvalidAppCredential, - multiFactorSession: session, - uiDelegate: uiDelegate - ) } - } /// Starts the flow to verify the client via silent push notification. /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an @@ -362,7 +390,8 @@ import Foundation // let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) // let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) // if(enforcement != .off) { -// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, action: .sendVerificationCode) +// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, +// action: .sendVerificationCode) // } let response = try await AuthBackend.call(with: request) return response.verificationID diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 2d6142c3527..e69426a23e0 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -33,15 +33,6 @@ } } - enum AuthRecaptchaEnablementStatus: String, CaseIterable { - case enforce = "ENFORCE" - case audit = "AUDIT" - case off = "OFF" - - // Convenience property for mapping values - var stringValue: String { self.rawValue } -} - enum AuthRecaptchaEnablementStatus: String, CaseIterable { case enforce = "ENFORCE" case audit = "AUDIT" @@ -176,12 +167,6 @@ let request = GetRecaptchaConfigRequest(requestConfiguration: requestConfiguration) let response = try await AuthBackend.call(with: request) AuthLog.logInfo(code: "I-AUT000029", message: "reCAPTCHA config retrieval succeeded.") - // Response's site key is of the format projects//keys/' - guard let keys = response.recaptchaKey?.components(separatedBy: "/"), - keys.count == 4 else { - throw AuthErrorUtils.error(code: .recaptchaNotEnabled, message: "Invalid siteKey") - } - let siteKey = keys[3] var enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] = [:] if let enforcementState = response.enforcementState { for state in enforcementState { @@ -194,6 +179,15 @@ enablementStatus[provider] = enforcement } } + var siteKey = "" + // Response's site key is of the format projects//keys/' + if let recaptchaKey = response.recaptchaKey { + let keys = recaptchaKey.components(separatedBy: "/") + if keys.count != 4 { + throw AuthErrorUtils.error(code: .recaptchaNotEnabled, message: "Invalid siteKey") + } + siteKey = keys[3] + } let config = AuthRecaptchaConfig(siteKey: siteKey, enablementStatus: enablementStatus) if let tenantID = auth?.tenantID { diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj index edee157b901..9edf2a88402 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj @@ -7,7 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 8848765129D314A400780FA6 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8848764F29D3149200780FA6 /* GoogleService-Info.plist */; }; + 241240D62C755F9100EF64BD /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 241240D42C755F9100EF64BD /* GoogleService-Info.plist */; }; + 241240D72C755F9100EF64BD /* GoogleService-Info_multi.plist in Resources */ = {isa = PBXBuildFile; fileRef = 241240D52C755F9100EF64BD /* GoogleService-Info_multi.plist */; }; + 241240DA2C755FEB00EF64BD /* RecaptchaEnterprise in Frameworks */ = {isa = PBXBuildFile; productRef = 241240D92C755FEB00EF64BD /* RecaptchaEnterprise */; }; DE8B636F2BEC2DC300607B82 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = DE8B636E2BEC2DC300607B82 /* FirebaseAuth */; }; DE8B63722BEC2FB900607B82 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = DE8B63712BEC2FB900607B82 /* GoogleSignIn */; }; DE8B63742BEC2FB900607B82 /* GoogleSignInSwift in Frameworks */ = {isa = PBXBuildFile; productRef = DE8B63732BEC2FB900607B82 /* GoogleSignInSwift */; }; @@ -31,9 +33,8 @@ DE8FD4942A7D9E2700B6831A /* PhoneMultiFactorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8FD4912A7D9D9E00B6831A /* PhoneMultiFactorTests.swift */; }; DEC2E5DD2A95331E0090260A /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */; }; DEC2E5DF2A9583CA0090260A /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DE2A9583CA0090260A /* AppManager.swift */; }; - DEC2E5E42A966DE20090260A /* GoogleService-Info_multi.plist in Resources */ = {isa = PBXBuildFile; fileRef = DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */; }; DED37F632AB0C4F7003A67E4 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */; }; - DEE261C52C21E9F500EECAC5 /* RecaptchaEnterprise in Frameworks */ = {isa = PBXBuildFile; productRef = DEE261C42C21E9F500EECAC5 /* RecaptchaEnterprise */; }; + DEE261C52C21E9F500EECAC5 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; EA02F68524A000E00079D000 /* UserActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68424A000E00079D000 /* UserActions.swift */; }; EA02F68D24A063E90079D000 /* LoginDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68C24A063E90079D000 /* LoginDelegate.swift */; }; EA062D5D24A0FEB6006714D3 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = EA062D5C24A0FEB6006714D3 /* README.md */; }; @@ -87,7 +88,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 8848764F29D3149200780FA6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; }; + 241240D42C755F9100EF64BD /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../../../../Downloads/GoogleService-Info.plist"; sourceTree = ""; }; + 241240D52C755F9100EF64BD /* GoogleService-Info_multi.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info_multi.plist"; path = "../../../../../../../Downloads/GoogleService-Info_multi.plist"; sourceTree = ""; }; DE4D8E1F2A8B0311001952B6 /* SwiftApplication.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SwiftApplication.plist; sourceTree = ""; }; DE8FD4682A7D660A00B6831A /* Credentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = ""; }; DE8FD4692A7D660A00B6831A /* AccountInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInfoTests.swift; sourceTree = ""; }; @@ -109,7 +111,6 @@ DE8FD4972A7DB00600B6831A /* AuthCredentials.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthCredentials.h; sourceTree = ""; }; DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; DEC2E5DE2A9583CA0090260A /* AppManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = ""; }; - DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info_multi.plist"; sourceTree = SOURCE_ROOT; }; DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsUITests.swift; sourceTree = ""; }; EA02F68424A000E00079D000 /* UserActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActions.swift; sourceTree = ""; }; EA02F68C24A063E90079D000 /* LoginDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDelegate.swift; sourceTree = ""; }; @@ -157,11 +158,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DEE261C52C21E9F500EECAC5 /* RecaptchaEnterprise in Frameworks */, + DEE261C52C21E9F500EECAC5 /* (null) in Frameworks */, DE8B63772BEC302200607B82 /* FacebookLogin in Frameworks */, DE8B63742BEC2FB900607B82 /* GoogleSignInSwift in Frameworks */, DE8B636F2BEC2DC300607B82 /* FirebaseAuth in Frameworks */, DE8B63722BEC2FB900607B82 /* GoogleSignIn in Frameworks */, + 241240DA2C755FEB00EF64BD /* RecaptchaEnterprise in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -305,7 +307,8 @@ EAE4CBC324855E3A00245E92 /* AuthenticationExample */ = { isa = PBXGroup; children = ( - DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */, + 241240D52C755F9100EF64BD /* GoogleService-Info_multi.plist */, + 241240D42C755F9100EF64BD /* GoogleService-Info.plist */, DE4D8E1F2A8B0311001952B6 /* SwiftApplication.plist */, EA062D5C24A0FEB6006714D3 /* README.md */, EA20B506249CA63300B5E581 /* AuthenticationExample.entitlements */, @@ -316,7 +319,6 @@ EA20B47724973BB100B5E581 /* CustomViews */, EAB3A17A2494626F00385291 /* Utility */, EAE4CBCD24855E3D00245E92 /* Assets.xcassets */, - 8848764F29D3149200780FA6 /* GoogleService-Info.plist */, EA217894248979E200736757 /* LaunchScreen.storyboard */, DEC2E5DE2A9583CA0090260A /* AppManager.swift */, ); @@ -390,7 +392,7 @@ DE8B63712BEC2FB900607B82 /* GoogleSignIn */, DE8B63732BEC2FB900607B82 /* GoogleSignInSwift */, DE8B63762BEC302200607B82 /* FacebookLogin */, - DEE261C42C21E9F500EECAC5 /* RecaptchaEnterprise */, + 241240D92C755FEB00EF64BD /* RecaptchaEnterprise */, ); productName = "Swifty Auth"; productReference = EAE4CBC124855E3A00245E92 /* AuthenticationExample.app */; @@ -478,6 +480,7 @@ DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, DE8B63752BEC302200607B82 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */, DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */, + 241240D82C755FD900EF64BD /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */, ); productRefGroup = EAE4CBC224855E3A00245E92 /* Products */; projectDirPath = ""; @@ -504,8 +507,8 @@ buildActionMask = 2147483647; files = ( EA217895248979E200736757 /* LaunchScreen.storyboard in Resources */, - 8848765129D314A400780FA6 /* GoogleService-Info.plist in Resources */, - DEC2E5E42A966DE20090260A /* GoogleService-Info_multi.plist in Resources */, + 241240D62C755F9100EF64BD /* GoogleService-Info.plist in Resources */, + 241240D72C755F9100EF64BD /* GoogleService-Info_multi.plist in Resources */, EAE4CBCE24855E3D00245E92 /* Assets.xcassets in Resources */, EA062D5D24A0FEB6006714D3 /* README.md in Resources */, ); @@ -794,7 +797,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -817,7 +820,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1.dev; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -974,6 +977,14 @@ /* End XCLocalSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */ + 241240D82C755FD900EF64BD /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 18.5.1; + }; + }; DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/google/GoogleSignIn-iOS.git"; @@ -1001,6 +1012,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 241240D92C755FEB00EF64BD /* RecaptchaEnterprise */ = { + isa = XCSwiftPackageProductDependency; + package = 241240D82C755FD900EF64BD /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */; + productName = RecaptchaEnterprise; + }; DE8B636E2BEC2DC300607B82 /* FirebaseAuth */ = { isa = XCSwiftPackageProductDependency; productName = FirebaseAuth; diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist index 50378fe58b0..b3e4a0c579f 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist @@ -24,7 +24,9 @@ CFBundleURLName CFBundleURLSchemes - + + app-1-585304629422-ios-cb0e5761816443a9d98d3f + CFBundleVersion diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift index 5c51425beb8..cbb86430685 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/OtherAuthMethodControllers/PhoneAuthViewController.swift @@ -27,7 +27,7 @@ class PhoneAuthViewController: OtherAuthViewController { } // MARK: - Firebase 🔥 - + private func phoneAuthLogin(_ phoneNumber: String) { let phoneNumber = String(format: "+%@", phoneNumber) Task { From 30ed91a4695f0fdb37c9f22593f394a10efcab3f Mon Sep 17 00:00:00 2001 From: Pragati Date: Mon, 26 Aug 2024 07:10:52 -0700 Subject: [PATCH 23/43] recaptcha auth flow clean --- .../AuthProvider/PhoneAuthProvider.swift | 185 ++++++----------- .../Utilities/AuthRecaptchaVerifier.swift | 24 ++- .../Tests/Unit/FIROAuthProviderTests.m | 193 ------------------ .../Unit/Fakes/FakeBackendRPCIssuer.swift | 33 ++- 4 files changed, 108 insertions(+), 327 deletions(-) delete mode 100644 FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 9f551a0fec7..7aed0d5106c 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -102,52 +102,25 @@ import Foundation /// identifies the user trying to enroll. For sign-in, this identifies that the user already /// passed the first factor challenge. /// - Returns: The verification ID - @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) - open func verifyPhoneNumber(_ phoneNumber: String, - uiDelegate: AuthUIDelegate? = nil, - multiFactorSession: MultiFactorSession? = nil) async throws - -> String { - guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, - urlTypes: auth.mainBundleUrlTypes) else { - fatalError( - "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." - ) - } - do { -// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) -// let enablementStatus = recaptchaVerifier.enablementStatus(forProvider: .phone) - // regular phone number login - if let verificationID = try await internalVerify( - phoneNumber: phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession - ) { - return verificationID - } else { - throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") - } - } catch { - throw error - } + @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) + open func verifyPhoneNumber(_ phoneNumber: String, + uiDelegate: AuthUIDelegate? = nil, + multiFactorSession: MultiFactorSession? = nil) async throws -> String { + guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, + urlTypes: auth.mainBundleUrlTypes) else { + fatalError( + "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." + ) } - -// @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) -// open func verifyPhoneNumberWithRecaptcha(_ phoneNumber: String, -// uiDelegate: AuthUIDelegate? = nil, -// multiFactorSession: MultiFactorSession? = nil) async throws -// -> String { -// guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, -// urlTypes: auth.mainBundleUrlTypes) else { -// fatalError( -// "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." -// ) -// } -// do { -// -// } catch { -// throw error -// } -// } + + if let verificationID = try await internalVerify(phoneNumber: phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) { + return verificationID + } else { + throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") + } + } /// Verify ownership of the second factor phone number by the current user. /// - Parameter multiFactorInfo: The phone multi factor whose number need to be verified. @@ -177,19 +150,15 @@ import Foundation /// identifies the user trying to enroll. For sign-in, this identifies that the user already /// passed the first factor challenge. /// - Returns: The verification ID. - @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) - open func verifyPhoneNumber(with multiFactorInfo: PhoneMultiFactorInfo, - uiDelegate: AuthUIDelegate? = nil, - multiFactorSession: MultiFactorSession?) async throws -> String { - do { - multiFactorSession?.multiFactorInfo = multiFactorInfo - return try await verifyPhoneNumber(multiFactorInfo.phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) - } catch { - throw error - } - } + @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) + open func verifyPhoneNumber(with multiFactorInfo: PhoneMultiFactorInfo, + uiDelegate: AuthUIDelegate? = nil, + multiFactorSession: MultiFactorSession?) async throws -> String { + multiFactorSession?.multiFactorInfo = multiFactorInfo + return try await verifyPhoneNumber(multiFactorInfo.phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) + } /// Creates an `AuthCredential` for the phone number provider identified by the /// verification ID and verification code. @@ -207,50 +176,31 @@ import Foundation verificationCode: verificationCode) } - private func internalVerify(phoneNumber: String, - uiDelegate: AuthUIDelegate?, - multiFactorSession: MultiFactorSession? = nil) async throws - -> String? { - guard phoneNumber.count > 0 else { - throw AuthErrorUtils.missingPhoneNumberError(message: nil) - } - guard let manager = auth.notificationManager else { - throw AuthErrorUtils.notificationNotForwardedError() - } - guard await manager.checkNotificationForwarding() else { - throw AuthErrorUtils.notificationNotForwardedError() - } - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) - let enablement = recaptchaVerifier.enablementStatus(forProvider: .phone) - if enablement == .off { - return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, - retryOnInvalidAppCredential: true, - multiFactorSession: multiFactorSession, - uiDelegate: uiDelegate) - } else { - do { - if enablement == .audit { - return try await verifyClAndSendVerificationCodeWithRecaptcha( - toPhoneNumber: phoneNumber, - retryOnInvalidAppCredential: true, - multiFactorSession: multiFactorSession, - uiDelegate: uiDelegate - ) - } else { - return try await verifyClAndSendVerificationCodeWithRecaptcha( - toPhoneNumber: phoneNumber, - retryOnInvalidAppCredential: false, - multiFactorSession: multiFactorSession, - uiDelegate: uiDelegate - ) - } - } catch { - print("ERROR: ", error) - throw error - } - } + private func internalVerify(phoneNumber: String, + uiDelegate: AuthUIDelegate?, + multiFactorSession: MultiFactorSession? = nil) async throws -> String? { + guard !phoneNumber.isEmpty else { + throw AuthErrorUtils.missingPhoneNumberError(message: nil) + } + guard let manager = auth.notificationManager else { + throw AuthErrorUtils.notificationNotForwardedError() } + guard await manager.checkNotificationForwarding() else { + throw AuthErrorUtils.notificationNotForwardedError() + } + + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) + + switch recaptchaVerifier.enablementStatus(forProvider: .phone) { + case .off: + return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession,uiDelegate: uiDelegate) + case .audit: + return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + case .enforce: + return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + } + } private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, @@ -297,11 +247,7 @@ import Foundation let response = try await AuthBackend.call(with: request) return response.verificationID } catch { - return try await handleVerifyErrorWithRetry(error: error, - phoneNumber: phoneNumber, - retryOnInvalidAppCredential: retryOnInvalidAppCredential, - multiFactorSession: nil, - uiDelegate: uiDelegate) + return try await handleVerifyErrorWithRetry(error: error,phoneNumber: phoneNumber,retryOnInvalidAppCredential:retryOnInvalidAppCredential,multiFactorSession: nil, uiDelegate: uiDelegate) } } @@ -314,16 +260,16 @@ import Foundation multiFactorSession session: MultiFactorSession?, uiDelegate: AuthUIDelegate?) async throws -> String? { -// if let settings = auth.settings, -// settings.isAppVerificationDisabledForTesting { -// let request = SendVerificationCodeRequest( -// phoneNumber: phoneNumber, -// codeIdentity: CodeIdentity.empty, -// requestConfiguration: auth.requestConfiguration -// ) -// let response = try await AuthBackend.call(with: request) -// return response.verificationID -// } + if let settings = auth.settings, + settings.isAppVerificationDisabledForTesting { + let request = SendVerificationCodeRequest( + phoneNumber: phoneNumber, + codeIdentity: CodeIdentity.empty, + requestConfiguration: auth.requestConfiguration + ) + let response = try await AuthBackend.call(with: request) + return response.verificationID + } guard let session else { return try await verifyClAndSendVerificationCodeWithRecaptcha( toPhoneNumber: phoneNumber, @@ -360,7 +306,6 @@ import Foundation return response.responseInfo?.sessionInfo } } catch { - print("Error= ", error) return try await handleVerifyErrorWithRetry( error: error, phoneNumber: phoneNumber, @@ -387,12 +332,6 @@ import Foundation codeIdentity: CodeIdentity.empty, requestConfiguration: auth.requestConfiguration ) -// let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) -// let enforcement = recaptchaVerifier.enablementStatus(forProvider: .phone) -// if(enforcement != .off) { -// try await recaptchaVerifier.injectRecaptchaFields(request: request, provider: .phone, -// action: .sendVerificationCode) -// } let response = try await AuthBackend.call(with: request) return response.verificationID } diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index e69426a23e0..1682894230a 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -23,10 +23,10 @@ @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) class AuthRecaptchaConfig { - let siteKey: String + var siteKey: String? let enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] - init(siteKey: String, + init(siteKey: String? = nil, enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus]) { self.siteKey = siteKey self.enablementStatus = enablementStatus @@ -167,7 +167,12 @@ let request = GetRecaptchaConfigRequest(requestConfiguration: requestConfiguration) let response = try await AuthBackend.call(with: request) AuthLog.logInfo(code: "I-AUT000029", message: "reCAPTCHA config retrieval succeeded.") + try await parseRecaptchaConfigFromResponse(response: response) + } + + func parseRecaptchaConfigFromResponse(response: GetRecaptchaConfigResponse) async throws { var enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] = [:] + var isRecaptchaEnabled: Bool = false if let enforcementState = response.enforcementState { for state in enforcementState { guard let providerString = state["provider"] as? String, @@ -177,16 +182,21 @@ continue // Skip to the next state in the loop } enablementStatus[provider] = enforcement + if enforcement != .off { + isRecaptchaEnabled = true + } } } var siteKey = "" // Response's site key is of the format projects//keys/' - if let recaptchaKey = response.recaptchaKey { - let keys = recaptchaKey.components(separatedBy: "/") - if keys.count != 4 { - throw AuthErrorUtils.error(code: .recaptchaNotEnabled, message: "Invalid siteKey") + if isRecaptchaEnabled { + if let recaptchaKey = response.recaptchaKey { + let keys = recaptchaKey.components(separatedBy: "/") + if keys.count != 4 { + throw AuthErrorUtils.error(code: .recaptchaNotEnabled, message: "Invalid siteKey") + } + siteKey = keys[3] } - siteKey = keys[3] } let config = AuthRecaptchaConfig(siteKey: siteKey, enablementStatus: enablementStatus) diff --git a/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m b/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m deleted file mode 100644 index b5921312caa..00000000000 --- a/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2017 Google - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import -#if TARGET_OS_IOS - -#import - -@import FirebaseAuth; -@import FirebaseCore; - -/** @var kExpectationTimeout - @brief The maximum time waiting for expectations to fulfill. - */ -static const NSTimeInterval kExpectationTimeout = 1; - -/** @var kFakeAuthorizedDomain - @brief A fake authorized domain for the app. - */ -static NSString *const kFakeAuthorizedDomain = @"test.firebaseapp.com"; - -/** @var kFakeBundleID - @brief A fake bundle ID. - */ -static NSString *const kFakeBundleID = @"com.firebaseapp.example"; - -/** @var kFakeAccessToken - @brief A fake access token for testing. - */ -static NSString *const kFakeAccessToken = @"fakeAccessToken"; - -/** @var kFakeIDToken - @brief A fake ID token for testing. - */ -static NSString *const kFakeIDToken = @"fakeIDToken"; - -/** @var kFakeProviderID - @brief A fake provider ID for testing. - */ -static NSString *const kFakeProviderID = @"fakeProviderID"; - -/** @var kFakeGivenName - @brief A fake given name for testing. - */ -static NSString *const kFakeGivenName = @"fakeGivenName"; - -/** @var kFakeFamilyName - @brief A fake family name for testing. - */ -static NSString *const kFakeFamilyName = @"fakeFamilyName"; - -/** @var kFakeAPIKey - @brief A fake API key. - */ -static NSString *const kFakeAPIKey = @"asdfghjkl"; - -/** @var kFakeEmulatorHost - @brief A fake emulator host. - */ -static NSString *const kFakeEmulatorHost = @"emulatorhost"; - -/** @var kFakeEmulatorPort - @brief A fake emulator port. - */ -static NSString *const kFakeEmulatorPort = @"12345"; - -/** @var kFakeClientID - @brief A fake client ID. - */ -static NSString *const kFakeClientID = @"123456.apps.googleusercontent.com"; - -/** @var kFakeReverseClientID - @brief The dot-reversed version of the fake client ID. - */ -static NSString *const kFakeReverseClientID = @"com.googleusercontent.apps.123456"; - -/** @var kFakeFirebaseAppID - @brief A fake Firebase app ID. - */ -static NSString *const kFakeFirebaseAppID = @"1:123456789:ios:123abc456def"; - -/** @var kFakeEncodedFirebaseAppID - @brief A fake encoded Firebase app ID to be used as a custom URL scheme. - */ -static NSString *const kFakeEncodedFirebaseAppID = @"app-1-123456789-ios-123abc456def"; - -/** @var kFakeTenantID - @brief A fake tenant ID. - */ -static NSString *const kFakeTenantID = @"tenantID"; - -/** @var kFakeOAuthResponseURL - @brief A fake OAuth response URL used in test. - */ -static NSString *const kFakeOAuthResponseURL = @"fakeOAuthResponseURL"; - -/** @var kFakeRedirectURLResponseURL - @brief A fake callback URL (minus the scheme) containing a fake response URL. - */ - -@interface FIROAuthProviderTests : XCTestCase - -@end - -@implementation FIROAuthProviderTests - -/** @fn testObtainingOAuthCredentialNoIDToken - @brief Tests the correct creation of an OAuthCredential without an IDToken. - */ -- (void)testObtainingOAuthCredentialNoIDToken { - FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:kFakeProviderID - accessToken:kFakeAccessToken]; - XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]); - FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; - XCTAssertEqualObjects(OAuthCredential.accessToken, kFakeAccessToken); - XCTAssertEqualObjects(OAuthCredential.provider, kFakeProviderID); - XCTAssertNil(OAuthCredential.IDToken); -} - -/** @fn testObtainingOAuthCredentialWithFullName - @brief Tests the correct creation of an OAuthCredential with a fullName. - */ -- (void)testObtainingOAuthCredentialWithFullName { - NSPersonNameComponents *fullName = [[NSPersonNameComponents alloc] init]; - fullName.givenName = kFakeGivenName; - fullName.familyName = kFakeFamilyName; - FIRAuthCredential *credential = [FIROAuthProvider appleCredentialWithIDToken:kFakeIDToken - rawNonce:nil - fullName:fullName]; - - XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]); - FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; - XCTAssertEqualObjects(OAuthCredential.provider, @"apple.com"); - XCTAssertEqualObjects(OAuthCredential.IDToken, kFakeIDToken); - XCTAssertNil(OAuthCredential.accessToken); -} - -/** @fn testObtainingOAuthCredentialWithIDToken - @brief Tests the correct creation of an OAuthCredential with an IDToken - */ -- (void)testObtainingOAuthCredentialWithIDToken { - FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:kFakeProviderID - IDToken:kFakeIDToken - accessToken:kFakeAccessToken]; - XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]); - FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; - XCTAssertEqualObjects(OAuthCredential.accessToken, kFakeAccessToken); - XCTAssertEqualObjects(OAuthCredential.provider, kFakeProviderID); - XCTAssertEqualObjects(OAuthCredential.IDToken, kFakeIDToken); -} - -/** @fn testGetCredentialWithUIDelegateWithClientIDOnMainThread - @brief Verifies @c getCredentialWithUIDelegate:completion: calls its completion handler on the - main thread. Regression test for firebase/FirebaseUI-iOS#1199. - */ -- (void)testGetCredentialWithUIDelegateWithClientIDOnMainThread { - XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; - - FIROptions *options = - [[FIROptions alloc] initWithGoogleAppID:@"0:0000000000000:ios:0000000000000000" - GCMSenderID:@"00000000000000000-00000000000-000000000"]; - options.APIKey = kFakeAPIKey; - options.projectID = @"myProjectID"; - options.clientID = kFakeClientID; - [FIRApp configureWithName:@"objAppName" options:options]; - FIRAuth *auth = [FIRAuth authWithApp:[FIRApp appNamed:@"objAppName"]]; - [auth setMainBundleUrlTypes:@[ @{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]} ]]; - - FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:auth]; - [provider getCredentialWithUIDelegate:nil - completion:^(FIRAuthCredential *_Nullable credential, - NSError *_Nullable error) { - XCTAssertTrue([NSThread isMainThread]); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; -} -@end - -#endif // TARGET_OS_IOS diff --git a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift index d51ccd255da..c4191fc926d 100644 --- a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift +++ b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift @@ -75,7 +75,8 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { var fakeSecureTokenServiceJSON: [String: AnyHashable]? var secureTokenNetworkError: NSError? var secureTokenErrorString: String? - var recaptchaSiteKey = "unset recaptcha siteKey" + var recaptchaSiteKey = "projects/fakeProjectId/keys/mockSiteKey" + var recaptchaEnabled: Bool = true func asyncCallToURL(with request: T, body: Data?, @@ -111,9 +112,33 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { } return } else if let _ = request as? GetRecaptchaConfigRequest { - guard let _ = try? respond(withJSON: ["recaptchaKey": recaptchaSiteKey]) - else { - fatalError("GetRecaptchaConfigRequest respond failed") + if recaptchaEnabled { // Check if reCAPTCHA is enabled + let recaptchaKey = recaptchaSiteKey // iOS key from your config + let enforcementState = [ + ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": "ENFORCE"], + ["provider": "PHONE_PROVIDER", "enforcementState": "ENFORCE"] + ] + guard let _ = try? respond(withJSON: [ + "recaptchaKey": recaptchaKey, + "recaptchaEnforcementState": enforcementState, + "managedRules": [ + ["endScore": 0.1, "action": "BLOCK"] + ], + "useSmsBotScore": true, + "useSmsTollFraudProtection": false + ]) else { + fatalError("GetRecaptchaConfigRequest respond failed") + } + } else { // reCAPTCHA OFF + let enforcementState = [ + ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": "OFF"], + ["provider": "PHONE_PROVIDER", "enforcementState": "OFF"] + ] + guard let _ = try? respond(withJSON: [ + "recaptchaEnforcementState": enforcementState, + ]) else { + fatalError("GetRecaptchaConfigRequest respond failed") + } } return } else if let _ = request as? SecureTokenRequest { From 9b75a9abeaf7e42a6393091bc0fa63aec9145731 Mon Sep 17 00:00:00 2001 From: Pragati Date: Mon, 26 Aug 2024 23:39:55 -0700 Subject: [PATCH 24/43] modify unit tests --- .../AuthProvider/PhoneAuthProvider.swift | 17 +- .../project.pbxproj | 30 +-- .../Unit/Fakes/FakeBackendRPCIssuer.swift | 13 +- .../Tests/Unit/PhoneAuthProviderTests.swift | 206 +++++++++++------- Package.swift | 2 +- 5 files changed, 158 insertions(+), 110 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 7aed0d5106c..698c3baed9b 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -196,17 +196,16 @@ import Foundation case .off: return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession,uiDelegate: uiDelegate) case .audit: - return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate, recaptchaVerifier: recaptchaVerifier) case .enforce: - return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate) + return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate, recaptchaVerifier: recaptchaVerifier) } } private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, - uiDelegate: AuthUIDelegate?) async throws + uiDelegate: AuthUIDelegate?, recaptchaVerifier: AuthRecaptchaVerifier) async throws -> String? { - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, codeIdentity: CodeIdentity.empty, requestConfiguration: auth @@ -258,7 +257,7 @@ import Foundation private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, multiFactorSession session: MultiFactorSession?, - uiDelegate: AuthUIDelegate?) async throws + uiDelegate: AuthUIDelegate?, recaptchaVerifier: AuthRecaptchaVerifier) async throws -> String? { if let settings = auth.settings, settings.isAppVerificationDisabledForTesting { @@ -274,12 +273,12 @@ import Foundation return try await verifyClAndSendVerificationCodeWithRecaptcha( toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: retryOnInvalidAppCredential, - uiDelegate: uiDelegate + uiDelegate: uiDelegate, + recaptchaVerifier: recaptchaVerifier ) } let startMFARequestInfo = AuthProtoStartMFAPhoneRequestInfo(phoneNumber: phoneNumber, codeIdentity: CodeIdentity.empty) - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) do { if let idToken = session.idToken { let request = StartMFAEnrollmentRequest(idToken: idToken, @@ -564,8 +563,9 @@ import Foundation private let auth: Auth private let callbackScheme: String private let usingClientIDScheme: Bool + private var recaptchaVerifier: AuthRecaptchaVerifier? = nil - init(auth: Auth) { + init(auth: Auth, recaptchaVerifier: AuthRecaptchaVerifier? = nil) { self.auth = auth if let clientID = auth.app?.options.clientID { let reverseClientIDScheme = clientID.components(separatedBy: ".").reversed() @@ -584,6 +584,7 @@ import Foundation return } callbackScheme = "" + self.recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) } private let kAuthTypeVerifyApp = "verifyApp" diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj index 9edf2a88402..422ed29f4a0 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj @@ -34,7 +34,7 @@ DEC2E5DD2A95331E0090260A /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */; }; DEC2E5DF2A9583CA0090260A /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DE2A9583CA0090260A /* AppManager.swift */; }; DED37F632AB0C4F7003A67E4 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */; }; - DEE261C52C21E9F500EECAC5 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + DEE261C52C21E9F500EECAC5 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; EA02F68524A000E00079D000 /* UserActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68424A000E00079D000 /* UserActions.swift */; }; EA02F68D24A063E90079D000 /* LoginDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68C24A063E90079D000 /* LoginDelegate.swift */; }; EA062D5D24A0FEB6006714D3 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = EA062D5C24A0FEB6006714D3 /* README.md */; }; @@ -158,7 +158,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DEE261C52C21E9F500EECAC5 /* (null) in Frameworks */, + DEE261C52C21E9F500EECAC5 /* BuildFile in Frameworks */, DE8B63772BEC302200607B82 /* FacebookLogin in Frameworks */, DE8B63742BEC2FB900607B82 /* GoogleSignInSwift in Frameworks */, DE8B636F2BEC2DC300607B82 /* FirebaseAuth in Frameworks */, @@ -185,11 +185,11 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - DE8B637B2BEC35F000607B82 /* Frameworks */ = { + 0FB63C613203812CC434D554 /* Pods */ = { isa = PBXGroup; children = ( ); - name = Frameworks; + path = Pods; sourceTree = ""; }; DE8FD47B2A7D96B900B6831A /* ObjCApiTests */ = { @@ -289,7 +289,7 @@ EAE4CBE524855E3E00245E92 /* AuthenticationExampleUITests */, DE8FD47B2A7D96B900B6831A /* ObjCApiTests */, EAE4CBC224855E3A00245E92 /* Products */, - DE8B637B2BEC35F000607B82 /* Frameworks */, + 0FB63C613203812CC434D554 /* Pods */, ); sourceTree = ""; }; @@ -476,10 +476,10 @@ ); mainGroup = EAE4CBB824855E3A00245E92; packageReferences = ( - DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "../../../../firebase-ios-sdk" */, - DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, + DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "firebase-ios-sdk" */, + DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */, DE8B63752BEC302200607B82 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */, - DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */, + DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */, 241240D82C755FD900EF64BD /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */, ); productRefGroup = EAE4CBC224855E3A00245E92 /* Products */; @@ -970,7 +970,7 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "../../../../firebase-ios-sdk" */ = { + DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "firebase-ios-sdk" */ = { isa = XCLocalSwiftPackageReference; relativePath = "../../../../firebase-ios-sdk"; }; @@ -985,7 +985,7 @@ minimumVersion = 18.5.1; }; }; - DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */ = { + DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/google/GoogleSignIn-iOS.git"; requirement = { @@ -1001,7 +1001,7 @@ minimumVersion = 17.0.2; }; }; - DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */ = { + DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/google/gtm-session-fetcher.git"; requirement = { @@ -1023,12 +1023,12 @@ }; DE8B63712BEC2FB900607B82 /* GoogleSignIn */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; + package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */; productName = GoogleSignIn; }; DE8B63732BEC2FB900607B82 /* GoogleSignInSwift */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; + package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */; productName = GoogleSignInSwift; }; DE8B63762BEC302200607B82 /* FacebookLogin */ = { @@ -1038,12 +1038,12 @@ }; DE8B63792BEC342000607B82 /* GTMSessionFetcher */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */; + package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */; productName = GTMSessionFetcher; }; DE8B637C2BEC35F000607B82 /* GTMSessionFetcher */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */; + package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */; productName = GTMSessionFetcher; }; /* End XCSwiftPackageProductDependency section */ diff --git a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift index c4191fc926d..13334254776 100644 --- a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift +++ b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift @@ -76,7 +76,7 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { var secureTokenNetworkError: NSError? var secureTokenErrorString: String? var recaptchaSiteKey = "projects/fakeProjectId/keys/mockSiteKey" - var recaptchaEnabled: Bool = true + var rceMode: AuthRecaptchaEnablementStatus = .off func asyncCallToURL(with request: T, body: Data?, @@ -112,20 +112,15 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { } return } else if let _ = request as? GetRecaptchaConfigRequest { - if recaptchaEnabled { // Check if reCAPTCHA is enabled + if rceMode != .off { // Check if reCAPTCHA is enabled let recaptchaKey = recaptchaSiteKey // iOS key from your config let enforcementState = [ - ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": "ENFORCE"], - ["provider": "PHONE_PROVIDER", "enforcementState": "ENFORCE"] + ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": rceMode.rawValue], + ["provider": "PHONE_PROVIDER", "enforcementState": rceMode.rawValue] ] guard let _ = try? respond(withJSON: [ "recaptchaKey": recaptchaKey, "recaptchaEnforcementState": enforcementState, - "managedRules": [ - ["endScore": 0.1, "action": "BLOCK"] - ], - "useSmsBotScore": true, - "useSmsTollFraudProtection": false ]) else { fatalError("GetRecaptchaConfigRequest respond failed") } diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 9b71521ab83..6717010480f 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -41,8 +41,11 @@ private let kVerificationIDKey = "sessionInfo" private let kFakeEncodedFirebaseAppID = "app-1-123456789-ios-123abc456def" private let kFakeReCAPTCHAToken = "fakeReCAPTCHAToken" + private let kCaptchaResponse: String = "captchaResponse" + private let kRecaptchaVersion: String = "RECAPTCHA_ENTERPRISE" static var auth: Auth? + //static var authRecaptchaVerifier: AuthRecaptchaVerifier /** @fn testCredentialWithVerificationID @brief Tests the @c credentialWithToken method to make sure that it returns a valid AuthCredential instance. @@ -62,31 +65,30 @@ } /** @fn testVerifyEmptyPhoneNumber - @brief Tests a failed invocation @c verifyPhoneNumber:completion: because an empty phone + @brief Tests a failed invocation verifyPhoneNumber because an empty phone number was provided. */ - func testVerifyEmptyPhoneNumber() throws { + func testVerifyEmptyPhoneNumber() async throws { initApp(#function) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) let provider = PhoneAuthProvider.provider(auth: auth) let expectation = self.expectation(description: #function) - // Empty phone number is checked on the client side so no backend RPC is faked. - provider.verifyPhoneNumber("", uiDelegate: nil) { verificationID, error in - XCTAssertNotNil(error) - XCTAssertNil(verificationID) - XCTAssertEqual((error as? NSError)?.code, AuthErrorCode.missingPhoneNumber.rawValue) - expectation.fulfill() + do { + _ = try await provider.verifyPhoneNumber("") + XCTFail("Expected an error, but verification succeeded.") + } catch { + XCTAssertEqual((error as NSError).code, AuthErrorCode.missingPhoneNumber.rawValue) } - waitForExpectations(timeout: 5) + await waitForExpectations(timeout: 5) } /** @fn testVerifyInvalidPhoneNumber @brief Tests a failed invocation @c verifyPhoneNumber:completion: because an invalid phone number was provided. */ - func testVerifyInvalidPhoneNumber() throws { - try internalTestVerify(errorString: "INVALID_PHONE_NUMBER", + func testVerifyInvalidPhoneNumber() async throws { + try await internalTestVerify(errorString: "INVALID_PHONE_NUMBER", errorCode: AuthErrorCode.invalidPhoneNumber.rawValue, function: #function) } @@ -94,24 +96,66 @@ /** @fn testVerifyPhoneNumber @brief Tests a successful invocation of @c verifyPhoneNumber:completion:. */ - func testVerifyPhoneNumber() throws { - try internalTestVerify(function: #function) + func testVerifyPhoneNumber() async throws { + try await internalTestVerify(function: #function) + } + + /** + @fn testVerifyPhoneNumberWithRceEnforce + @brief Tests a successful invocation of @c verifyPhoneNumber:completion: with recaptcha enterprise enforced + */ + func testVerifyPhoneNumberWithRceEnforce() async throws { + initApp(#function) + let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) + // TODO: Figure out how to mock objective C's FIRRecaptchaGetToken response + let mockVerifier = FakeAuthRecaptchaVerifier.shared(auth: auth) + let provider = PhoneAuthProvider.provider(auth: auth) + + let requestExpectation = self.expectation(description: "verifyRequester") + rpcIssuer.rceMode = .enforce + rpcIssuer?.verifyRequester = { request in + XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) + XCTAssertEqual(request.captchaResponse, "mock-token") + XCTAssertEqual(request.recaptchaVersion, "RECAPTCHA_ENTERPRISE") + XCTAssertNil(request.codeIdentity) + requestExpectation.fulfill() + do { + try self.rpcIssuer? + .respond(withJSON: [self.kVerificationIDKey: self.kTestVerificationID]) + } catch { + XCTFail("Failure sending response: \(error)") + } + } + + do { + let verificationID = try await provider.verifyPhoneNumber(self.kTestPhoneNumber) + XCTAssertEqual(verificationID, self.kTestVerificationID) + } catch { + XCTFail("Unexpected error: \(error)") + } + + wait(for: [requestExpectation], timeout: 5.0) + } + + + func testVerifyPhoneNumberWithRceAuditFallback() async throws { + try await internalTestVerify(function: #function, useClientID: true, bothClientAndAppID: true, reCAPTCHAfallback: true, rceMode: .audit) } /** @fn testVerifyPhoneNumberInTestMode @brief Tests a successful invocation of @c verifyPhoneNumber:completion: when app verification is disabled. */ - func testVerifyPhoneNumberInTestMode() throws { - try internalTestVerify(function: #function, testMode: true) + func testVerifyPhoneNumberInTestMode() async throws { + try await internalTestVerify(function: #function, testMode: true) } /** @fn testVerifyPhoneNumberInTestModeFailure @brief Tests a failed invocation of @c verifyPhoneNumber:completion: when app verification is disabled. */ - func testVerifyPhoneNumberInTestModeFailure() throws { - try internalTestVerify(errorString: "INVALID_PHONE_NUMBER", + func testVerifyPhoneNumberInTestModeFailure() async throws { + try await internalTestVerify(errorString: "INVALID_PHONE_NUMBER", errorCode: AuthErrorCode.invalidPhoneNumber.rawValue, function: #function, testMode: true) } @@ -119,32 +163,32 @@ /** @fn testVerifyPhoneNumberUIDelegateFirebaseAppIdFlow @brief Tests a successful invocation of @c verifyPhoneNumber:UIDelegate:completion:. */ - func testVerifyPhoneNumberUIDelegateFirebaseAppIdFlow() throws { - try internalTestVerify(function: #function, reCAPTCHAfallback: true) + func testVerifyPhoneNumberUIDelegateFirebaseAppIdFlow() async throws { + try await internalTestVerify(function: #function, reCAPTCHAfallback: true) } /** @fn testVerifyPhoneNumberUIDelegateFirebaseAppIdWhileClientIdPresentFlow @brief Tests a successful invocation of @c verifyPhoneNumber:UIDelegate:completion: when the client ID is present in the plist file, but the encoded app ID is the registered custom URL scheme. */ - func testVerifyPhoneNumberUIDelegateFirebaseAppIdWhileClientIdPresentFlow() throws { - try internalTestVerify(function: #function, useClientID: true, + func testVerifyPhoneNumberUIDelegateFirebaseAppIdWhileClientIdPresentFlow() async throws { + try await internalTestVerify(function: #function, useClientID: true, bothClientAndAppID: true, reCAPTCHAfallback: true) } /** @fn testVerifyPhoneNumberUIDelegateClientIdFlow @brief Tests a successful invocation of @c verifyPhoneNumber:UIDelegate:completion:. */ - func testVerifyPhoneNumberUIDelegateClientIdFlow() throws { - try internalTestVerify(function: #function, useClientID: true, reCAPTCHAfallback: true) + func testVerifyPhoneNumberUIDelegateClientIdFlow() async throws { + try await internalTestVerify(function: #function, useClientID: true, reCAPTCHAfallback: true) } /** @fn testVerifyPhoneNumberUIDelegateInvalidClientID @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an invalid client ID error. */ - func testVerifyPhoneNumberUIDelegateInvalidClientID() throws { - try internalTestVerify( + func testVerifyPhoneNumberUIDelegateInvalidClientID() async throws { + try await internalTestVerify( errorURLString: PhoneAuthProviderTests.kFakeRedirectURLStringInvalidClientID, errorCode: AuthErrorCode.invalidClientID.rawValue, function: #function, @@ -157,8 +201,8 @@ @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in a web network request failed error. */ - func testVerifyPhoneNumberUIDelegateWebNetworkRequestFailed() throws { - try internalTestVerify( + func testVerifyPhoneNumberUIDelegateWebNetworkRequestFailed() async throws { + try await internalTestVerify( errorURLString: PhoneAuthProviderTests.kFakeRedirectURLStringWebNetworkRequestFailed, errorCode: AuthErrorCode.webNetworkRequestFailed.rawValue, function: #function, @@ -171,8 +215,8 @@ @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in a web internal error. */ - func testVerifyPhoneNumberUIDelegateWebInternalError() throws { - try internalTestVerify( + func testVerifyPhoneNumberUIDelegateWebInternalError() async throws { + try await internalTestVerify( errorURLString: PhoneAuthProviderTests.kFakeRedirectURLStringWebInternalError, errorCode: AuthErrorCode.webInternalError.rawValue, function: #function, @@ -182,11 +226,11 @@ } /** @fn testVerifyPhoneNumberUIDelegateUnexpectedError - @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an - invalid client ID. + @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an + invalid client ID. */ - func testVerifyPhoneNumberUIDelegateUnexpectedError() throws { - try internalTestVerify( + func testVerifyPhoneNumberUIDelegateUnexpectedError() async throws { + try await internalTestVerify( errorURLString: PhoneAuthProviderTests.kFakeRedirectURLStringUnknownError, errorCode: AuthErrorCode.webSignInUserInteractionFailure.rawValue, function: #function, @@ -196,12 +240,12 @@ } /** @fn testVerifyPhoneNumberUIDelegateUnstructuredError - @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an - error being surfaced with a default NSLocalizedFailureReasonErrorKey due to an unexpected - structure of the error response. + @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an + error being surfaced with a default NSLocalizedFailureReasonErrorKey due to an unexpected + structure of the error response. */ - func testVerifyPhoneNumberUIDelegateUnstructuredError() throws { - try internalTestVerify( + func testVerifyPhoneNumberUIDelegateUnstructuredError() async throws { + try await internalTestVerify( errorURLString: PhoneAuthProviderTests.kFakeRedirectURLStringUnstructuredError, errorCode: AuthErrorCode.appVerificationUserInteractionFailure.rawValue, function: #function, @@ -214,10 +258,10 @@ // The test runs correctly, but it's not clear how to automate fatal_error testing. Switching to // Swift exceptions would break the API. /** @fn testVerifyPhoneNumberUIDelegateRaiseException - @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an - exception. + @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an + exception. */ - func SKIPtestVerifyPhoneNumberUIDelegateRaiseException() throws { + func SKIPtestVerifyPhoneNumberUIDelegateRaiseException() async throws { initApp(#function) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) auth.mainBundleUrlTypes = [["CFBundleURLSchemes": ["fail"]]] @@ -228,11 +272,11 @@ } /** @fn testNotForwardingNotification - @brief Tests returning an error for the app failing to forward notification. + @brief Tests returning an error for the app failing to forward notification. */ func testNotForwardingNotification() throws { - func testVerifyPhoneNumberUIDelegateUnstructuredError() throws { - try internalTestVerify( + func testVerifyPhoneNumberUIDelegateUnstructuredError() async throws { + try await internalTestVerify( errorURLString: PhoneAuthProviderTests.kFakeRedirectURLStringUnstructuredError, errorCode: AuthErrorCode.appVerificationUserInteractionFailure.rawValue, function: #function, @@ -244,10 +288,10 @@ } /** @fn testMissingAPNSToken - @brief Tests returning an error for the app failing to provide an APNS device token. + @brief Tests returning an error for the app failing to provide an APNS device token. */ - func testMissingAPNSToken() throws { - try internalTestVerify( + func testMissingAPNSToken() async throws { + try await internalTestVerify( errorCode: AuthErrorCode.missingAppToken.rawValue, function: #function, useClientID: true, @@ -268,28 +312,28 @@ } /** @fn testVerifyClient - @brief Tests verifying client before sending verification code. + @brief Tests verifying client before sending verification code. */ func testVerifyClient() throws { try internalFlow(function: #function, useClientID: true, reCAPTCHAfallback: false) } /** @fn testSendVerificationCodeFailedRetry - @brief Tests failed retry after failing to send verification code. + @brief Tests failed retry after failing to send verification code. */ func testSendVerificationCodeFailedRetry() throws { try internalFlowRetry(function: #function) } /** @fn testSendVerificationCodeSuccessfulRetry - @brief Tests successful retry after failing to send verification code. + @brief Tests successful retry after failing to send verification code. */ func testSendVerificationCodeSuccessfulRetry() throws { try internalFlowRetry(function: #function, goodRetry: true) } /** @fn testPhoneAuthCredentialCoding - @brief Tests successful archiving and unarchiving of @c PhoneAuthCredential. + @brief Tests successful archiving and unarchiving of @c PhoneAuthCredential. */ func testPhoneAuthCredentialCoding() throws { let kVerificationID = "My verificationID" @@ -315,7 +359,7 @@ } /** @fn testPhoneAuthCredentialCodingPhone - @brief Tests successful archiving and unarchiving of @c PhoneAuthCredential after other constructor. + @brief Tests successful archiving and unarchiving of @c PhoneAuthCredential after other constructor. */ func testPhoneAuthCredentialCodingPhone() throws { let kTemporaryProof = "Proof" @@ -536,7 +580,6 @@ /** @fn testVerifyClient @brief Tests verifying client before sending verification code. */ - private func internalTestVerify(errorString: String? = nil, errorURLString: String? = nil, errorCode: Int = 0, @@ -546,13 +589,16 @@ bothClientAndAppID: Bool = false, reCAPTCHAfallback: Bool = false, forwardingNotification: Bool = true, - presenterError: Error? = nil) throws { + presenterError: Error? = nil, + rceMode: AuthRecaptchaEnablementStatus = .off + ) async throws { initApp(function, useClientID: useClientID, bothClientAndAppID: bothClientAndAppID, testMode: testMode, forwardingNotification: forwardingNotification) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) let provider = PhoneAuthProvider.provider(auth: auth) let expectation = self.expectation(description: function) + rpcIssuer?.rceMode = rceMode if !reCAPTCHAfallback { // Fake out appCredentialManager flow. @@ -575,7 +621,20 @@ } } } - + if reCAPTCHAfallback { + // Use fake authURLPresenter so we can test the parameters that get sent to it. + let urlString = errorURLString ?? + PhoneAuthProviderTests.kFakeRedirectURLStringWithReCAPTCHAToken + let errorTest = errorURLString != nil + PhoneAuthProviderTests.auth?.authURLPresenter = + FakePresenter( + urlString: urlString, + clientID: useClientID ? PhoneAuthProviderTests.kFakeClientID : nil, + firebaseAppID: useClientID ? nil : PhoneAuthProviderTests.kFakeFirebaseAppID, + errorTest: errorTest, + presenterError: presenterError + ) + } if errorURLString == nil, presenterError == nil { let requestExpectation = self.expectation(description: "verifyRequester") rpcIssuer?.verifyRequester = { request in @@ -605,40 +664,20 @@ } } } - if reCAPTCHAfallback { - // Use fake authURLPresenter so we can test the parameters that get sent to it. - let urlString = errorURLString ?? - PhoneAuthProviderTests.kFakeRedirectURLStringWithReCAPTCHAToken - let errorTest = errorURLString != nil - PhoneAuthProviderTests.auth?.authURLPresenter = - FakePresenter( - urlString: urlString, - clientID: useClientID ? PhoneAuthProviderTests.kFakeClientID : nil, - firebaseAppID: useClientID ? nil : PhoneAuthProviderTests.kFakeFirebaseAppID, - errorTest: errorTest, - presenterError: presenterError - ) - } let uiDelegate = reCAPTCHAfallback ? FakeUIDelegate() : nil - // 2. After setting up the parameters, call `verifyPhoneNumber`. do { // Call the async function to verify the phone number let verificationID = try await provider.verifyPhoneNumber(kTestPhoneNumber, uiDelegate: uiDelegate) - - // Check that we are on the main thread - XCTAssertTrue(Thread.isMainThread) - // Assert that the verificationID matches the expected value XCTAssertEqual(verificationID, self.kTestVerificationID) } catch { // If an error occurs, assert that verificationID is nil and the error code matches the expected value - XCTAssertNil(verificationID) XCTAssertEqual((error as? NSError)?.code, errorCode) } - + expectation.fulfill() // Make sure the test waits for expectations to be fulfilled - waitForExpectations(timeout: 5) + await waitForExpectations(timeout: 5.0) } private func initApp(_ functionName: String, @@ -690,6 +729,19 @@ } } } + + class FakeAuthRecaptchaVerifier: AuthRecaptchaVerifier { + var captchaResponse: String = "captchaResponse" + var fakeError: Error? + + override func verify(forceRefresh: Bool, action: AuthRecaptchaAction) async throws -> String { + if let error = fakeError { + throw error + } + return captchaResponse + } + } + class FakeTokenManager: AuthAPNSTokenManager { override func getTokenInternal(callback: @escaping (Result) -> Void) { diff --git a/Package.swift b/Package.swift index 8647898aba5..56af647c794 100644 --- a/Package.swift +++ b/Package.swift @@ -1343,7 +1343,7 @@ func googleAppMeasurementDependency() -> Package.Dependency { return .package(url: appMeasurementURL, branch: "main") } - return .package(url: appMeasurementURL, exact: "11.1.0") + return .package(url: appMeasurementURL, branch: "main") } func abseilDependency() -> Package.Dependency { From 01dbe0986d15f26131320fe10346ea0d6ceedacd Mon Sep 17 00:00:00 2001 From: Pragati Date: Mon, 26 Aug 2024 23:42:53 -0700 Subject: [PATCH 25/43] Revert setup changes --- .../project.pbxproj | 68 +++++++------------ .../SwiftApplication.plist | 4 +- Package.swift | 2 +- 3 files changed, 28 insertions(+), 46 deletions(-) diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj index 422ed29f4a0..edee157b901 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample.xcodeproj/project.pbxproj @@ -7,9 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 241240D62C755F9100EF64BD /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 241240D42C755F9100EF64BD /* GoogleService-Info.plist */; }; - 241240D72C755F9100EF64BD /* GoogleService-Info_multi.plist in Resources */ = {isa = PBXBuildFile; fileRef = 241240D52C755F9100EF64BD /* GoogleService-Info_multi.plist */; }; - 241240DA2C755FEB00EF64BD /* RecaptchaEnterprise in Frameworks */ = {isa = PBXBuildFile; productRef = 241240D92C755FEB00EF64BD /* RecaptchaEnterprise */; }; + 8848765129D314A400780FA6 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8848764F29D3149200780FA6 /* GoogleService-Info.plist */; }; DE8B636F2BEC2DC300607B82 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = DE8B636E2BEC2DC300607B82 /* FirebaseAuth */; }; DE8B63722BEC2FB900607B82 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = DE8B63712BEC2FB900607B82 /* GoogleSignIn */; }; DE8B63742BEC2FB900607B82 /* GoogleSignInSwift in Frameworks */ = {isa = PBXBuildFile; productRef = DE8B63732BEC2FB900607B82 /* GoogleSignInSwift */; }; @@ -33,8 +31,9 @@ DE8FD4942A7D9E2700B6831A /* PhoneMultiFactorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8FD4912A7D9D9E00B6831A /* PhoneMultiFactorTests.swift */; }; DEC2E5DD2A95331E0090260A /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */; }; DEC2E5DF2A9583CA0090260A /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DE2A9583CA0090260A /* AppManager.swift */; }; + DEC2E5E42A966DE20090260A /* GoogleService-Info_multi.plist in Resources */ = {isa = PBXBuildFile; fileRef = DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */; }; DED37F632AB0C4F7003A67E4 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */; }; - DEE261C52C21E9F500EECAC5 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; + DEE261C52C21E9F500EECAC5 /* RecaptchaEnterprise in Frameworks */ = {isa = PBXBuildFile; productRef = DEE261C42C21E9F500EECAC5 /* RecaptchaEnterprise */; }; EA02F68524A000E00079D000 /* UserActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68424A000E00079D000 /* UserActions.swift */; }; EA02F68D24A063E90079D000 /* LoginDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68C24A063E90079D000 /* LoginDelegate.swift */; }; EA062D5D24A0FEB6006714D3 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = EA062D5C24A0FEB6006714D3 /* README.md */; }; @@ -88,8 +87,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 241240D42C755F9100EF64BD /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../../../../Downloads/GoogleService-Info.plist"; sourceTree = ""; }; - 241240D52C755F9100EF64BD /* GoogleService-Info_multi.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info_multi.plist"; path = "../../../../../../../Downloads/GoogleService-Info_multi.plist"; sourceTree = ""; }; + 8848764F29D3149200780FA6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; }; DE4D8E1F2A8B0311001952B6 /* SwiftApplication.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SwiftApplication.plist; sourceTree = ""; }; DE8FD4682A7D660A00B6831A /* Credentials.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = ""; }; DE8FD4692A7D660A00B6831A /* AccountInfoTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInfoTests.swift; sourceTree = ""; }; @@ -111,6 +109,7 @@ DE8FD4972A7DB00600B6831A /* AuthCredentials.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuthCredentials.h; sourceTree = ""; }; DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; DEC2E5DE2A9583CA0090260A /* AppManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = ""; }; + DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info_multi.plist"; sourceTree = SOURCE_ROOT; }; DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsUITests.swift; sourceTree = ""; }; EA02F68424A000E00079D000 /* UserActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActions.swift; sourceTree = ""; }; EA02F68C24A063E90079D000 /* LoginDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDelegate.swift; sourceTree = ""; }; @@ -158,12 +157,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - DEE261C52C21E9F500EECAC5 /* BuildFile in Frameworks */, + DEE261C52C21E9F500EECAC5 /* RecaptchaEnterprise in Frameworks */, DE8B63772BEC302200607B82 /* FacebookLogin in Frameworks */, DE8B63742BEC2FB900607B82 /* GoogleSignInSwift in Frameworks */, DE8B636F2BEC2DC300607B82 /* FirebaseAuth in Frameworks */, DE8B63722BEC2FB900607B82 /* GoogleSignIn in Frameworks */, - 241240DA2C755FEB00EF64BD /* RecaptchaEnterprise in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -185,11 +183,11 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0FB63C613203812CC434D554 /* Pods */ = { + DE8B637B2BEC35F000607B82 /* Frameworks */ = { isa = PBXGroup; children = ( ); - path = Pods; + name = Frameworks; sourceTree = ""; }; DE8FD47B2A7D96B900B6831A /* ObjCApiTests */ = { @@ -289,7 +287,7 @@ EAE4CBE524855E3E00245E92 /* AuthenticationExampleUITests */, DE8FD47B2A7D96B900B6831A /* ObjCApiTests */, EAE4CBC224855E3A00245E92 /* Products */, - 0FB63C613203812CC434D554 /* Pods */, + DE8B637B2BEC35F000607B82 /* Frameworks */, ); sourceTree = ""; }; @@ -307,8 +305,7 @@ EAE4CBC324855E3A00245E92 /* AuthenticationExample */ = { isa = PBXGroup; children = ( - 241240D52C755F9100EF64BD /* GoogleService-Info_multi.plist */, - 241240D42C755F9100EF64BD /* GoogleService-Info.plist */, + DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */, DE4D8E1F2A8B0311001952B6 /* SwiftApplication.plist */, EA062D5C24A0FEB6006714D3 /* README.md */, EA20B506249CA63300B5E581 /* AuthenticationExample.entitlements */, @@ -319,6 +316,7 @@ EA20B47724973BB100B5E581 /* CustomViews */, EAB3A17A2494626F00385291 /* Utility */, EAE4CBCD24855E3D00245E92 /* Assets.xcassets */, + 8848764F29D3149200780FA6 /* GoogleService-Info.plist */, EA217894248979E200736757 /* LaunchScreen.storyboard */, DEC2E5DE2A9583CA0090260A /* AppManager.swift */, ); @@ -392,7 +390,7 @@ DE8B63712BEC2FB900607B82 /* GoogleSignIn */, DE8B63732BEC2FB900607B82 /* GoogleSignInSwift */, DE8B63762BEC302200607B82 /* FacebookLogin */, - 241240D92C755FEB00EF64BD /* RecaptchaEnterprise */, + DEE261C42C21E9F500EECAC5 /* RecaptchaEnterprise */, ); productName = "Swifty Auth"; productReference = EAE4CBC124855E3A00245E92 /* AuthenticationExample.app */; @@ -476,11 +474,10 @@ ); mainGroup = EAE4CBB824855E3A00245E92; packageReferences = ( - DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "firebase-ios-sdk" */, - DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */, + DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "../../../../firebase-ios-sdk" */, + DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, DE8B63752BEC302200607B82 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */, - DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */, - 241240D82C755FD900EF64BD /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */, + DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */, ); productRefGroup = EAE4CBC224855E3A00245E92 /* Products */; projectDirPath = ""; @@ -507,8 +504,8 @@ buildActionMask = 2147483647; files = ( EA217895248979E200736757 /* LaunchScreen.storyboard in Resources */, - 241240D62C755F9100EF64BD /* GoogleService-Info.plist in Resources */, - 241240D72C755F9100EF64BD /* GoogleService-Info_multi.plist in Resources */, + 8848765129D314A400780FA6 /* GoogleService-Info.plist in Resources */, + DEC2E5E42A966DE20090260A /* GoogleService-Info_multi.plist in Resources */, EAE4CBCE24855E3D00245E92 /* Assets.xcassets in Resources */, EA062D5D24A0FEB6006714D3 /* README.md in Resources */, ); @@ -797,7 +794,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1.dev; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -820,7 +817,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1.dev; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseExperimental1; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; @@ -970,22 +967,14 @@ /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ - DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "firebase-ios-sdk" */ = { + DE8B636D2BEC2DC300607B82 /* XCLocalSwiftPackageReference "../../../../firebase-ios-sdk" */ = { isa = XCLocalSwiftPackageReference; relativePath = "../../../../firebase-ios-sdk"; }; /* End XCLocalSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */ - 241240D82C755FD900EF64BD /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/GoogleCloudPlatform/recaptcha-enterprise-mobile-sdk"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 18.5.1; - }; - }; - DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */ = { + DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/google/GoogleSignIn-iOS.git"; requirement = { @@ -1001,7 +990,7 @@ minimumVersion = 17.0.2; }; }; - DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */ = { + DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/google/gtm-session-fetcher.git"; requirement = { @@ -1012,23 +1001,18 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 241240D92C755FEB00EF64BD /* RecaptchaEnterprise */ = { - isa = XCSwiftPackageProductDependency; - package = 241240D82C755FD900EF64BD /* XCRemoteSwiftPackageReference "recaptcha-enterprise-mobile-sdk" */; - productName = RecaptchaEnterprise; - }; DE8B636E2BEC2DC300607B82 /* FirebaseAuth */ = { isa = XCSwiftPackageProductDependency; productName = FirebaseAuth; }; DE8B63712BEC2FB900607B82 /* GoogleSignIn */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */; + package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; productName = GoogleSignIn; }; DE8B63732BEC2FB900607B82 /* GoogleSignInSwift */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS.git" */; + package = DE8B63702BEC2FB900607B82 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; productName = GoogleSignInSwift; }; DE8B63762BEC302200607B82 /* FacebookLogin */ = { @@ -1038,12 +1022,12 @@ }; DE8B63792BEC342000607B82 /* GTMSessionFetcher */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */; + package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */; productName = GTMSessionFetcher; }; DE8B637C2BEC35F000607B82 /* GTMSessionFetcher */ = { isa = XCSwiftPackageProductDependency; - package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher.git" */; + package = DE8B63782BEC342000607B82 /* XCRemoteSwiftPackageReference "gtm-session-fetcher" */; productName = GTMSessionFetcher; }; /* End XCSwiftPackageProductDependency section */ diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist index b3e4a0c579f..50378fe58b0 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/SwiftApplication.plist @@ -24,9 +24,7 @@ CFBundleURLName CFBundleURLSchemes - - app-1-585304629422-ios-cb0e5761816443a9d98d3f - + CFBundleVersion diff --git a/Package.swift b/Package.swift index 56af647c794..8647898aba5 100644 --- a/Package.swift +++ b/Package.swift @@ -1343,7 +1343,7 @@ func googleAppMeasurementDependency() -> Package.Dependency { return .package(url: appMeasurementURL, branch: "main") } - return .package(url: appMeasurementURL, branch: "main") + return .package(url: appMeasurementURL, exact: "11.1.0") } func abseilDependency() -> Package.Dependency { From 89671402cf0466112f24b98edbcdb787bfbdc781 Mon Sep 17 00:00:00 2001 From: Pragati Date: Mon, 26 Aug 2024 23:45:40 -0700 Subject: [PATCH 26/43] lint changes --- .../AuthProvider/PhoneAuthProvider.swift | 159 ++++++++++-------- .../Utilities/AuthRecaptchaVerifier.swift | 4 +- .../Unit/Fakes/FakeBackendRPCIssuer.swift | 4 +- .../Tests/Unit/PhoneAuthProviderTests.swift | 77 +++++---- 4 files changed, 139 insertions(+), 105 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 698c3baed9b..81222684b3f 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -102,25 +102,26 @@ import Foundation /// identifies the user trying to enroll. For sign-in, this identifies that the user already /// passed the first factor challenge. /// - Returns: The verification ID - @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) - open func verifyPhoneNumber(_ phoneNumber: String, - uiDelegate: AuthUIDelegate? = nil, - multiFactorSession: MultiFactorSession? = nil) async throws -> String { - guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, - urlTypes: auth.mainBundleUrlTypes) else { - fatalError( - "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." - ) - } - - if let verificationID = try await internalVerify(phoneNumber: phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) { - return verificationID - } else { - throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") + @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) + open func verifyPhoneNumber(_ phoneNumber: String, + uiDelegate: AuthUIDelegate? = nil, + multiFactorSession: MultiFactorSession? = nil) async throws + -> String { + guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, + urlTypes: auth.mainBundleUrlTypes) else { + fatalError( + "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." + ) + } + + if let verificationID = try await internalVerify(phoneNumber: phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) { + return verificationID + } else { + throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") + } } - } /// Verify ownership of the second factor phone number by the current user. /// - Parameter multiFactorInfo: The phone multi factor whose number need to be verified. @@ -150,15 +151,15 @@ import Foundation /// identifies the user trying to enroll. For sign-in, this identifies that the user already /// passed the first factor challenge. /// - Returns: The verification ID. - @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) - open func verifyPhoneNumber(with multiFactorInfo: PhoneMultiFactorInfo, - uiDelegate: AuthUIDelegate? = nil, - multiFactorSession: MultiFactorSession?) async throws -> String { - multiFactorSession?.multiFactorInfo = multiFactorInfo - return try await verifyPhoneNumber(multiFactorInfo.phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) - } + @available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *) + open func verifyPhoneNumber(with multiFactorInfo: PhoneMultiFactorInfo, + uiDelegate: AuthUIDelegate? = nil, + multiFactorSession: MultiFactorSession?) async throws -> String { + multiFactorSession?.multiFactorInfo = multiFactorInfo + return try await verifyPhoneNumber(multiFactorInfo.phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) + } /// Creates an `AuthCredential` for the phone number provider identified by the /// verification ID and verification code. @@ -176,35 +177,54 @@ import Foundation verificationCode: verificationCode) } - private func internalVerify(phoneNumber: String, - uiDelegate: AuthUIDelegate?, - multiFactorSession: MultiFactorSession? = nil) async throws -> String? { - guard !phoneNumber.isEmpty else { - throw AuthErrorUtils.missingPhoneNumberError(message: nil) - } - guard let manager = auth.notificationManager else { - throw AuthErrorUtils.notificationNotForwardedError() - } - guard await manager.checkNotificationForwarding() else { - throw AuthErrorUtils.notificationNotForwardedError() - } - - let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) - - switch recaptchaVerifier.enablementStatus(forProvider: .phone) { - case .off: - return try await verifyClAndSendVerificationCode(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession,uiDelegate: uiDelegate) - case .audit: - return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate, recaptchaVerifier: recaptchaVerifier) - case .enforce: - return try await verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber: phoneNumber, retryOnInvalidAppCredential: true, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate, recaptchaVerifier: recaptchaVerifier) + private func internalVerify(phoneNumber: String, + uiDelegate: AuthUIDelegate?, + multiFactorSession: MultiFactorSession? = nil) async throws + -> String? { + guard !phoneNumber.isEmpty else { + throw AuthErrorUtils.missingPhoneNumberError(message: nil) + } + guard let manager = auth.notificationManager else { + throw AuthErrorUtils.notificationNotForwardedError() + } + guard await manager.checkNotificationForwarding() else { + throw AuthErrorUtils.notificationNotForwardedError() + } + + let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) + try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) + + switch recaptchaVerifier.enablementStatus(forProvider: .phone) { + case .off: + return try await verifyClAndSendVerificationCode( + toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: true, + multiFactorSession: multiFactorSession, + uiDelegate: uiDelegate + ) + case .audit: + return try await verifyClAndSendVerificationCodeWithRecaptcha( + toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: true, + multiFactorSession: multiFactorSession, + uiDelegate: uiDelegate, + recaptchaVerifier: recaptchaVerifier + ) + case .enforce: + return try await verifyClAndSendVerificationCodeWithRecaptcha( + toPhoneNumber: phoneNumber, + retryOnInvalidAppCredential: true, + multiFactorSession: multiFactorSession, + uiDelegate: uiDelegate, + recaptchaVerifier: recaptchaVerifier + ) + } } - } private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, - uiDelegate: AuthUIDelegate?, recaptchaVerifier: AuthRecaptchaVerifier) async throws + uiDelegate: AuthUIDelegate?, + recaptchaVerifier: AuthRecaptchaVerifier) async throws -> String? { let request = SendVerificationCodeRequest(phoneNumber: phoneNumber, codeIdentity: CodeIdentity.empty, @@ -246,7 +266,13 @@ import Foundation let response = try await AuthBackend.call(with: request) return response.verificationID } catch { - return try await handleVerifyErrorWithRetry(error: error,phoneNumber: phoneNumber,retryOnInvalidAppCredential:retryOnInvalidAppCredential,multiFactorSession: nil, uiDelegate: uiDelegate) + return try await handleVerifyErrorWithRetry( + error: error, + phoneNumber: phoneNumber, + retryOnInvalidAppCredential: retryOnInvalidAppCredential, + multiFactorSession: nil, + uiDelegate: uiDelegate + ) } } @@ -257,18 +283,19 @@ import Foundation private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, multiFactorSession session: MultiFactorSession?, - uiDelegate: AuthUIDelegate?, recaptchaVerifier: AuthRecaptchaVerifier) async throws + uiDelegate: AuthUIDelegate?, + recaptchaVerifier: AuthRecaptchaVerifier) async throws -> String? { - if let settings = auth.settings, - settings.isAppVerificationDisabledForTesting { - let request = SendVerificationCodeRequest( - phoneNumber: phoneNumber, - codeIdentity: CodeIdentity.empty, - requestConfiguration: auth.requestConfiguration - ) - let response = try await AuthBackend.call(with: request) - return response.verificationID - } + if let settings = auth.settings, + settings.isAppVerificationDisabledForTesting { + let request = SendVerificationCodeRequest( + phoneNumber: phoneNumber, + codeIdentity: CodeIdentity.empty, + requestConfiguration: auth.requestConfiguration + ) + let response = try await AuthBackend.call(with: request) + return response.verificationID + } guard let session else { return try await verifyClAndSendVerificationCodeWithRecaptcha( toPhoneNumber: phoneNumber, @@ -563,9 +590,9 @@ import Foundation private let auth: Auth private let callbackScheme: String private let usingClientIDScheme: Bool - private var recaptchaVerifier: AuthRecaptchaVerifier? = nil + private var recaptchaVerifier: AuthRecaptchaVerifier? - init(auth: Auth, recaptchaVerifier: AuthRecaptchaVerifier? = nil) { + init(auth: Auth, recaptchaVerifier: AuthRecaptchaVerifier? = nil) { self.auth = auth if let clientID = auth.app?.options.clientID { let reverseClientIDScheme = clientID.components(separatedBy: ".").reversed() diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 1682894230a..3e2742eee86 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -169,10 +169,10 @@ AuthLog.logInfo(code: "I-AUT000029", message: "reCAPTCHA config retrieval succeeded.") try await parseRecaptchaConfigFromResponse(response: response) } - + func parseRecaptchaConfigFromResponse(response: GetRecaptchaConfigResponse) async throws { var enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] = [:] - var isRecaptchaEnabled: Bool = false + var isRecaptchaEnabled = false if let enforcementState = response.enforcementState { for state in enforcementState { guard let providerString = state["provider"] as? String, diff --git a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift index 13334254776..9ce47ed7039 100644 --- a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift +++ b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift @@ -116,7 +116,7 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { let recaptchaKey = recaptchaSiteKey // iOS key from your config let enforcementState = [ ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": rceMode.rawValue], - ["provider": "PHONE_PROVIDER", "enforcementState": rceMode.rawValue] + ["provider": "PHONE_PROVIDER", "enforcementState": rceMode.rawValue], ] guard let _ = try? respond(withJSON: [ "recaptchaKey": recaptchaKey, @@ -127,7 +127,7 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { } else { // reCAPTCHA OFF let enforcementState = [ ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": "OFF"], - ["provider": "PHONE_PROVIDER", "enforcementState": "OFF"] + ["provider": "PHONE_PROVIDER", "enforcementState": "OFF"], ] guard let _ = try? respond(withJSON: [ "recaptchaEnforcementState": enforcementState, diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 6717010480f..8ef7bcf1887 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -45,7 +45,7 @@ private let kRecaptchaVersion: String = "RECAPTCHA_ENTERPRISE" static var auth: Auth? - //static var authRecaptchaVerifier: AuthRecaptchaVerifier + // static var authRecaptchaVerifier: AuthRecaptchaVerifier /** @fn testCredentialWithVerificationID @brief Tests the @c credentialWithToken method to make sure that it returns a valid AuthCredential instance. @@ -89,8 +89,8 @@ */ func testVerifyInvalidPhoneNumber() async throws { try await internalTestVerify(errorString: "INVALID_PHONE_NUMBER", - errorCode: AuthErrorCode.invalidPhoneNumber.rawValue, - function: #function) + errorCode: AuthErrorCode.invalidPhoneNumber.rawValue, + function: #function) } /** @fn testVerifyPhoneNumber @@ -99,7 +99,7 @@ func testVerifyPhoneNumber() async throws { try await internalTestVerify(function: #function) } - + /** @fn testVerifyPhoneNumberWithRceEnforce @brief Tests a successful invocation of @c verifyPhoneNumber:completion: with recaptcha enterprise enforced @@ -110,8 +110,8 @@ // TODO: Figure out how to mock objective C's FIRRecaptchaGetToken response let mockVerifier = FakeAuthRecaptchaVerifier.shared(auth: auth) let provider = PhoneAuthProvider.provider(auth: auth) - - let requestExpectation = self.expectation(description: "verifyRequester") + + let requestExpectation = expectation(description: "verifyRequester") rpcIssuer.rceMode = .enforce rpcIssuer?.verifyRequester = { request in XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) @@ -126,20 +126,25 @@ XCTFail("Failure sending response: \(error)") } } - + do { - let verificationID = try await provider.verifyPhoneNumber(self.kTestPhoneNumber) - XCTAssertEqual(verificationID, self.kTestVerificationID) + let verificationID = try await provider.verifyPhoneNumber(kTestPhoneNumber) + XCTAssertEqual(verificationID, kTestVerificationID) } catch { XCTFail("Unexpected error: \(error)") } - + wait(for: [requestExpectation], timeout: 5.0) } - func testVerifyPhoneNumberWithRceAuditFallback() async throws { - try await internalTestVerify(function: #function, useClientID: true, bothClientAndAppID: true, reCAPTCHAfallback: true, rceMode: .audit) + try await internalTestVerify( + function: #function, + useClientID: true, + bothClientAndAppID: true, + reCAPTCHAfallback: true, + rceMode: .audit + ) } /** @fn testVerifyPhoneNumberInTestMode @@ -156,8 +161,8 @@ */ func testVerifyPhoneNumberInTestModeFailure() async throws { try await internalTestVerify(errorString: "INVALID_PHONE_NUMBER", - errorCode: AuthErrorCode.invalidPhoneNumber.rawValue, - function: #function, testMode: true) + errorCode: AuthErrorCode.invalidPhoneNumber.rawValue, + function: #function, testMode: true) } /** @fn testVerifyPhoneNumberUIDelegateFirebaseAppIdFlow @@ -173,7 +178,7 @@ */ func testVerifyPhoneNumberUIDelegateFirebaseAppIdWhileClientIdPresentFlow() async throws { try await internalTestVerify(function: #function, useClientID: true, - bothClientAndAppID: true, reCAPTCHAfallback: true) + bothClientAndAppID: true, reCAPTCHAfallback: true) } /** @fn testVerifyPhoneNumberUIDelegateClientIdFlow @@ -590,8 +595,7 @@ reCAPTCHAfallback: Bool = false, forwardingNotification: Bool = true, presenterError: Error? = nil, - rceMode: AuthRecaptchaEnablementStatus = .off - ) async throws { + rceMode: AuthRecaptchaEnablementStatus = .off) async throws { initApp(function, useClientID: useClientID, bothClientAndAppID: bothClientAndAppID, testMode: testMode, forwardingNotification: forwardingNotification) @@ -622,18 +626,18 @@ } } if reCAPTCHAfallback { - // Use fake authURLPresenter so we can test the parameters that get sent to it. + // Use fake authURLPresenter so we can test the parameters that get sent to it. let urlString = errorURLString ?? - PhoneAuthProviderTests.kFakeRedirectURLStringWithReCAPTCHAToken + PhoneAuthProviderTests.kFakeRedirectURLStringWithReCAPTCHAToken let errorTest = errorURLString != nil PhoneAuthProviderTests.auth?.authURLPresenter = - FakePresenter( - urlString: urlString, - clientID: useClientID ? PhoneAuthProviderTests.kFakeClientID : nil, - firebaseAppID: useClientID ? nil : PhoneAuthProviderTests.kFakeFirebaseAppID, - errorTest: errorTest, - presenterError: presenterError - ) + FakePresenter( + urlString: urlString, + clientID: useClientID ? PhoneAuthProviderTests.kFakeClientID : nil, + firebaseAppID: useClientID ? nil : PhoneAuthProviderTests.kFakeFirebaseAppID, + errorTest: errorTest, + presenterError: presenterError + ) } if errorURLString == nil, presenterError == nil { let requestExpectation = self.expectation(description: "verifyRequester") @@ -667,16 +671,20 @@ let uiDelegate = reCAPTCHAfallback ? FakeUIDelegate() : nil // 2. After setting up the parameters, call `verifyPhoneNumber`. do { - // Call the async function to verify the phone number - let verificationID = try await provider.verifyPhoneNumber(kTestPhoneNumber, uiDelegate: uiDelegate) - // Assert that the verificationID matches the expected value - XCTAssertEqual(verificationID, self.kTestVerificationID) + // Call the async function to verify the phone number + let verificationID = try await provider.verifyPhoneNumber( + kTestPhoneNumber, + uiDelegate: uiDelegate + ) + // Assert that the verificationID matches the expected value + XCTAssertEqual(verificationID, kTestVerificationID) } catch { - // If an error occurs, assert that verificationID is nil and the error code matches the expected value + // If an error occurs, assert that verificationID is nil and the error code matches the + // expected value XCTAssertEqual((error as? NSError)?.code, errorCode) } expectation.fulfill() - // Make sure the test waits for expectations to be fulfilled + // Make sure the test waits for expectations to be fulfilled await waitForExpectations(timeout: 5.0) } @@ -729,11 +737,11 @@ } } } - + class FakeAuthRecaptchaVerifier: AuthRecaptchaVerifier { var captchaResponse: String = "captchaResponse" var fakeError: Error? - + override func verify(forceRefresh: Bool, action: AuthRecaptchaAction) async throws -> String { if let error = fakeError { throw error @@ -742,7 +750,6 @@ } } - class FakeTokenManager: AuthAPNSTokenManager { override func getTokenInternal(callback: @escaping (Result) -> Void) { let error = NSError(domain: "dummy domain", code: AuthErrorCode.missingAppToken.rawValue) From 5f230c35c4dcf8ba96854d84d53ab15a3ba1c0e9 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 27 Aug 2024 09:15:39 -0700 Subject: [PATCH 27/43] decouple recaptcha enabled unit tests --- .../Tests/Unit/PhoneAuthProviderTests.swift | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 8ef7bcf1887..d762b5dd59a 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -100,53 +100,6 @@ try await internalTestVerify(function: #function) } - /** - @fn testVerifyPhoneNumberWithRceEnforce - @brief Tests a successful invocation of @c verifyPhoneNumber:completion: with recaptcha enterprise enforced - */ - func testVerifyPhoneNumberWithRceEnforce() async throws { - initApp(#function) - let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) - // TODO: Figure out how to mock objective C's FIRRecaptchaGetToken response - let mockVerifier = FakeAuthRecaptchaVerifier.shared(auth: auth) - let provider = PhoneAuthProvider.provider(auth: auth) - - let requestExpectation = expectation(description: "verifyRequester") - rpcIssuer.rceMode = .enforce - rpcIssuer?.verifyRequester = { request in - XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) - XCTAssertEqual(request.captchaResponse, "mock-token") - XCTAssertEqual(request.recaptchaVersion, "RECAPTCHA_ENTERPRISE") - XCTAssertNil(request.codeIdentity) - requestExpectation.fulfill() - do { - try self.rpcIssuer? - .respond(withJSON: [self.kVerificationIDKey: self.kTestVerificationID]) - } catch { - XCTFail("Failure sending response: \(error)") - } - } - - do { - let verificationID = try await provider.verifyPhoneNumber(kTestPhoneNumber) - XCTAssertEqual(verificationID, kTestVerificationID) - } catch { - XCTFail("Unexpected error: \(error)") - } - - wait(for: [requestExpectation], timeout: 5.0) - } - - func testVerifyPhoneNumberWithRceAuditFallback() async throws { - try await internalTestVerify( - function: #function, - useClientID: true, - bothClientAndAppID: true, - reCAPTCHAfallback: true, - rceMode: .audit - ) - } - /** @fn testVerifyPhoneNumberInTestMode @brief Tests a successful invocation of @c verifyPhoneNumber:completion: when app verification is disabled. From e82baef9bc94a584d4006b724237f53bdfc28bb5 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 27 Aug 2024 09:19:20 -0700 Subject: [PATCH 28/43] restore oauthprovidertests --- .../Tests/Unit/FIROAuthProviderTests.m | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m diff --git a/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m b/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m new file mode 100644 index 00000000000..09c31b0a6ba --- /dev/null +++ b/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m @@ -0,0 +1,193 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import +#if TARGET_OS_IOS + +#import + +@import FirebaseAuth; +@import FirebaseCore; + +/** @var kExpectationTimeout + @brief The maximum time waiting for expectations to fulfill. + */ +static const NSTimeInterval kExpectationTimeout = 1; + +/** @var kFakeAuthorizedDomain + @brief A fake authorized domain for the app. + */ +static NSString *const kFakeAuthorizedDomain = @"test.firebaseapp.com"; + +/** @var kFakeBundleID + @brief A fake bundle ID. + */ +static NSString *const kFakeBundleID = @"com.firebaseapp.example"; + +/** @var kFakeAccessToken + @brief A fake access token for testing. + */ +static NSString *const kFakeAccessToken = @"fakeAccessToken"; + +/** @var kFakeIDToken + @brief A fake ID token for testing. + */ +static NSString *const kFakeIDToken = @"fakeIDToken"; + +/** @var kFakeProviderID + @brief A fake provider ID for testing. + */ +static NSString *const kFakeProviderID = @"fakeProviderID"; + +/** @var kFakeGivenName + @brief A fake given name for testing. + */ +static NSString *const kFakeGivenName = @"fakeGivenName"; + +/** @var kFakeFamilyName + @brief A fake family name for testing. + */ +static NSString *const kFakeFamilyName = @"fakeFamilyName"; + +/** @var kFakeAPIKey + @brief A fake API key. + */ +static NSString *const kFakeAPIKey = @"asdfghjkl"; + +/** @var kFakeEmulatorHost + @brief A fake emulator host. + */ +static NSString *const kFakeEmulatorHost = @"emulatorhost"; + +/** @var kFakeEmulatorPort + @brief A fake emulator port. + */ +static NSString *const kFakeEmulatorPort = @"12345"; + +/** @var kFakeClientID + @brief A fake client ID. + */ +static NSString *const kFakeClientID = @"123456.apps.googleusercontent.com"; + +/** @var kFakeReverseClientID + @brief The dot-reversed version of the fake client ID. + */ +static NSString *const kFakeReverseClientID = @"com.googleusercontent.apps.123456"; + +/** @var kFakeFirebaseAppID + @brief A fake Firebase app ID. + */ +static NSString *const kFakeFirebaseAppID = @"1:123456789:ios:123abc456def"; + +/** @var kFakeEncodedFirebaseAppID + @brief A fake encoded Firebase app ID to be used as a custom URL scheme. + */ +static NSString *const kFakeEncodedFirebaseAppID = @"app-1-123456789-ios-123abc456def"; + +/** @var kFakeTenantID + @brief A fake tenant ID. + */ +static NSString *const kFakeTenantID = @"tenantID"; + +/** @var kFakeOAuthResponseURL + @brief A fake OAuth response URL used in test. + */ +static NSString *const kFakeOAuthResponseURL = @"fakeOAuthResponseURL"; + +/** @var kFakeRedirectURLResponseURL + @brief A fake callback URL (minus the scheme) containing a fake response URL. + */ + +@interface FIROAuthProviderTests : XCTestCase + +@end + +@implementation FIROAuthProviderTests + +/** @fn testObtainingOAuthCredentialNoIDToken + @brief Tests the correct creation of an OAuthCredential without an IDToken. + */ +- (void)testObtainingOAuthCredentialNoIDToken { + FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:kFakeProviderID + accessToken:kFakeAccessToken]; + XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]); + FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; + XCTAssertEqualObjects(OAuthCredential.accessToken, kFakeAccessToken); + XCTAssertEqualObjects(OAuthCredential.provider, kFakeProviderID); + XCTAssertNil(OAuthCredential.IDToken); +} + +/** @fn testObtainingOAuthCredentialWithFullName + @brief Tests the correct creation of an OAuthCredential with a fullName. + */ +- (void)testObtainingOAuthCredentialWithFullName { + NSPersonNameComponents *fullName = [[NSPersonNameComponents alloc] init]; + fullName.givenName = kFakeGivenName; + fullName.familyName = kFakeFamilyName; + FIRAuthCredential *credential = [FIROAuthProvider appleCredentialWithIDToken:kFakeIDToken + rawNonce:nil + fullName:fullName]; + + XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]); + FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; + XCTAssertEqualObjects(OAuthCredential.provider, @"apple.com"); + XCTAssertEqualObjects(OAuthCredential.IDToken, kFakeIDToken); + XCTAssertNil(OAuthCredential.accessToken); +} + +/** @fn testObtainingOAuthCredentialWithIDToken + @brief Tests the correct creation of an OAuthCredential with an IDToken + */ +- (void)testObtainingOAuthCredentialWithIDToken { + FIRAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:kFakeProviderID + IDToken:kFakeIDToken + accessToken:kFakeAccessToken]; + XCTAssertTrue([credential isKindOfClass:[FIROAuthCredential class]]); + FIROAuthCredential *OAuthCredential = (FIROAuthCredential *)credential; + XCTAssertEqualObjects(OAuthCredential.accessToken, kFakeAccessToken); + XCTAssertEqualObjects(OAuthCredential.provider, kFakeProviderID); + XCTAssertEqualObjects(OAuthCredential.IDToken, kFakeIDToken); +} + +/** @fn testGetCredentialWithUIDelegateWithClientIDOnMainThread + @brief Verifies @c getCredentialWithUIDelegate:completion: calls its completion handler on the + main thread. Regression test for firebase/FirebaseUI-iOS#1199. + */ +- (void)testGetCredentialWithUIDelegateWithClientIDOnMainThread { + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + + FIROptions *options = + [[FIROptions alloc] initWithGoogleAppID:@"0:0000000000000:ios:0000000000000000" + GCMSenderID:@"00000000000000000-00000000000-000000000"]; + options.APIKey = kFakeAPIKey; + options.projectID = @"myProjectID"; + options.clientID = kFakeClientID; + [FIRApp configureWithName:@"objAppName" options:options]; + FIRAuth *auth = [FIRAuth authWithApp:[FIRApp appNamed:@"objAppName"]]; + [auth setMainBundleUrlTypes:@[ @{@"CFBundleURLSchemes" : @[ kFakeReverseClientID ]} ]]; + + FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:kFakeProviderID auth:auth]; + [provider getCredentialWithUIDelegate:nil + completion:^(FIRAuthCredential *_Nullable credential, + NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; +} +@end + +#endif // TARGET_OS_IOS \ No newline at end of file From 99167bc578f4d5cc835a7590c4fc5f0ddca6a52b Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 27 Aug 2024 09:19:57 -0700 Subject: [PATCH 29/43] line --- FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m b/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m index 09c31b0a6ba..b5921312caa 100644 --- a/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m +++ b/FirebaseAuth/Tests/Unit/FIROAuthProviderTests.m @@ -190,4 +190,4 @@ - (void)testGetCredentialWithUIDelegateWithClientIDOnMainThread { } @end -#endif // TARGET_OS_IOS \ No newline at end of file +#endif // TARGET_OS_IOS From 9c332e2199031733aedd77fd5fe268aadc17f394 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 15:19:15 -0700 Subject: [PATCH 30/43] fix CI failures --- .../Sources/Swift/Utilities/AuthRecaptchaVerifier.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift index 3e2742eee86..c9c5775102d 100644 --- a/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift +++ b/FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift @@ -33,6 +33,7 @@ } } + @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) enum AuthRecaptchaEnablementStatus: String, CaseIterable { case enforce = "ENFORCE" case audit = "AUDIT" @@ -42,6 +43,7 @@ var stringValue: String { rawValue } } + @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) enum AuthRecaptchaProvider: String, CaseIterable { case password = "EMAIL_PASSWORD_PROVIDER" case phone = "PHONE_PROVIDER" @@ -50,6 +52,7 @@ var stringValue: String { rawValue } } + @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) enum AuthRecaptchaAction: String { case defaultAction case signInWithPassword @@ -175,8 +178,8 @@ var isRecaptchaEnabled = false if let enforcementState = response.enforcementState { for state in enforcementState { - guard let providerString = state["provider"] as? String, - let enforcementString = state["enforcementState"] as? String, + guard let providerString = state["provider"], + let enforcementString = state["enforcementState"], let provider = AuthRecaptchaProvider(rawValue: providerString), let enforcement = AuthRecaptchaEnablementStatus(rawValue: enforcementString) else { continue // Skip to the next state in the loop From db43d9955a296d38b2ecd1a6f825da2bf756e1ed Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 15:51:28 -0700 Subject: [PATCH 31/43] modifying objc methods to use swift implementations --- .../AuthProvider/PhoneAuthProvider.swift | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 81222684b3f..73bd4eafa97 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -72,23 +72,15 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession? = nil, completion: ((_: String?, _: Error?) -> Void)?) { - guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, - urlTypes: auth.mainBundleUrlTypes) else { - fatalError( - "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." - ) - } - kAuthGlobalWorkQueue.async { - Task { - do { - let verificationID = try await self.internalVerify( - phoneNumber: phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession - ) - Auth.wrapMainAsync(callback: completion, withParam: verificationID, error: nil) - } catch { - Auth.wrapMainAsync(callback: completion, withParam: nil, error: error) + Task { + do { + let verificationID = try await verifyPhoneNumber(phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) + await MainActor.run { + completion?(verificationID, nil) + } + } catch { + await MainActor.run { + completion?(nil, error) } } } @@ -136,11 +128,18 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession?, completion: ((_: String?, _: Error?) -> Void)?) { - multiFactorSession?.multiFactorInfo = multiFactorInfo - verifyPhoneNumber(multiFactorInfo.phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession, - completion: completion) + Task { + do { + let verificationID = try await verifyPhoneNumber(with: multiFactorInfo, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) + await MainActor.run { + completion?(verificationID, nil) + } + } catch { + await MainActor.run { + completion?(nil, error) + } + } + } } /// Verify ownership of the second factor phone number by the current user. From b9479faa0f830df50d497145f5b5a399d3c71c0c Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 16:29:02 -0700 Subject: [PATCH 32/43] remove ios only enablement enum --- .../Swift/AuthProvider/PhoneAuthProvider.swift | 12 ++++++++++-- .../Tests/Unit/Fakes/FakeBackendRPCIssuer.swift | 8 ++++---- FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift | 4 +--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 73bd4eafa97..602d60e2d94 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -74,7 +74,11 @@ import Foundation completion: ((_: String?, _: Error?) -> Void)?) { Task { do { - let verificationID = try await verifyPhoneNumber(phoneNumber, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) + let verificationID = try await verifyPhoneNumber( + phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession + ) await MainActor.run { completion?(verificationID, nil) } @@ -130,7 +134,11 @@ import Foundation completion: ((_: String?, _: Error?) -> Void)?) { Task { do { - let verificationID = try await verifyPhoneNumber(with: multiFactorInfo, uiDelegate: uiDelegate, multiFactorSession: multiFactorSession) + let verificationID = try await verifyPhoneNumber( + with: multiFactorInfo, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession + ) await MainActor.run { completion?(verificationID, nil) } diff --git a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift index 9ce47ed7039..82c1a9afd1e 100644 --- a/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift +++ b/FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift @@ -76,7 +76,7 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { var secureTokenNetworkError: NSError? var secureTokenErrorString: String? var recaptchaSiteKey = "projects/fakeProjectId/keys/mockSiteKey" - var rceMode: AuthRecaptchaEnablementStatus = .off + var rceMode: String = "OFF" func asyncCallToURL(with request: T, body: Data?, @@ -112,11 +112,11 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer { } return } else if let _ = request as? GetRecaptchaConfigRequest { - if rceMode != .off { // Check if reCAPTCHA is enabled + if rceMode != "OFF" { // Check if reCAPTCHA is enabled let recaptchaKey = recaptchaSiteKey // iOS key from your config let enforcementState = [ - ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": rceMode.rawValue], - ["provider": "PHONE_PROVIDER", "enforcementState": rceMode.rawValue], + ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": rceMode], + ["provider": "PHONE_PROVIDER", "enforcementState": rceMode], ] guard let _ = try? respond(withJSON: [ "recaptchaKey": recaptchaKey, diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index d762b5dd59a..12ef03489f0 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -547,15 +547,13 @@ bothClientAndAppID: Bool = false, reCAPTCHAfallback: Bool = false, forwardingNotification: Bool = true, - presenterError: Error? = nil, - rceMode: AuthRecaptchaEnablementStatus = .off) async throws { + presenterError: Error? = nil) async throws { initApp(function, useClientID: useClientID, bothClientAndAppID: bothClientAndAppID, testMode: testMode, forwardingNotification: forwardingNotification) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) let provider = PhoneAuthProvider.provider(auth: auth) let expectation = self.expectation(description: function) - rpcIssuer?.rceMode = rceMode if !reCAPTCHAfallback { // Fake out appCredentialManager flow. From a97947938e8581e12f9305b6733629f5e63842e2 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 17:05:34 -0700 Subject: [PATCH 33/43] silence expectations related swift warning --- .../Tests/Unit/PhoneAuthProviderTests.swift | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 12ef03489f0..f25e73d188a 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -80,7 +80,7 @@ } catch { XCTAssertEqual((error as NSError).code, AuthErrorCode.missingPhoneNumber.rawValue) } - await waitForExpectations(timeout: 5) + await fulfillment(of: [expectation], timeout: 5.0) } /** @fn testVerifyInvalidPhoneNumber @@ -553,7 +553,7 @@ forwardingNotification: forwardingNotification) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) let provider = PhoneAuthProvider.provider(auth: auth) - let expectation = self.expectation(description: function) + var expectations = [expectation(description: function)] if !reCAPTCHAfallback { // Fake out appCredentialManager flow. @@ -561,7 +561,7 @@ secret: kTestSecret) } else { // 1. Intercept, handle, and test the projectConfiguration RPC calls. - let projectConfigExpectation = self.expectation(description: "projectConfiguration") + let projectConfigExpectation = expectation(description: "projectConfiguration") rpcIssuer?.projectConfigRequester = { request in XCTAssertEqual(request.apiKey, PhoneAuthProviderTests.kFakeAPIKey) projectConfigExpectation.fulfill() @@ -591,7 +591,8 @@ ) } if errorURLString == nil, presenterError == nil { - let requestExpectation = self.expectation(description: "verifyRequester") + let requestExpectation = expectation(description: "verifyRequester") + expectations.append(requestExpectation) rpcIssuer?.verifyRequester = { request in XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) switch request.codeIdentity { @@ -632,11 +633,11 @@ } catch { // If an error occurs, assert that verificationID is nil and the error code matches the // expected value - XCTAssertEqual((error as? NSError)?.code, errorCode) + XCTAssertEqual((error as NSError).code, errorCode) } - expectation.fulfill() + expectations[0].fulfill() // Make sure the test waits for expectations to be fulfilled - await waitForExpectations(timeout: 5.0) + await fulfillment(of: expectations, timeout: 5.0) } private func initApp(_ functionName: String, From 268f74dcc338d642bd52d068b42eb2073a487e24 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 17:27:26 -0700 Subject: [PATCH 34/43] fix --- FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index f25e73d188a..3cc21b12e71 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -562,6 +562,7 @@ } else { // 1. Intercept, handle, and test the projectConfiguration RPC calls. let projectConfigExpectation = expectation(description: "projectConfiguration") + expectations.append(projectConfigExpectation) rpcIssuer?.projectConfigRequester = { request in XCTAssertEqual(request.apiKey, PhoneAuthProviderTests.kFakeAPIKey) projectConfigExpectation.fulfill() From 40a18e4079cad1dc35e26751367d6c9f2fac6480 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 18:12:18 -0700 Subject: [PATCH 35/43] fix recaptcha config unit tests --- FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift b/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift index df64689c7c0..c0ae14f7018 100644 --- a/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift +++ b/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift @@ -47,8 +47,16 @@ class GetRecaptchaConfigTests: RPCBaseTests { let request = GetRecaptchaConfigRequest(requestConfiguration: makeRequestConfiguration()) rpcIssuer.recaptchaSiteKey = kTestRecaptchaKey + let enforcementMode = "AUDIT" + rpcIssuer.rceMode = enforcementMode let response = try await AuthBackend.call(with: request) XCTAssertEqual(response.recaptchaKey, kTestRecaptchaKey) - XCTAssertNil(response.enforcementState) + XCTAssertEqual( + response.enforcementState, + [ + ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": enforcementMode], + ["provider": "PHONE_PROVIDER", "enforcementState": enforcementMode], + ] + ) } } From fdd4a27a57b3125f659e72a948f68fcaa8a9ae22 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 18:41:39 -0700 Subject: [PATCH 36/43] add recaptcha config unit tests --- .../Tests/Unit/GetRecaptchaConfigTests.swift | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift b/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift index c0ae14f7018..95e5bf39963 100644 --- a/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift +++ b/FirebaseAuth/Tests/Unit/GetRecaptchaConfigTests.swift @@ -39,10 +39,10 @@ class GetRecaptchaConfigTests: RPCBaseTests { ) } - /** @fn testSuccessfulGetRecaptchaConfigRequest - @brief This test simulates a successful @c getRecaptchaConfig Flow. + /** @fn testSuccessfulGetRecaptchaConfigRequestRecaptchaEnabled + @brief This test simulates a successful @c getRecaptchaConfig Flow when recaptcha is enabled. */ - func testSuccessfulGetRecaptchaConfigRequest() async throws { + func testSuccessfulGetRecaptchaConfigRequestRecaptchaEnabled() async throws { let kTestRecaptchaKey = "projects/123/keys/456" let request = GetRecaptchaConfigRequest(requestConfiguration: makeRequestConfiguration()) @@ -59,4 +59,22 @@ class GetRecaptchaConfigTests: RPCBaseTests { ] ) } + + /** @fn testSuccessfulGetRecaptchaConfigRequestRecaptchaDisabled + @brief This test simulates a successful @c getRecaptchaConfig Flow when recaptcha is disabled. + */ + func testSuccessfulGetRecaptchaConfigRequestRecaptchaDisabled() async throws { + let request = GetRecaptchaConfigRequest(requestConfiguration: makeRequestConfiguration()) + let enforcementMode = "OFF" + rpcIssuer.rceMode = enforcementMode + let response = try await AuthBackend.call(with: request) + XCTAssertEqual(response.recaptchaKey, nil) + XCTAssertEqual( + response.enforcementState, + [ + ["provider": "EMAIL_PASSWORD_PROVIDER", "enforcementState": enforcementMode], + ["provider": "PHONE_PROVIDER", "enforcementState": enforcementMode], + ] + ) + } } From bfaaf1d8c699f43052def03d529e96834b0bc901 Mon Sep 17 00:00:00 2001 From: Pragati Date: Tue, 3 Sep 2024 19:02:34 -0700 Subject: [PATCH 37/43] address PR feedback --- .../Swift/AuthProvider/PhoneAuthProvider.swift | 2 +- FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 602d60e2d94..050f267fd8a 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -220,7 +220,7 @@ import Foundation case .enforce: return try await verifyClAndSendVerificationCodeWithRecaptcha( toPhoneNumber: phoneNumber, - retryOnInvalidAppCredential: true, + retryOnInvalidAppCredential: false, multiFactorSession: multiFactorSession, uiDelegate: uiDelegate, recaptchaVerifier: recaptchaVerifier diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 3cc21b12e71..4475e736cf5 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -72,7 +72,6 @@ initApp(#function) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) let provider = PhoneAuthProvider.provider(auth: auth) - let expectation = self.expectation(description: #function) do { _ = try await provider.verifyPhoneNumber("") @@ -80,7 +79,6 @@ } catch { XCTAssertEqual((error as NSError).code, AuthErrorCode.missingPhoneNumber.rawValue) } - await fulfillment(of: [expectation], timeout: 5.0) } /** @fn testVerifyInvalidPhoneNumber @@ -553,7 +551,6 @@ forwardingNotification: forwardingNotification) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) let provider = PhoneAuthProvider.provider(auth: auth) - var expectations = [expectation(description: function)] if !reCAPTCHAfallback { // Fake out appCredentialManager flow. @@ -561,11 +558,8 @@ secret: kTestSecret) } else { // 1. Intercept, handle, and test the projectConfiguration RPC calls. - let projectConfigExpectation = expectation(description: "projectConfiguration") - expectations.append(projectConfigExpectation) rpcIssuer?.projectConfigRequester = { request in XCTAssertEqual(request.apiKey, PhoneAuthProviderTests.kFakeAPIKey) - projectConfigExpectation.fulfill() do { // Response for the underlying VerifyClientRequest RPC call. try self.rpcIssuer?.respond( @@ -592,8 +586,6 @@ ) } if errorURLString == nil, presenterError == nil { - let requestExpectation = expectation(description: "verifyRequester") - expectations.append(requestExpectation) rpcIssuer?.verifyRequester = { request in XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) switch request.codeIdentity { @@ -607,7 +599,6 @@ case .empty: XCTAssertTrue(testMode) } - requestExpectation.fulfill() do { // Response for the underlying SendVerificationCode RPC call. if let errorString { @@ -636,9 +627,6 @@ // expected value XCTAssertEqual((error as NSError).code, errorCode) } - expectations[0].fulfill() - // Make sure the test waits for expectations to be fulfilled - await fulfillment(of: expectations, timeout: 5.0) } private func initApp(_ functionName: String, From 35f5b2fc50944d99b8c99d56892f610df1097ef5 Mon Sep 17 00:00:00 2001 From: Pragati Date: Wed, 4 Sep 2024 14:29:44 -0700 Subject: [PATCH 38/43] add expectations to callbacks fns --- FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 4475e736cf5..07f867b7091 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -534,7 +534,7 @@ } /** @fn testVerifyClient - @brief Tests verifying client before sending verification code. + @brief Tests verifying client before sending verification code. */ private func internalTestVerify(errorString: String? = nil, errorURLString: String? = nil, @@ -551,6 +551,7 @@ forwardingNotification: forwardingNotification) let auth = try XCTUnwrap(PhoneAuthProviderTests.auth) let provider = PhoneAuthProvider.provider(auth: auth) + var expectations: [XCTestExpectation] = [] if !reCAPTCHAfallback { // Fake out appCredentialManager flow. @@ -558,8 +559,11 @@ secret: kTestSecret) } else { // 1. Intercept, handle, and test the projectConfiguration RPC calls. + let projectConfigExpectation = expectation(description: "projectConfiguration") + expectations.append(projectConfigExpectation) rpcIssuer?.projectConfigRequester = { request in XCTAssertEqual(request.apiKey, PhoneAuthProviderTests.kFakeAPIKey) + projectConfigExpectation.fulfill() do { // Response for the underlying VerifyClientRequest RPC call. try self.rpcIssuer?.respond( @@ -586,6 +590,8 @@ ) } if errorURLString == nil, presenterError == nil { + let requestExpectation = expectation(description: "verifyRequester") + expectations.append(requestExpectation) rpcIssuer?.verifyRequester = { request in XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) switch request.codeIdentity { @@ -599,6 +605,7 @@ case .empty: XCTAssertTrue(testMode) } + requestExpectation.fulfill() do { // Response for the underlying SendVerificationCode RPC call. if let errorString { @@ -627,6 +634,7 @@ // expected value XCTAssertEqual((error as NSError).code, errorCode) } + await fulfillment(of: expectations, timeout: 5.0) } private func initApp(_ functionName: String, From d32b3205f89752f6fe57e6c43ee08cee786e6eb7 Mon Sep 17 00:00:00 2001 From: Pragati Date: Wed, 4 Sep 2024 14:30:42 -0700 Subject: [PATCH 39/43] add --- FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 07f867b7091..2d06dcf5018 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -559,7 +559,7 @@ secret: kTestSecret) } else { // 1. Intercept, handle, and test the projectConfiguration RPC calls. - let projectConfigExpectation = expectation(description: "projectConfiguration") + let projectConfigExpectation = self.expectation(description: "projectConfiguration") expectations.append(projectConfigExpectation) rpcIssuer?.projectConfigRequester = { request in XCTAssertEqual(request.apiKey, PhoneAuthProviderTests.kFakeAPIKey) @@ -590,7 +590,7 @@ ) } if errorURLString == nil, presenterError == nil { - let requestExpectation = expectation(description: "verifyRequester") + let requestExpectation = self.expectation(description: "verifyRequester") expectations.append(requestExpectation) rpcIssuer?.verifyRequester = { request in XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) From bcfa8626b4fd5ebf39cccc4c36f84275b0e136ee Mon Sep 17 00:00:00 2001 From: Pragati Date: Wed, 4 Sep 2024 14:33:55 -0700 Subject: [PATCH 40/43] lint --- FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift index 2d06dcf5018..07f867b7091 100644 --- a/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift +++ b/FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift @@ -559,7 +559,7 @@ secret: kTestSecret) } else { // 1. Intercept, handle, and test the projectConfiguration RPC calls. - let projectConfigExpectation = self.expectation(description: "projectConfiguration") + let projectConfigExpectation = expectation(description: "projectConfiguration") expectations.append(projectConfigExpectation) rpcIssuer?.projectConfigRequester = { request in XCTAssertEqual(request.apiKey, PhoneAuthProviderTests.kFakeAPIKey) @@ -590,7 +590,7 @@ ) } if errorURLString == nil, presenterError == nil { - let requestExpectation = self.expectation(description: "verifyRequester") + let requestExpectation = expectation(description: "verifyRequester") expectations.append(requestExpectation) rpcIssuer?.verifyRequester = { request in XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber) From 37176295d4f3821181d67710c8270b90e529eb12 Mon Sep 17 00:00:00 2001 From: Pragati Date: Wed, 4 Sep 2024 14:59:29 -0700 Subject: [PATCH 41/43] change force refresh value --- FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 050f267fd8a..318e2a00663 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -199,7 +199,7 @@ import Foundation } let recaptchaVerifier = AuthRecaptchaVerifier.shared(auth: auth) - try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: true) + try await recaptchaVerifier.retrieveRecaptchaConfig(forceRefresh: false) switch recaptchaVerifier.enablementStatus(forProvider: .phone) { case .off: From 88ac589b55f4a5782553b6c9a5a89c5e84165504 Mon Sep 17 00:00:00 2001 From: Pragati Date: Thu, 5 Sep 2024 15:21:03 -0700 Subject: [PATCH 42/43] remove async implementations --- .../AuthProvider/PhoneAuthProvider.swift | 89 +++++++++---------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 318e2a00663..6746b83c8de 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -72,19 +72,23 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession? = nil, completion: ((_: String?, _: Error?) -> Void)?) { - Task { - do { - let verificationID = try await verifyPhoneNumber( - phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession - ) - await MainActor.run { - completion?(verificationID, nil) - } - } catch { - await MainActor.run { - completion?(nil, error) + guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, + urlTypes: auth.mainBundleUrlTypes) else { + fatalError( + "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." + ) + } + kAuthGlobalWorkQueue.async { + Task { + do { + let verificationID = try await self.internalVerify( + phoneNumber: phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession + ) + Auth.wrapMainAsync(callback: completion, withParam: verificationID, error: nil) + } catch { + Auth.wrapMainAsync(callback: completion, withParam: nil, error: error) } } } @@ -103,19 +107,16 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession? = nil) async throws -> String { - guard AuthWebUtils.isCallbackSchemeRegistered(forCustomURLScheme: callbackScheme, - urlTypes: auth.mainBundleUrlTypes) else { - fatalError( - "Please register custom URL scheme \(callbackScheme) in the app's Info.plist file." - ) - } - - if let verificationID = try await internalVerify(phoneNumber: phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) { - return verificationID - } else { - throw AuthErrorUtils.invalidVerificationIDError(message: "Invalid verification ID") + return try await withCheckedThrowingContinuation { continuation in + self.verifyPhoneNumber(phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) { result, error in + if let error { + continuation.resume(throwing: error) + } else if let result { + continuation.resume(returning: result) + } + } } } @@ -132,22 +133,11 @@ import Foundation uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession?, completion: ((_: String?, _: Error?) -> Void)?) { - Task { - do { - let verificationID = try await verifyPhoneNumber( - with: multiFactorInfo, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession - ) - await MainActor.run { - completion?(verificationID, nil) - } - } catch { - await MainActor.run { - completion?(nil, error) - } - } - } + multiFactorSession?.multiFactorInfo = multiFactorInfo + verifyPhoneNumber(multiFactorInfo.phoneNumber, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession, + completion: completion) } /// Verify ownership of the second factor phone number by the current user. @@ -162,10 +152,17 @@ import Foundation open func verifyPhoneNumber(with multiFactorInfo: PhoneMultiFactorInfo, uiDelegate: AuthUIDelegate? = nil, multiFactorSession: MultiFactorSession?) async throws -> String { - multiFactorSession?.multiFactorInfo = multiFactorInfo - return try await verifyPhoneNumber(multiFactorInfo.phoneNumber, - uiDelegate: uiDelegate, - multiFactorSession: multiFactorSession) + return try await withCheckedThrowingContinuation { continuation in + self.verifyPhoneNumber(with: multiFactorInfo, + uiDelegate: uiDelegate, + multiFactorSession: multiFactorSession) { result, error in + if let error { + continuation.resume(throwing: error) + } else if let result { + continuation.resume(returning: result) + } + } + } } /// Creates an `AuthCredential` for the phone number provider identified by the From 0a4b9bcc6ee8d18a0817cf8e4135d7c2a7438855 Mon Sep 17 00:00:00 2001 From: Pragati Date: Mon, 9 Sep 2024 09:33:54 -0700 Subject: [PATCH 43/43] add comments --- .../AuthProvider/PhoneAuthProvider.swift | 58 ++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index 6746b83c8de..cd2aab02194 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -225,6 +225,17 @@ import Foundation } } + /// Initiates the verification flow by sending a verification code with reCAPTCHA protection to + /// the provided phone number. + /// - Parameters: + /// - phoneNumber: The phone number to which the verification code should be sent. + /// - retryOnInvalidAppCredential: A boolean indicating whether to retry the flow if an + /// AuthErrorCodeInvalidAppCredential error occurs. + /// - uiDelegate: An optional delegate for handling UI events during the verification process. + /// - recaptchaVerifier: An instance of `AuthRecaptchaVerifier` to inject reCAPTCHA fields + /// into the request. + /// - Returns: A string containing the verification ID if the request is successful; otherwise, + /// handles the error and returns nil. private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, uiDelegate: AuthUIDelegate?, @@ -251,12 +262,15 @@ import Foundation } } - /// Starts the flow to verify the client via silent push notification. - /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an - /// AuthErrorCodeInvalidAppCredential error is returned from the backend. - /// - Parameter phoneNumber: The phone number to be verified. - /// - Parameter callback: The callback to be invoked on the global work queue when the flow is - /// finished. + /// Initiates the verification flow by sending a verification code to the provided phone number + /// using a silent push notification. + /// - Parameters: + /// - phoneNumber: The phone number to which the verification code should be sent. + /// - retryOnInvalidAppCredential: A boolean indicating whether to retry the flow if an + /// AuthErrorCodeInvalidAppCredential error occurs. + /// - uiDelegate: An optional delegate for handling UI events during the verification process. + /// - Returns: A string containing the verification ID if the request is successful; otherwise, + /// handles the error and returns nil. private func verifyClAndSendVerificationCode(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, uiDelegate: AuthUIDelegate?) async throws @@ -280,10 +294,19 @@ import Foundation } } - /// Starts the flow to verify the client via silent push notification. - /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an - /// AuthErrorCodeInvalidAppCredential error is returned from the backend. - /// - Parameter phoneNumber: The phone number to be verified. + /// Initiates the verification flow by sending a verification code with reCAPTCHA protection, + /// optionally considering a multi-factor session. + /// - Parameters: + /// - phoneNumber: The phone number to which the verification code should be sent. + /// - retryOnInvalidAppCredential: A boolean indicating whether to retry the flow if an + /// AuthErrorCodeInvalidAppCredential error occurs. + /// - session: An optional `MultiFactorSession` instance to include in the verification flow + /// for multi-factor authentication. + /// - uiDelegate: An optional delegate for handling UI events during the verification process. + /// - recaptchaVerifier: An instance of `AuthRecaptchaVerifier` to inject reCAPTCHA fields + /// into the request. + /// - Returns: A string containing the verification ID or session info if the request is + /// successful; otherwise, handles the error and returns nil. private func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, multiFactorSession session: MultiFactorSession?, @@ -346,10 +369,17 @@ import Foundation } } - /// Starts the flow to verify the client via silent push notification. - /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an - /// AuthErrorCodeInvalidAppCredential error is returned from the backend. - /// - Parameter phoneNumber: The phone number to be verified. + /// Initiates the verification flow by sending a verification code, optionally considering a + /// multi-factor session using silent push notification. + /// - Parameters: + /// - phoneNumber: The phone number to which the verification code should be sent. + /// - retryOnInvalidAppCredential: A boolean indicating whether to retry the flow if an + /// AuthErrorCodeInvalidAppCredential error occurs. + /// - session: An optional `MultiFactorSession` instance to include in the verification flow + /// for multi-factor authentication. + /// - uiDelegate: An optional delegate for handling UI events during the verification process. + /// - Returns: A string containing the verification ID or session info if the request is + /// successful; otherwise, handles the error and returns nil. private func verifyClAndSendVerificationCode(toPhoneNumber phoneNumber: String, retryOnInvalidAppCredential: Bool, multiFactorSession session: MultiFactorSession?,