Skip to content

Commit 71f50a1

Browse files
committed
More fixes
1 parent e0f9336 commit 71f50a1

File tree

2 files changed

+127
-136
lines changed

2 files changed

+127
-136
lines changed

FirebaseAuth/Sources/Swift/Auth/Auth.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,8 @@ extension Auth: AuthInterop {
12931293
return false
12941294
}
12951295

1296+
let recaptchaVerifier: AuthRecaptchaVerifier
1297+
12961298
#if os(iOS) && !targetEnvironment(macCatalyst)
12971299

12981300
/// Initializes reCAPTCHA using the settings configured for the project or tenant.
@@ -1315,8 +1317,6 @@ extension Auth: AuthInterop {
13151317
}
13161318
}
13171319

1318-
let recaptchaVerifier: AuthRecaptchaVerifier
1319-
13201320
/// Initializes reCAPTCHA using the settings configured for the project or tenant.
13211321
///
13221322
/// If you change the tenant ID of the `Auth` instance, the configuration will be

FirebaseAuth/Sources/Swift/Utilities/AuthRecaptchaVerifier.swift

Lines changed: 125 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@ import Foundation
1818
import FirebaseAuthInternal
1919
#endif
2020

21-
#if os(iOS)
22-
import RecaptchaInterop
23-
#endif
21+
import RecaptchaInterop
2422

25-
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
23+
@available(iOS 13, *)
2624
class AuthRecaptchaConfig {
2725
var siteKey: String?
2826
let enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus]
@@ -34,7 +32,7 @@ class AuthRecaptchaConfig {
3432
}
3533
}
3634

