Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Auth] Add support for upcoming Recaptcha changes #14201

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FirebaseAuth.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ supports email and password accounts, as well as several 3rd party authenticatio
s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 8.0'
s.dependency 'GoogleUtilities/Environment', '~> 8.0'
s.dependency 'GTMSessionFetcher/Core', '>= 3.4', '< 5.0'
s.ios.dependency 'RecaptchaInterop', '~> 100.0'
s.ios.dependency 'RecaptchaInterop', '~> 101.0'
s.test_spec 'unit' do |unit_tests|
unit_tests.scheme = { :code_coverage => true }
# Unit tests can't run on watchOS.
Expand Down
4 changes: 4 additions & 0 deletions FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Unreleased
- [changed] Using reCAPTCHA Enterprise and Firebase Auth requires reCAPTCHA
Enterprise X.Y.Z or later.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: update with final Recaptcha version

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the Recaptcha non-beta version publishing gates the merge.


# 11.6.0
- [added] Added reCAPTCHA Enterprise support for app verification during phone
authentication for Firebase Authentication (#14114)
Expand Down
88 changes: 0 additions & 88 deletions FirebaseAuth/Sources/ObjC/FIRRecaptchaBridge.m

This file was deleted.

33 changes: 0 additions & 33 deletions FirebaseAuth/Sources/Public/FirebaseAuth/FIRRecaptchaBridge.h

This file was deleted.

1 change: 0 additions & 1 deletion FirebaseAuth/Sources/Public/FirebaseAuth/FirebaseAuth.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,4 @@
#import "FIRGoogleAuthProvider.h"
#import "FIRMultiFactor.h"
#import "FIRPhoneAuthProvider.h"
#import "FIRRecaptchaBridge.h"
#import "FIRTwitterAuthProvider.h"
104 changes: 76 additions & 28 deletions FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll refactor this further in a following PR. It more easily reviewable now as it's a direct port of the ObjC bridge logic.

Original file line number Diff line number Diff line change
Expand Up @@ -125,38 +125,86 @@
// No recaptcha on internal build system.
return actionString
#else
return try await withCheckedThrowingContinuation { continuation in
FIRRecaptchaGetToken(siteKey, actionString,
"NO_RECAPTCHA") { (token: String, error: Error?,
linked: Bool, actionCreated: Bool) in
guard linked else {
continuation.resume(throwing: AuthErrorUtils.recaptchaSDKNotLinkedError())
return
}
guard actionCreated else {
continuation.resume(throwing: AuthErrorUtils.recaptchaActionCreationFailed())
return
}
if let error {
continuation.resume(throwing: error)
return
} else {
if token == "NO_RECAPTCHA" {
AuthLog.logInfo(code: "I-AUT000031",
message: "reCAPTCHA token retrieval failed. NO_RECAPTCHA sent as the fake code.")
} else {
AuthLog.logInfo(
code: "I-AUT000030",
message: "reCAPTCHA token retrieval succeeded."
)
}
continuation.resume(returning: token)
}
}

let (token, error, linked, actionCreated) = await recaptchaToken(
siteKey: siteKey,
actionString: actionString,
fakeToken: "NO_RECAPTCHA"
)

guard linked else {
throw AuthErrorUtils.recaptchaSDKNotLinkedError()
}
guard actionCreated else {
throw AuthErrorUtils.recaptchaActionCreationFailed()
}
if let error {
throw error
}
if token == "NO_RECAPTCHA" {
AuthLog.logInfo(code: "I-AUT000031",
message: "reCAPTCHA token retrieval failed. NO_RECAPTCHA sent as the fake code.")
} else {
AuthLog.logInfo(
code: "I-AUT000030",
message: "reCAPTCHA token retrieval succeeded."
)
}
return token
#endif // !(COCOAPODS || SWIFT_PACKAGE)
}

private static var recaptchaClient: (any RCARecaptchaClientProtocol)?

private func recaptchaToken(siteKey: String,
actionString: String,
fakeToken: String) async -> (token: String, error: Error?,
linked: Bool, actionCreated: Bool) {
if let recaptchaClient {
return await retrieveToken(
actionString: actionString,
fakeToken: fakeToken,
recaptchaClient: recaptchaClient
)
}

if let recaptcha =
NSClassFromString("RecaptchaEnterprise.RCARecaptcha") as? RCARecaptchaProtocol.Type {
do {
// let client = try await recaptcha.fetchClient(withSiteKey: siteKey)
let client = try await recaptcha.getClient(withSiteKey: siteKey)
Comment on lines +174 to +175
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when recaptcha SDK is released:

Suggested change
// let client = try await recaptcha.fetchClient(withSiteKey: siteKey)
let client = try await recaptcha.getClient(withSiteKey: siteKey)
let client = try await recaptcha.fetchClient(withSiteKey: siteKey)

recaptchaClient = client
return await retrieveToken(
actionString: actionString,
fakeToken: fakeToken,
recaptchaClient: client
)
} catch {
return ("", error, true, true)
}
} else {
// RecaptchaEnterprise not linked.
return ("", nil, false, false)
}
}

private func retrieveToken(actionString: String,
fakeToken: String,
recaptchaClient: RCARecaptchaClientProtocol) async -> (token: String,
error: Error?,
linked: Bool,
actionCreated: Bool) {
if let recaptchaAction =
NSClassFromString("RecaptchaEnterprise.RCAAction") as? RCAActionProtocol.Type {
let action = recaptchaAction.init(customAction: actionString)
let token = try? await recaptchaClient.execute(withAction: action)
return (token ?? "NO_RECAPTCHA", nil, true, true)
} else {
// RecaptchaEnterprise not linked.
return ("", nil, false, false)
}
}

func retrieveRecaptchaConfig(forceRefresh: Bool) async throws {
if !forceRefresh {
if let tenantID = auth?.tenantID {
Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ let package = Package(
.package(
url: "https://github.com/google/interop-ios-for-google-sdks.git",
"100.0.0" ..< "101.0.0"
// "101.0.0" ..< "102.0.0"
),
.package(url: "https://github.com/google/app-check.git",
"11.0.1" ..< "12.0.0"),
Expand Down
Loading