diff --git a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift index a6d5a6aec75..7d7e24468bc 100644 --- a/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift +++ b/FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift @@ -287,11 +287,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?, - recaptchaVerifier: AuthRecaptchaVerifier) async throws + func verifyClAndSendVerificationCodeWithRecaptcha(toPhoneNumber phoneNumber: String, + retryOnInvalidAppCredential: Bool, + multiFactorSession session: MultiFactorSession?, + uiDelegate: AuthUIDelegate?, + recaptchaVerifier: AuthRecaptchaVerifier) async throws -> String? { if let settings = auth.settings, settings.isAppVerificationDisabledForTesting { diff --git a/FirebaseAuth/Tests/Unit/RPCBaseTests.swift b/FirebaseAuth/Tests/Unit/RPCBaseTests.swift index 009289a1b44..7b8d00fb069 100644 --- a/FirebaseAuth/Tests/Unit/RPCBaseTests.swift +++ b/FirebaseAuth/Tests/Unit/RPCBaseTests.swift @@ -42,6 +42,7 @@ class RPCBaseTests: XCTestCase { let kCreationDateTimeIntervalInSeconds = 1_505_858_500.0 let kLastSignInDateTimeIntervalInSeconds = 1_505_858_583.0 let kTestPhoneNumber = "415-555-1234" + let kIdToken = "FAKE_ID_TOKEN" static let kOAuthSessionID = "sessionID" static let kOAuthRequestURI = "requestURI" let kGoogleIDToken = "GOOGLE_ID_TOKEN" diff --git a/FirebaseAuth/Tests/Unit/StartMFAEnrollmentRequestTests.swift b/FirebaseAuth/Tests/Unit/StartMFAEnrollmentRequestTests.swift index be39a361563..7c269b85f08 100644 --- a/FirebaseAuth/Tests/Unit/StartMFAEnrollmentRequestTests.swift +++ b/FirebaseAuth/Tests/Unit/StartMFAEnrollmentRequestTests.swift @@ -23,16 +23,19 @@ import XCTest @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) class StartMFAEnrollmentRequestTests: RPCBaseTests { let kAPIKey = "APIKey" + let kIDToken = "idToken" + let kTOTPEnrollmentInfo = "totpEnrollmentInfo" + let kPhoneEnrollmentInfo = "enrollmentInfo" + let kPhoneNumber = "phoneNumber" + let kReCAPTCHAToken = "recaptchaToken" + let kCaptchaResponse = "captchaResponse" + let kRecaptchaVersion = "recaptchaVersion" /** @fn testTOTPStartMFAEnrollmentRequest @brief Tests the Start MFA Enrollment using TOTP request. */ func testTOTPStartMFAEnrollmentRequest() async throws { - let kIDToken = "idToken" - let kTOTPEnrollmentInfo = "totpEnrollmentInfo" - let kPhoneEnrollmentInfo = "enrollmentInfo" - let requestConfiguration = AuthRequestConfiguration(apiKey: kAPIKey, appID: "appID") let requestInfo = AuthProtoStartMFATOTPEnrollmentRequestInfo() let request = StartMFAEnrollmentRequest(idToken: kIDToken, @@ -58,4 +61,55 @@ class StartMFAEnrollmentRequestTests: RPCBaseTests { XCTAssertEqual(totpInfo, [:]) XCTAssertNil(requestDictionary[kPhoneEnrollmentInfo]) } + + /** + @fn testPhoneStartMFAEnrollmentRequest + @brief Tests the Start MFA Enrollment using SMS request. + */ + func testPhoneStartMFAEnrollmentInjectRecaptchaFields() async throws { + // created a base startMFAEnrollment Request + let testPhoneNumber = "1234567890" + let testRecaptchaToken = "RECAPTCHA_FAKE_TOKEN" + + let requestConfiguration = AuthRequestConfiguration(apiKey: kAPIKey, appID: "appID") + let smsEnrollmentInfo = AuthProtoStartMFAPhoneRequestInfo( + phoneNumber: testPhoneNumber, + codeIdentity: CodeIdentity.recaptcha(testRecaptchaToken) + ) + let request = StartMFAEnrollmentRequest(idToken: kIDToken, + enrollmentInfo: smsEnrollmentInfo, + requestConfiguration: requestConfiguration) + + // inject reCAPTCHA response + let testRecaptchaResponse = "RECAPTCHA_FAKE_RESPONSE" + let testRecaptchaVersion = "RECAPTCHA_FAKE_ENTERPRISE" + request.injectRecaptchaFields( + recaptchaResponse: testRecaptchaResponse, + recaptchaVersion: testRecaptchaVersion + ) + + let expectedURL = + "https://identitytoolkit.googleapis.com/v2/accounts/mfaEnrollment:start?key=\(kAPIKey)" + + do { + try await checkRequest( + request: request, + expected: expectedURL, + key: kIDToken, + value: kIDToken + ) + } catch { + // Ignore error from missing users array in fake JSON return. + return + } + + let requestDictionary = try XCTUnwrap(rpcIssuer.decodedRequest as? [String: AnyHashable]) + let smsInfo = try XCTUnwrap(requestDictionary["phoneEnrollmentInfo"] as? [String: String]) + XCTAssertEqual(smsInfo[kPhoneNumber], testPhoneNumber) + XCTAssertEqual(smsInfo[kReCAPTCHAToken], testRecaptchaToken) + XCTAssertEqual(smsInfo[kRecaptchaVersion], kRecaptchaVersion) + XCTAssertEqual(smsInfo[kCaptchaResponse], testRecaptchaResponse) + + XCTAssertNil(requestDictionary[kTOTPEnrollmentInfo]) + } } diff --git a/FirebaseAuth/Tests/Unit/StartMFASignInRequestTests.swift b/FirebaseAuth/Tests/Unit/StartMFASignInRequestTests.swift new file mode 100644 index 00000000000..837273c403a --- /dev/null +++ b/FirebaseAuth/Tests/Unit/StartMFASignInRequestTests.swift @@ -0,0 +1,89 @@ +// Copyright 2024 Google LLC +// +// 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 Foundation +import XCTest + +@testable import FirebaseAuth + +/** @class StartMFASignInRequestTests + @brief Tests for @c StartMFASignInRequest + */ +@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) +class StartMFASignInRequestTests: RPCBaseTests { + let kAPIKey = "APIKey" + let kMfaEnrollmentId = "mfaEnrollmentId" + let kTOTPEnrollmentInfo = "totpEnrollmentInfo" + let kPhoneEnrollmentInfo = "enrollmentInfo" + let kPhoneNumber = "phoneNumber" + let kReCAPTCHAToken = "recaptchaToken" + let kCaptchaResponse = "captchaResponse" + let kRecaptchaVersion = "recaptchaVersion" + + /** + @fn testPhoneStartMFASignInRequest + @brief Tests the Start MFA Sign In using SMS request. + */ + func testPhoneStartMFASignInRequest() async throws { + let testPendingCredential = "FAKE_PENDING_CREDENTIAL" + let testEnrollmentID = "FAKE_ENROLLMENT_ID" + let testPhoneNumber = "1234567890" + let testRecaptchaToken = "RECAPTCHA_FAKE_TOKEN" + + let requestConfiguration = AuthRequestConfiguration(apiKey: kAPIKey, appID: "appID") + let smsSignInInfo = AuthProtoStartMFAPhoneRequestInfo( + phoneNumber: testPhoneNumber, + codeIdentity: CodeIdentity.recaptcha(testRecaptchaToken) + ) + + let request = StartMFASignInRequest( + MFAPendingCredential: testPendingCredential, + MFAEnrollmentID: testEnrollmentID, + signInInfo: smsSignInInfo, + requestConfiguration: requestConfiguration + ) + + let expectedURL = + "https://identitytoolkit.googleapis.com/v2/accounts/mfaSignIn:start?key=\(kAPIKey)" + + // inject reCAPTCHA response + let testRecaptchaResponse = "RECAPTCHA_FAKE_RESPONSE" + let testRecaptchaVersion = "RECAPTCHA_FAKE_ENTERPRISE" + request.injectRecaptchaFields( + recaptchaResponse: testRecaptchaResponse, + recaptchaVersion: testRecaptchaVersion + ) + + do { + try await checkRequest( + request: request, + expected: expectedURL, + key: kMfaEnrollmentId, + value: testEnrollmentID + ) + } catch { + // Ignore error from missing users array in fake JSON return. + return + } + + let requestDictionary = try XCTUnwrap(rpcIssuer.decodedRequest as? [String: AnyHashable]) + let smsInfo = try XCTUnwrap(requestDictionary["phoneEnrollmentInfo"] as? [String: String]) + XCTAssertEqual(smsInfo[kPhoneNumber], testPhoneNumber) + XCTAssertEqual(smsInfo[kReCAPTCHAToken], testRecaptchaToken) + XCTAssertEqual(smsInfo[kRecaptchaVersion], kRecaptchaVersion) + XCTAssertEqual(smsInfo[kCaptchaResponse], testRecaptchaResponse) + + XCTAssertNil(requestDictionary[kTOTPEnrollmentInfo]) + } +}