37-
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
35+
@available(iOS 13, *)
3836
enum AuthRecaptchaEnablementStatus: String, CaseIterable {
3937
case enforce = "ENFORCE"
4038
case audit = "AUDIT"
@@ -44,7 +42,7 @@ enum AuthRecaptchaEnablementStatus: String, CaseIterable {
4442
var stringValue: String { rawValue }
4543
}
4644

47-
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
45+
@available(iOS 13, *)
4846
enum AuthRecaptchaProvider: String, CaseIterable {
4947
case password = "EMAIL_PASSWORD_PROVIDER"
5048
case phone = "PHONE_PROVIDER"
@@ -53,7 +51,7 @@ enum AuthRecaptchaProvider: String, CaseIterable {
5351
var stringValue: String { rawValue }
5452
}
5553

56-
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
54+
@available(iOS 13, *)
5755
enum AuthRecaptchaAction: String {
5856
case defaultAction
5957
case signInWithPassword
@@ -67,16 +65,14 @@ enum AuthRecaptchaAction: String {
6765
var stringValue: String { rawValue }
6866
}
6967

70-
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
68+
@available(iOS 13, *)
7169
class AuthRecaptchaVerifier {
7270
weak var auth: Auth?
7371
private var agentConfig: AuthRecaptchaConfig?
7472
private var tenantConfigs: [String: AuthRecaptchaConfig] = [:]
7573
private var recaptchaClient: RCARecaptchaClientProtocol?
7674
private let kRecaptchaVersion = "RECAPTCHA_ENTERPRISE"
7775

78-
init() {}
79-
8076
func verify(forceRefresh: Bool, action: AuthRecaptchaAction) async throws -> String {
8177
try await retrieveRecaptchaConfig(forceRefresh: forceRefresh)
8278
guard let siteKey = siteKey() else {
@@ -114,154 +110,149 @@ class AuthRecaptchaVerifier {
114110
return token
115111
#endif // !(COCOAPODS || SWIFT_PACKAGE)
116112
}
117-
}
118113

119-
#if os(iOS)
120-
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
121-
extension AuthRecaptchaVerifier {
122-
func siteKey() -> String? {
114+
func enablementStatus(forProvider provider: AuthRecaptchaProvider)
115+
-> AuthRecaptchaEnablementStatus {
116+
if let tenantID = auth?.tenantID,
117+
let tenantConfig = tenantConfigs[tenantID],
118+
let status = tenantConfig.enablementStatus[provider] {
119+
return status
120+
} else if let agentConfig = agentConfig,
121+
let status = agentConfig.enablementStatus[provider] {
122+
return status
123+
} else {
124+
return AuthRecaptchaEnablementStatus.off
125+
}
126+
}
127+
128+
func retrieveRecaptchaConfig(forceRefresh: Bool) async throws {
129+
if !forceRefresh {
123130
if let tenantID = auth?.tenantID {
124-
if let config = tenantConfigs[tenantID] {
125-
return config.siteKey
131+
if tenantConfigs[tenantID] != nil {
132+
return
126133
}
127-
return nil
134+
} else if agentConfig != nil {
135+
return
128136
}
129-
return agentConfig?.siteKey
130137
}
131138

132-
func enablementStatus(forProvider provider: AuthRecaptchaProvider)
133-
-> AuthRecaptchaEnablementStatus {
134-
if let tenantID = auth?.tenantID,
135-
let tenantConfig = tenantConfigs[tenantID],
136-
let status = tenantConfig.enablementStatus[provider] {
137-
return status
138-
} else if let agentConfig = agentConfig,
139-
let status = agentConfig.enablementStatus[provider] {
140-
return status
141-
} else {
142-
return AuthRecaptchaEnablementStatus.off
139+
guard let auth = auth else {
140+
throw AuthErrorUtils.error(code: .recaptchaNotEnabled,
141+
message: "No requestConfiguration for Auth instance")
142+
}
143+
let request = GetRecaptchaConfigRequest(requestConfiguration: auth.requestConfiguration)
144+
let response = try await auth.backend.call(with: request)
145+
AuthLog.logInfo(code: "I-AUT000029", message: "reCAPTCHA config retrieval succeeded.")
146+
try await parseRecaptchaConfigFromResponse(response: response)
147+
}
148+
149+
func injectRecaptchaFields(request: any AuthRPCRequest,
150+
provider: AuthRecaptchaProvider,
151+
action: AuthRecaptchaAction) async throws {
152+
try await retrieveRecaptchaConfig(forceRefresh: false)
153+
if enablementStatus(forProvider: provider) != .off {
154+
let token = try await verify(forceRefresh: false, action: action)
155+
request.injectRecaptchaFields(recaptchaResponse: token, recaptchaVersion: kRecaptchaVersion)
156+
} else {
157+
request.injectRecaptchaFields(recaptchaResponse: nil, recaptchaVersion: kRecaptchaVersion)
158+
}
159+
}
160+
161+
private func siteKey() -> String? {
162+
if let tenantID = auth?.tenantID {
163+
if let config = tenantConfigs[tenantID] {
164+
return config.siteKey
143165
}
166+
return nil
167+
}
168+
return agentConfig?.siteKey
169+
}
170+
171+
private func recaptchaToken(siteKey: String,
172+
actionString: String,
173+
fakeToken: String) async -> (token: String, error: Error?,
174+
linked: Bool, actionCreated: Bool) {
175+
if let recaptchaClient {
176+
return await retrieveToken(
177+
actionString: actionString,
178+
fakeToken: fakeToken,
179+
recaptchaClient: recaptchaClient
180+
)
144181
}
145182

146-
private func recaptchaToken(siteKey: String,
147-
actionString: String,
148-
fakeToken: String) async -> (token: String, error: Error?,
149-
linked: Bool, actionCreated: Bool) {
150-
if let recaptchaClient {
183+
if let recaptcha =
184+
NSClassFromString("RecaptchaEnterprise.RCARecaptcha") as? RCARecaptchaProtocol.Type {
185+
do {
186+
// Note, reCAPTCHA does not support multi-tenancy, so only one site key can be used per
187+
// runtime.
188+
// let client = try await recaptcha.fetchClient(withSiteKey: siteKey)
189+
let client = try await recaptcha.getClient(withSiteKey: siteKey)
190+
recaptchaClient = client
151191
return await retrieveToken(
152192
actionString: actionString,
153193
fakeToken: fakeToken,
154-
recaptchaClient: recaptchaClient
194+
recaptchaClient: client
155195
)
196+
} catch {
197+
return ("", error, true, true)
156198
}
157-
158-
if let recaptcha =
159-
NSClassFromString("RecaptchaEnterprise.RCARecaptcha") as? RCARecaptchaProtocol.Type {
160-
do {
161-
// Note, reCAPTCHA does not support multi-tenancy, so only one site key can be used per
162-
// runtime.
163-
// let client = try await recaptcha.fetchClient(withSiteKey: siteKey)
164-
let client = try await recaptcha.getClient(withSiteKey: siteKey)
165-
recaptchaClient = client
166-
return await retrieveToken(
167-
actionString: actionString,
168-
fakeToken: fakeToken,
169-
recaptchaClient: client
170-
)
171-
} catch {
172-
return ("", error, true, true)
173-
}
174-
} else {
175-
// RecaptchaEnterprise not linked.
176-
return ("", nil, false, false)
177-
}
199+
} else {
200+
// RecaptchaEnterprise not linked.
201+
return ("", nil, false, false)
178202
}
203+
}
179204

180-
private func retrieveToken(actionString: String,
181-
fakeToken: String,
182-
recaptchaClient: RCARecaptchaClientProtocol) async -> (token: String,
183-
error: Error?,
184-
linked: Bool,
185-
actionCreated: Bool) {
186-
if let recaptchaAction =
187-
NSClassFromString("RecaptchaEnterprise.RCAAction") as? RCAActionProtocol.Type {
188-
let action = recaptchaAction.init(customAction: actionString)
189-
let token = try? await recaptchaClient.execute(withAction: action)
190-
return (token ?? "NO_RECAPTCHA", nil, true, true)
191-
} else {
192-
// RecaptchaEnterprise not linked.
193-
return ("", nil, false, false)
194-
}
205+
private func retrieveToken(actionString: String,
206+
fakeToken: String,
207+
recaptchaClient: RCARecaptchaClientProtocol) async -> (token: String,
208+
error: Error?,
209+
linked: Bool,
210+
actionCreated: Bool) {
211+
if let recaptchaAction =
212+
NSClassFromString("RecaptchaEnterprise.RCAAction") as? RCAActionProtocol.Type {
213+
let action = recaptchaAction.init(customAction: actionString)
214+
let token = try? await recaptchaClient.execute(withAction: action)
215+
return (token ?? "NO_RECAPTCHA", nil, true, true)
216+
} else {
217+
// RecaptchaEnterprise not linked.
218+
return ("", nil, false, false)
195219
}
220+
}
196221

197-
func retrieveRecaptchaConfig(forceRefresh: Bool) async throws {
198-
if !forceRefresh {
199-
if let tenantID = auth?.tenantID {
200-
if tenantConfigs[tenantID] != nil {
201-
return
202-
}
203-
} else if agentConfig != nil {
204-
return
222+
private func parseRecaptchaConfigFromResponse(response: GetRecaptchaConfigResponse) async throws {
223+
var enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] = [:]
224+
var isRecaptchaEnabled = false
225+
if let enforcementState = response.enforcementState {
226+
for state in enforcementState {
227+
guard let providerString = state["provider"],
228+
let enforcementString = state["enforcementState"],
229+
let provider = AuthRecaptchaProvider(rawValue: providerString),
230+
let enforcement = AuthRecaptchaEnablementStatus(rawValue: enforcementString) else {
231+
continue // Skip to the next state in the loop
205232
}
206-
}
207-
208-
guard let auth = auth else {
209-
throw AuthErrorUtils.error(code: .recaptchaNotEnabled,
210-
message: "No requestConfiguration for Auth instance")
211-
}
212-
let request = GetRecaptchaConfigRequest(requestConfiguration: auth.requestConfiguration)
213-
let response = try await auth.backend.call(with: request)
214-
AuthLog.logInfo(code: "I-AUT000029", message: "reCAPTCHA config retrieval succeeded.")
215-
try await parseRecaptchaConfigFromResponse(response: response)
216-
}
217-
218-
func parseRecaptchaConfigFromResponse(response: GetRecaptchaConfigResponse) async throws {
219-
var enablementStatus: [AuthRecaptchaProvider: AuthRecaptchaEnablementStatus] = [:]
220-
var isRecaptchaEnabled = false
221-
if let enforcementState = response.enforcementState {
222-
for state in enforcementState {
223-
guard let providerString = state["provider"],
224-
let enforcementString = state["enforcementState"],
225-
let provider = AuthRecaptchaProvider(rawValue: providerString),
226-
let enforcement = AuthRecaptchaEnablementStatus(rawValue: enforcementString) else {
227-
continue // Skip to the next state in the loop
228-
}
229-
enablementStatus[provider] = enforcement
230-
if enforcement != .off {
231-
isRecaptchaEnabled = true
232-
}
233+
enablementStatus[provider] = enforcement
234+
if enforcement != .off {
235+
isRecaptchaEnabled = true
233236
}
234237
}
235-
var siteKey = ""
236-
// Response's site key is of the format projects/<project-id>/keys/<site-key>'
237-
if isRecaptchaEnabled {
238-
if let recaptchaKey = response.recaptchaKey {
239-
let keys = recaptchaKey.components(separatedBy: "/")
240-
if keys.count != 4 {
241-
throw AuthErrorUtils.error(code: .recaptchaNotEnabled, message: "Invalid siteKey")
242-
}
243-
siteKey = keys[3]
238+
}
239+
var siteKey = ""
240+
// Response's site key is of the format projects/<project-id>/keys/<site-key>'
241+
if isRecaptchaEnabled {
242+
if let recaptchaKey = response.recaptchaKey {
243+
let keys = recaptchaKey.components(separatedBy: "/")
244+
if keys.count != 4 {
245+
throw AuthErrorUtils.error(code: .recaptchaNotEnabled, message: "Invalid siteKey")
244246
}
245-
}
246-
let config = AuthRecaptchaConfig(siteKey: siteKey, enablementStatus: enablementStatus)
247-
248-
if let tenantID = auth?.tenantID {
249-
tenantConfigs[tenantID] = config
250-
} else {
251-
agentConfig = config
247+
siteKey = keys[3]
252248
}
253249
}
250+
let config = AuthRecaptchaConfig(siteKey: siteKey, enablementStatus: enablementStatus)
254251

255-
func injectRecaptchaFields(request: any AuthRPCRequest,
256-
provider: AuthRecaptchaProvider,
257-
action: AuthRecaptchaAction) async throws {
258-
try await retrieveRecaptchaConfig(forceRefresh: false)
259-
if enablementStatus(forProvider: provider) != .off {
260-
let token = try await verify(forceRefresh: false, action: action)
261-
request.injectRecaptchaFields(recaptchaResponse: token, recaptchaVersion: kRecaptchaVersion)
262-
} else {
263-
request.injectRecaptchaFields(recaptchaResponse: nil, recaptchaVersion: kRecaptchaVersion)
264-
}
252+
if let tenantID = auth?.tenantID {
253+
tenantConfigs[tenantID] = config
254+
} else {
255+
agentConfig = config
265256
}
266257
}
267-
#endif
258+
}

0 commit comments

Comments
 (0)