Skip to content

Commit

Permalink
Recaptcha Unit Tests (#14151)
Browse files Browse the repository at this point in the history
  • Loading branch information
srushtisv authored Nov 21, 2024
1 parent c396f47 commit f36c832
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
80 changes: 80 additions & 0 deletions FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,86 @@
)
}

/// @fn testVerifyPhoneNumberWithRceAudit
/// @brief Tests a successful invocation of @c verifyPhoneNumber with recaptcha enterprise in
/// audit mode
func testVerifyPhoneNumberWithRceAuditSuccess() async throws {
initApp(#function)
let auth = try XCTUnwrap(PhoneAuthProviderTests.auth)
let provider = PhoneAuthProvider.provider(auth: auth)
let mockVerifier = FakeAuthRecaptchaVerifier(captchaResponse: kCaptchaResponse)
AuthRecaptchaVerifier.setShared(mockVerifier, auth: auth)
rpcIssuer.rceMode = "AUDIT"
let requestExpectation = expectation(description: "verifyRequester")
rpcIssuer?.verifyRequester = { request in
XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber)
XCTAssertEqual(request.captchaResponse, self.kCaptchaResponse)
XCTAssertEqual(request.recaptchaVersion, "RECAPTCHA_ENTERPRISE")
XCTAssertEqual(request.codeIdentity, CodeIdentity.empty)
requestExpectation.fulfill()
do {
try self.rpcIssuer?
.respond(withJSON: [self.kVerificationIDKey: self.kTestVerificationID])
} catch {
XCTFail("Failure sending response: \(error)")
}
}
do {
let result = try await provider.verifyClAndSendVerificationCodeWithRecaptcha(
toPhoneNumber: kTestPhoneNumber,
retryOnInvalidAppCredential: false,
uiDelegate: nil,
recaptchaVerifier: mockVerifier
)
XCTAssertEqual(result, kTestVerificationID)
} catch {
XCTFail("Unexpected error")
}
await fulfillment(of: [requestExpectation], timeout: 5.0)
}

/// @fn testVerifyPhoneNumberWithRceAuditInvalidRecaptcha
/// @brief Tests a successful invocation of @c verifyPhoneNumber with recaptcha enterprise in
/// audit mode
func testVerifyPhoneNumberWithRceAuditInvalidRecaptcha() async throws {
initApp(#function)
let auth = try XCTUnwrap(PhoneAuthProviderTests.auth)
let provider = PhoneAuthProvider.provider(auth: auth)
let mockVerifier = FakeAuthRecaptchaVerifier()
AuthRecaptchaVerifier.setShared(mockVerifier, auth: auth)
rpcIssuer.rceMode = "AUDIT"
let requestExpectation = expectation(description: "verifyRequester")
rpcIssuer?.verifyRequester = { request in
XCTAssertEqual(request.phoneNumber, self.kTestPhoneNumber)
XCTAssertEqual(request.captchaResponse, "NO_RECAPTCHA")
XCTAssertEqual(request.recaptchaVersion, "RECAPTCHA_ENTERPRISE")
XCTAssertEqual(request.codeIdentity, CodeIdentity.empty)
requestExpectation.fulfill()
do {
try self.rpcIssuer?
.respond(
serverErrorMessage: "INVALID_RECAPTCHA_TOKEN",
error: AuthErrorUtils.invalidRecaptchaTokenError() as NSError
)
} catch {
XCTFail("Failure sending response: \(error)")
}
}
do {
_ = try await provider.verifyClAndSendVerificationCodeWithRecaptcha(
toPhoneNumber: kTestPhoneNumber,
retryOnInvalidAppCredential: false,
uiDelegate: nil,
recaptchaVerifier: mockVerifier
)
} catch {
let underlyingError = (error as NSError).userInfo[NSUnderlyingErrorKey] as? NSError
let rootError = underlyingError?.userInfo[NSUnderlyingErrorKey] as? NSError
XCTAssertEqual(rootError?.code, AuthErrorCode.invalidRecaptchaToken.code.rawValue)
}
await fulfillment(of: [requestExpectation], timeout: 5.0)
}

/** @fn testVerifyPhoneNumberInTestMode
@brief Tests a successful invocation of @c verifyPhoneNumber:completion: when app verification
is disabled.
Expand Down
1 change: 1 addition & 0 deletions FirebaseAuth/Tests/Unit/RPCBaseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
58 changes: 58 additions & 0 deletions FirebaseAuth/Tests/Unit/StartMFAEnrollmentRequestTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ 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
Expand Down Expand Up @@ -58,4 +65,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])
}
}
89 changes: 89 additions & 0 deletions FirebaseAuth/Tests/Unit/StartMFASignInRequestTests.swift
Original file line number Diff line number Diff line change
@@ -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])
}
}

0 comments on commit f36c832

Please sign in to comment.