Skip to content

Commit cae7c38

Browse files
authored
[Auth] Add support for upcoming Recaptcha changes (#14201)
1 parent 0fbc538 commit cae7c38

File tree

7 files changed

+81
-152
lines changed

7 files changed

+81
-152
lines changed

FirebaseAuth.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ supports email and password accounts, as well as several 3rd party authenticatio
6363
s.dependency 'GoogleUtilities/AppDelegateSwizzler', '~> 8.0'
6464
s.dependency 'GoogleUtilities/Environment', '~> 8.0'
6565
s.dependency 'GTMSessionFetcher/Core', '>= 3.4', '< 5.0'
66-
s.ios.dependency 'RecaptchaInterop', '~> 100.0'
66+
s.ios.dependency 'RecaptchaInterop', '~> 101.0'
6767
s.test_spec 'unit' do |unit_tests|
6868
unit_tests.scheme = { :code_coverage => true }
6969
# Unit tests can't run on watchOS.

FirebaseAuth/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Unreleased
2+
- [changed] Using reCAPTCHA Enterprise and Firebase Auth requires reCAPTCHA
3+
Enterprise 18.7.0 or later.
4+
15
# 11.8.0
26
- [added] Added `ActionCodeSettings.linkDomain` to customize the Firebase Hosting link domain
37
that is used in out-of-band email action flows.

FirebaseAuth/Sources/ObjC/FIRRecaptchaBridge.m

Lines changed: 0 additions & 88 deletions
This file was deleted.

FirebaseAuth/Sources/Public/FirebaseAuth/FIRRecaptchaBridge.h

Lines changed: 0 additions & 33 deletions
This file was deleted.

FirebaseAuth/Sources/Public/FirebaseAuth/FirebaseAuth.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,4 @@
2626
#import "FIRGoogleAuthProvider.h"
2727
#import "FIRMultiFactor.h"
2828
#import "FIRPhoneAuthProvider.h"
29-
#import "FIRRecaptchaBridge.h"
3029
#import "FIRTwitterAuthProvider.h"

FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -125,38 +125,85 @@
125125
// No recaptcha on internal build system.
126126
return actionString
127127
#else
128-
return try await withCheckedThrowingContinuation { continuation in
129-
FIRRecaptchaGetToken(siteKey, actionString,
130-
"NO_RECAPTCHA") { (token: String, error: Error?,
131-
linked: Bool, actionCreated: Bool) in
132-
guard linked else {
133-
continuation.resume(throwing: AuthErrorUtils.recaptchaSDKNotLinkedError())
134-
return
135-
}
136-
guard actionCreated else {
137-
continuation.resume(throwing: AuthErrorUtils.recaptchaActionCreationFailed())
138-
return
139-
}
140-
if let error {
141-
continuation.resume(throwing: error)
142-
return
143-
} else {
144-
if token == "NO_RECAPTCHA" {
145-
AuthLog.logInfo(code: "I-AUT000031",
146-
message: "reCAPTCHA token retrieval failed. NO_RECAPTCHA sent as the fake code.")
147-
} else {
148-
AuthLog.logInfo(
149-
code: "I-AUT000030",
150-
message: "reCAPTCHA token retrieval succeeded."
151-
)
152-
}
153-
continuation.resume(returning: token)
154-
}
155-
}
128+
129+
let (token, error, linked, actionCreated) = await recaptchaToken(
130+
siteKey: siteKey,
131+
actionString: actionString,
132+
fakeToken: "NO_RECAPTCHA"
133+
)
134+
135+
guard linked else {
136+
throw AuthErrorUtils.recaptchaSDKNotLinkedError()
137+
}
138+
guard actionCreated else {
139+
throw AuthErrorUtils.recaptchaActionCreationFailed()
140+
}
141+
if let error {
142+
throw error
156143
}
144+
if token == "NO_RECAPTCHA" {
145+
AuthLog.logInfo(code: "I-AUT000031",
146+
message: "reCAPTCHA token retrieval failed. NO_RECAPTCHA sent as the fake code.")
147+
} else {
148+
AuthLog.logInfo(
149+
code: "I-AUT000030",
150+
message: "reCAPTCHA token retrieval succeeded."
151+
)
152+
}
153+
return token
157154
#endif // !(COCOAPODS || SWIFT_PACKAGE)
158155
}
159156

157+
private static var recaptchaClient: (any RCARecaptchaClientProtocol)?
158+
159+
private func recaptchaToken(siteKey: String,
160+
actionString: String,
161+
fakeToken: String) async -> (token: String, error: Error?,
162+
linked: Bool, actionCreated: Bool) {
163+
if let recaptchaClient {
164+
return await retrieveToken(
165+
actionString: actionString,
166+
fakeToken: fakeToken,
167+
recaptchaClient: recaptchaClient
168+
)
169+
}
170+
171+
if let recaptcha =
172+
NSClassFromString("RecaptchaEnterprise.RCARecaptcha") as? RCARecaptchaProtocol.Type {
173+
do {
174+
let client = try await recaptcha.fetchClient(withSiteKey: siteKey)
175+
recaptchaClient = client
176+
return await retrieveToken(
177+
actionString: actionString,
178+
fakeToken: fakeToken,
179+
recaptchaClient: client
180+
)
181+
} catch {
182+
return ("", error, true, true)
183+
}
184+
} else {
185+
// RecaptchaEnterprise not linked.
186+
return ("", nil, false, false)
187+
}
188+
}
189+
190+
private func retrieveToken(actionString: String,
191+
fakeToken: String,
192+
recaptchaClient: RCARecaptchaClientProtocol) async -> (token: String,
193+
error: Error?,
194+
linked: Bool,
195+
actionCreated: Bool) {
196+
if let recaptchaAction =
197+
NSClassFromString("RecaptchaEnterprise.RCAAction") as? RCAActionProtocol.Type {
198+
let action = recaptchaAction.init(customAction: actionString)
199+
let token = try? await recaptchaClient.execute(withAction: action)
200+
return (token ?? "NO_RECAPTCHA", nil, true, true)
201+
} else {
202+
// RecaptchaEnterprise not linked.
203+
return ("", nil, false, false)
204+
}
205+
}
206+
160207
func retrieveRecaptchaConfig(forceRefresh: Bool) async throws {
161208
if !forceRefresh {
162209
if let tenantID = auth?.tenantID {

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ let package = Package(
168168
),
169169
.package(
170170
url: "https://github.com/google/interop-ios-for-google-sdks.git",
171-
"100.0.0" ..< "101.0.0"
171+
"101.0.0" ..< "102.0.0"
172172
),
173173
.package(url: "https://github.com/google/app-check.git",
174174
"11.0.1" ..< "12.0.0"),

0 commit comments

Comments
 (0)