|
125 | 125 | // No recaptcha on internal build system. |
126 | 126 | return actionString |
127 | 127 | #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 | | - } |
156 | | - } |
| 128 | + |
| 129 | + let (token, error, linked, actionCreated) = await recaptchaToken(siteKey: siteKey, actionString: actionString, fakeToken: "NO_RECAPTCHA") |
| 130 | + |
| 131 | + guard linked else { |
| 132 | + throw AuthErrorUtils.recaptchaSDKNotLinkedError() |
| 133 | + } |
| 134 | + guard actionCreated else { |
| 135 | + throw AuthErrorUtils.recaptchaActionCreationFailed() |
| 136 | + } |
| 137 | + if let error { |
| 138 | + throw error |
| 139 | + } |
| 140 | + if token == "NO_RECAPTCHA" { |
| 141 | + AuthLog.logInfo(code: "I-AUT000031", |
| 142 | + message: "reCAPTCHA token retrieval failed. NO_RECAPTCHA sent as the fake code.") |
| 143 | + } else { |
| 144 | + AuthLog.logInfo( |
| 145 | + code: "I-AUT000030", |
| 146 | + message: "reCAPTCHA token retrieval succeeded." |
| 147 | + ) |
| 148 | + } |
| 149 | + return token |
157 | 150 | #endif // !(COCOAPODS || SWIFT_PACKAGE) |
158 | 151 | } |
159 | 152 |
|
| 153 | + private static var recaptchaClient: (any RCARecaptchaClientProtocol)? |
| 154 | + |
| 155 | + private func recaptchaToken( |
| 156 | + siteKey: String, |
| 157 | + actionString: String, |
| 158 | + fakeToken: String |
| 159 | + ) async -> (token: String, error: Error?, linked: Bool, actionCreated: Bool) { |
| 160 | + if recaptchaClient != nil { |
| 161 | + return await retrieveToken(actionString: actionString, fakeToken: fakeToken) |
| 162 | + } |
| 163 | + |
| 164 | + if let recaptcha = NSClassFromString("RecaptchaEnterprise.RCARecaptcha") as? RCARecaptchaProtocol.Type { |
| 165 | + do { |
| 166 | + // TODO(ncooke3): This should be `fetchClient(withSiteKey:)`. |
| 167 | + let client = try await recaptcha.getClient(withSiteKey: siteKey) |
| 168 | + recaptchaClient = client |
| 169 | + return await retrieveToken(actionString: actionString, fakeToken: fakeToken) |
| 170 | + } catch { |
| 171 | + return ("", error, true, true); |
| 172 | + } |
| 173 | + } else if let recaptcha = NSClassFromString("Recaptcha") { |
| 174 | + // Fall back to attempting to connect with pre-18.7.0 RecaptchaEnterprise. |
| 175 | + let client = try await recaptcha.getClient(withSiteKey: siteKey) |
| 176 | + recaptchaClient = client |
| 177 | + return await retrieveToken(actionString: actionString, fakeToken: fakeToken) |
| 178 | + } catch { |
| 179 | + return ("", error, true, true); |
| 180 | + } |
| 181 | + } else { |
| 182 | + // RecaptchaEnterprise not linked. |
| 183 | + return ("", nil, false, false) |
| 184 | + } |
| 185 | + } |
| 186 | + |
| 187 | + private func retrieveToken( |
| 188 | + actionString: String, |
| 189 | + fakeToken: String |
| 190 | + ) async -> (token: String, error: Error?, linked: Bool, actionCreated: Bool) { |
| 191 | + let recaptchaAction = ( |
| 192 | + NSClassFromString("RecaptchaEnterprise.RCAAction") ?? NSClassFromString("RecaptchaAction") |
| 193 | + ) as? RCAActionProtocol.Type |
| 194 | + |
| 195 | + guard let recaptchaAction else { |
| 196 | + // RecaptchaEnterprise not linked. |
| 197 | + return ("", nil, false, false) |
| 198 | + } |
| 199 | + |
| 200 | + let action = recaptchaAction.init(customAction: actionString) |
| 201 | + let token = try? await recaptchaClient!.execute(withAction: action) |
| 202 | + return (token ?? "NO_RECAPTCHA", nil, true, true) |
| 203 | + } |
| 204 | + |
160 | 205 | func retrieveRecaptchaConfig(forceRefresh: Bool) async throws { |
161 | 206 | if !forceRefresh { |
162 | 207 | if let tenantID = auth?.tenantID { |
|
0 commit comments