@@ -72,23 +72,19 @@ import Foundation
7272 uiDelegate: AuthUIDelegate ? = nil ,
7373 multiFactorSession: MultiFactorSession ? = nil ,
7474 completion: ( ( _: String ? , _: Error ? ) -> Void ) ? ) {
75- guard AuthWebUtils . isCallbackSchemeRegistered ( forCustomURLScheme: callbackScheme,
76- urlTypes: auth. mainBundleUrlTypes) else {
77- fatalError (
78- " Please register custom URL scheme \( callbackScheme) in the app's Info.plist file. "
79- )
80- }
81- kAuthGlobalWorkQueue. async {
82- Task {
83- do {
84- let verificationID = try await self . internalVerify (
85- phoneNumber: phoneNumber,
86- uiDelegate: uiDelegate,
87- multiFactorSession: multiFactorSession
88- )
89- Auth . wrapMainAsync ( callback: completion, withParam: verificationID, error: nil )
90- } catch {
91- Auth . wrapMainAsync ( callback: completion, withParam: nil , error: error)
75+ Task {
76+ do {
77+ let verificationID = try await verifyPhoneNumber (
78+ phoneNumber,
79+ uiDelegate: uiDelegate,
80+ multiFactorSession: multiFactorSession
81+ )
82+ await MainActor . run {
83+ completion ? ( verificationID, nil )
84+ }
85+ } catch {
86+ await MainActor . run {
87+ completion ? ( nil , error)
9288 }
9389 }
9490 }
@@ -107,16 +103,19 @@ import Foundation
107103 uiDelegate: AuthUIDelegate ? = nil ,
108104 multiFactorSession: MultiFactorSession ? = nil ) async throws
109105 -> String {
110- return try await withCheckedThrowingContinuation { continuation in
111- self . verifyPhoneNumber ( phoneNumber,
112- uiDelegate: uiDelegate,
113- multiFactorSession: multiFactorSession) { result, error in
114- if let error {
115- continuation. resume ( throwing: error)
116- } else if let result {
117- continuation. resume ( returning: result)
118- }
119- }
106+ guard AuthWebUtils . isCallbackSchemeRegistered ( forCustomURLScheme: callbackScheme,
107+ urlTypes: auth. mainBundleUrlTypes) else {
108+ fatalError (
109+ " Please register custom URL scheme \( callbackScheme) in the app's Info.plist file. "
110+ )
111+ }
112+
113+ if let verificationID = try await internalVerify ( phoneNumber: phoneNumber,
114+ uiDelegate: uiDelegate,
115+ multiFactorSession: multiFactorSession) {
116+ return verificationID
117+ } else {
118+ throw AuthErrorUtils . invalidVerificationIDError ( message: " Invalid verification ID " )
120119 }
121120 }
122121
@@ -133,11 +132,22 @@ import Foundation
133132 uiDelegate: AuthUIDelegate ? = nil ,
134133 multiFactorSession: MultiFactorSession ? ,
135134 completion: ( ( _: String ? , _: Error ? ) -> Void ) ? ) {
136- multiFactorSession? . multiFactorInfo = multiFactorInfo
137- verifyPhoneNumber ( multiFactorInfo. phoneNumber,
138- uiDelegate: uiDelegate,
139- multiFactorSession: multiFactorSession,
140- completion: completion)
135+ Task {
136+ do {
137+ let verificationID = try await verifyPhoneNumber (
138+ with: multiFactorInfo,
139+ uiDelegate: uiDelegate,
140+ multiFactorSession: multiFactorSession
141+ )
142+ await MainActor . run {
143+ completion ? ( verificationID, nil )
144+ }
145+ } catch {
146+ await MainActor . run {
147+ completion ? ( nil , error)
148+ }
149+ }
150+ }
141151 }
142152
143153 /// Verify ownership of the second factor phone number by the current user.
@@ -152,17 +162,10 @@ import Foundation
152162 open func verifyPhoneNumber( with multiFactorInfo: PhoneMultiFactorInfo ,
153163 uiDelegate: AuthUIDelegate ? = nil ,
154164 multiFactorSession: MultiFactorSession ? ) async throws -> String {
155- return try await withCheckedThrowingContinuation { continuation in
156- self . verifyPhoneNumber ( with: multiFactorInfo,
157- uiDelegate: uiDelegate,
158- multiFactorSession: multiFactorSession) { result, error in
159- if let error {
160- continuation. resume ( throwing: error)
161- } else if let result {
162- continuation. resume ( returning: result)
163- }
164- }
165- }
165+ multiFactorSession? . multiFactorInfo = multiFactorInfo
166+ return try await verifyPhoneNumber ( multiFactorInfo. phoneNumber,
167+ uiDelegate: uiDelegate,
168+ multiFactorSession: multiFactorSession)
166169 }
167170
168171 /// Creates an `AuthCredential` for the phone number provider identified by the
@@ -185,7 +188,7 @@ import Foundation
185188 uiDelegate: AuthUIDelegate ? ,
186189 multiFactorSession: MultiFactorSession ? = nil ) async throws
187190 -> String ? {
188- guard phoneNumber. count > 0 else {
191+ guard ! phoneNumber. isEmpty else {
189192 throw AuthErrorUtils . missingPhoneNumberError ( message: nil )
190193 }
191194 guard let manager = auth. notificationManager else {
@@ -194,37 +197,155 @@ import Foundation
194197 guard await manager. checkNotificationForwarding ( ) else {
195198 throw AuthErrorUtils . notificationNotForwardedError ( )
196199 }
197- return try await verifyClAndSendVerificationCode ( toPhoneNumber: phoneNumber,
198- retryOnInvalidAppCredential: true ,
199- multiFactorSession: multiFactorSession,
200- uiDelegate: uiDelegate)
200+
201+ let recaptchaVerifier = AuthRecaptchaVerifier . shared ( auth: auth)
202+ try await recaptchaVerifier. retrieveRecaptchaConfig ( forceRefresh: false )
203+
204+ switch recaptchaVerifier. enablementStatus ( forProvider: . phone) {
205+ case . off:
206+ return try await verifyClAndSendVerificationCode (
207+ toPhoneNumber: phoneNumber,
208+ retryOnInvalidAppCredential: true ,
209+ multiFactorSession: multiFactorSession,
210+ uiDelegate: uiDelegate
211+ )
212+ case . audit:
213+ return try await verifyClAndSendVerificationCodeWithRecaptcha (
214+ toPhoneNumber: phoneNumber,
215+ retryOnInvalidAppCredential: true ,
216+ multiFactorSession: multiFactorSession,
217+ uiDelegate: uiDelegate,
218+ recaptchaVerifier: recaptchaVerifier
219+ )
220+ case . enforce:
221+ return try await verifyClAndSendVerificationCodeWithRecaptcha (
222+ toPhoneNumber: phoneNumber,
223+ retryOnInvalidAppCredential: false ,
224+ multiFactorSession: multiFactorSession,
225+ uiDelegate: uiDelegate,
226+ recaptchaVerifier: recaptchaVerifier
227+ )
228+ }
229+ }
230+
231+ func verifyClAndSendVerificationCodeWithRecaptcha( toPhoneNumber phoneNumber: String ,
232+ retryOnInvalidAppCredential: Bool ,
233+ uiDelegate: AuthUIDelegate ? ,
234+ recaptchaVerifier: AuthRecaptchaVerifier ) async throws
235+ -> String ? {
236+ let request = SendVerificationCodeRequest ( phoneNumber: phoneNumber,
237+ codeIdentity: CodeIdentity . empty,
238+ requestConfiguration: auth
239+ . requestConfiguration)
240+ do {
241+ try await recaptchaVerifier. injectRecaptchaFields (
242+ request: request,
243+ provider: . phone,
244+ action: . sendVerificationCode
245+ )
246+ let response = try await AuthBackend . call ( with: request)
247+ return response. verificationID
248+ } catch {
249+ return try await handleVerifyErrorWithRetry ( error: error,
250+ phoneNumber: phoneNumber,
251+ retryOnInvalidAppCredential: retryOnInvalidAppCredential,
252+ multiFactorSession: nil ,
253+ uiDelegate: uiDelegate)
254+ }
201255 }
202256
203257 /// Starts the flow to verify the client via silent push notification.
204- /// - Parameter retryOnInvalidAppCredential: Whether or not the flow should be retried if an
258+ /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an
205259 /// AuthErrorCodeInvalidAppCredential error is returned from the backend.
206260 /// - Parameter phoneNumber: The phone number to be verified.
207261 /// - Parameter callback: The callback to be invoked on the global work queue when the flow is
208262 /// finished.
209- private func verifyClAndSendVerificationCode( toPhoneNumber phoneNumber: String ,
210- retryOnInvalidAppCredential: Bool ,
211- uiDelegate: AuthUIDelegate ? ) async throws
263+ func verifyClAndSendVerificationCode( toPhoneNumber phoneNumber: String ,
264+ retryOnInvalidAppCredential: Bool ,
265+ uiDelegate: AuthUIDelegate ? ) async throws
212266 -> String ? {
213267 let codeIdentity = try await verifyClient ( withUIDelegate: uiDelegate)
214268 let request = SendVerificationCodeRequest ( phoneNumber: phoneNumber,
215269 codeIdentity: codeIdentity,
216270 requestConfiguration: auth
217271 . requestConfiguration)
218-
219272 do {
220- let response = try await auth . backend . call ( with: request)
273+ let response = try await AuthBackend . call ( with: request)
221274 return response. verificationID
222275 } catch {
223- return try await handleVerifyErrorWithRetry ( error: error,
224- phoneNumber: phoneNumber,
225- retryOnInvalidAppCredential: retryOnInvalidAppCredential,
226- multiFactorSession: nil ,
227- uiDelegate: uiDelegate)
276+ return try await handleVerifyErrorWithRetry (
277+ error: error,
278+ phoneNumber: phoneNumber,
279+ retryOnInvalidAppCredential: retryOnInvalidAppCredential,
280+ multiFactorSession: nil ,
281+ uiDelegate: uiDelegate
282+ )
283+ }
284+ }
285+
286+ /// Starts the flow to verify the client via silent push notification.
287+ /// - Parameter retryOnInvalidAppCredential: Whether of not the flow should be retried if an
288+ /// AuthErrorCodeInvalidAppCredential error is returned from the backend.
289+ /// - Parameter phoneNumber: The phone number to be verified.
290+ private func verifyClAndSendVerificationCodeWithRecaptcha( toPhoneNumber phoneNumber: String ,
291+ retryOnInvalidAppCredential: Bool ,
292+ multiFactorSession session: MultiFactorSession ? ,
293+ uiDelegate: AuthUIDelegate ? ,
294+ recaptchaVerifier: AuthRecaptchaVerifier ) async throws
295+ -> String ? {
296+ if let settings = auth. settings,
297+ settings. isAppVerificationDisabledForTesting {
298+ let request = SendVerificationCodeRequest (
299+ phoneNumber: phoneNumber,
300+ codeIdentity: CodeIdentity . empty,
301+ requestConfiguration: auth. requestConfiguration
302+ )
303+ let response = try await AuthBackend . call ( with: request)
304+ return response. verificationID
305+ }
306+ guard let session else {
307+ return try await verifyClAndSendVerificationCodeWithRecaptcha (
308+ toPhoneNumber: phoneNumber,
309+ retryOnInvalidAppCredential: retryOnInvalidAppCredential,
310+ uiDelegate: uiDelegate,
311+ recaptchaVerifier: recaptchaVerifier
312+ )
313+ }
314+ let startMFARequestInfo = AuthProtoStartMFAPhoneRequestInfo ( phoneNumber: phoneNumber,
315+ codeIdentity: CodeIdentity . empty)
316+ do {
317+ if let idToken = session. idToken {
318+ let request = StartMFAEnrollmentRequest ( idToken: idToken,
319+ enrollmentInfo: startMFARequestInfo,
320+ requestConfiguration: auth. requestConfiguration)
321+ try await recaptchaVerifier. injectRecaptchaFields (
322+ request: request,
323+ provider: . phone,
324+ action: . startMfaEnrollment
325+ )
326+ let response = try await AuthBackend . call ( with: request)
327+ return response. phoneSessionInfo? . sessionInfo
328+ } else {
329+ let request = StartMFASignInRequest ( MFAPendingCredential: session. mfaPendingCredential,
330+ MFAEnrollmentID: session. multiFactorInfo? . uid,
331+ signInInfo: startMFARequestInfo,
332+ requestConfiguration: auth. requestConfiguration)
333+ try await recaptchaVerifier. injectRecaptchaFields (
334+ request: request,
335+ provider: . phone,
336+ action: . startMfaSignin
337+ )
338+ let response = try await AuthBackend . call ( with: request)
339+ return response. responseInfo? . sessionInfo
340+ }
341+ } catch {
342+ return try await handleVerifyErrorWithRetry (
343+ error: error,
344+ phoneNumber: phoneNumber,
345+ retryOnInvalidAppCredential: retryOnInvalidAppCredential,
346+ multiFactorSession: session,
347+ uiDelegate: uiDelegate
348+ )
228349 }
229350 }
230351
@@ -474,8 +595,9 @@ import Foundation
474595 private let auth : Auth
475596 private let callbackScheme : String
476597 private let usingClientIDScheme : Bool
598+ private var recaptchaVerifier : AuthRecaptchaVerifier ?
477599
478- init ( auth: Auth ) {
600+ init ( auth: Auth , recaptchaVerifier : AuthRecaptchaVerifier ? = nil ) {
479601 self . auth = auth
480602 if let clientID = auth. app? . options. clientID {
481603 let reverseClientIDScheme = clientID. components ( separatedBy: " . " ) . reversed ( )
@@ -494,6 +616,7 @@ import Foundation
494616 return
495617 }
496618 callbackScheme = " "
619+ self . recaptchaVerifier = AuthRecaptchaVerifier . shared ( auth: auth)
497620 }
498621
499622 private let kAuthTypeVerifyApp = " verifyApp "
0 commit comments