diff --git a/packages/auth/demo/src/index.js b/packages/auth/demo/src/index.js index fb18b4273b8..418ab1101a8 100644 --- a/packages/auth/demo/src/index.js +++ b/packages/auth/demo/src/index.js @@ -2315,7 +2315,9 @@ function initApp() { ); $('#signin-verify-phone-number').click(onSignInVerifyPhoneNumber); - $('#signin-verify-phone-number-webotp').click(onSignInVerifyPhoneNumberWebOTP); + $('#signin-verify-phone-number-webotp').click( + onSignInVerifyPhoneNumberWebOTP + ); $('#signin-confirm-phone-verification').click( onSignInConfirmPhoneVerification ); diff --git a/packages/auth/src/model/public_types.ts b/packages/auth/src/model/public_types.ts index a251380a8eb..134f506910c 100644 --- a/packages/auth/src/model/public_types.ts +++ b/packages/auth/src/model/public_types.ts @@ -602,7 +602,6 @@ export interface ConfirmationResult { ): Promise; confirmed(auth: Auth): Promise; - } /** diff --git a/packages/auth/src/platform_browser/providers/phone.ts b/packages/auth/src/platform_browser/providers/phone.ts index b56c1e2191a..06f3bdbd92d 100644 --- a/packages/auth/src/platform_browser/providers/phone.ts +++ b/packages/auth/src/platform_browser/providers/phone.ts @@ -152,9 +152,7 @@ export class PhoneAuthProvider { return _verifyPhoneNumber( this.auth, phoneOptions, - getModularInstance( - applicationVerifier as ApplicationVerifierInternal - ), + getModularInstance(applicationVerifier as ApplicationVerifierInternal) ); } catch (error) { throw error; diff --git a/packages/auth/src/platform_browser/strategies/phone.ts b/packages/auth/src/platform_browser/strategies/phone.ts index 8161ebe451b..d002922f213 100644 --- a/packages/auth/src/platform_browser/strategies/phone.ts +++ b/packages/auth/src/platform_browser/strategies/phone.ts @@ -71,44 +71,59 @@ class ConfirmationResultImpl implements ConfirmationResult { private readonly onConfirmation: OnConfirmationCallback ) {} - // confirmed method that races confirm and confirmWithWebOTP - confirmed(auth: Auth): Promise { - // Create a manual confirmation promise that resolves when confirm is called - const manualConfirmationPromise: Promise = new Promise((resolve, reject) => { - // Store the original confirm method - const originalConfirm = this.confirm.bind(this); + private confirmInProgress = false; + private confirmResolve: ( + value: UserCredential | PromiseLike + ) => void = () => {}; + private confirmReject: (reason: Error) => void = () => {}; + + // confirm method with minimal changes + async confirm(verificationCode: string): Promise { + this.confirmInProgress = true; + try { + const authCredential = PhoneAuthCredential._fromVerification( + this.verificationId, + verificationCode + ); + const userCredential = await this.onConfirmation(authCredential); + this.confirmResolve(userCredential); // Resolve any waiting confirmed promise + return userCredential; + } catch (error) { + this.confirmReject(error as Error); // Reject any waiting confirmed promise + throw error; + } finally { + this.confirmInProgress = false; + } + } - // Override confirm temporarily - this.confirm = async (verificationCode: string) => { - try { - // Call the original confirm method and resolve the promise if successful - const result = await originalConfirm(verificationCode); - // resolve(result); - return result; - } catch (error) { - // Reject the promise if there's an error - // reject(error); - throw error; - } finally { - // Restore the original confirm method - this.confirm = originalConfirm; + // New confirmed method + confirmed(auth: Auth): Promise { + // If confirm is already in progress, we return a promise that will be resolved + // or rejected by the ongoing confirm operation. + if (this.confirmInProgress) { + return new Promise((resolve, reject) => { + this.confirmResolve = resolve; + this.confirmReject = reject; + }); + } else { + // If confirm is not in progress, we race confirmWithWebOTP with a promise + // that can be resolved or rejected by a future confirm call. + const manualConfirmationPromise = new Promise( + (resolve, reject) => { + this.confirmResolve = resolve; + this.confirmReject = reject; } - }; - }); + ); - // Use Promise.race to create a promise that resolves or rejects as soon as either confirm or confirmWithWebOTP does - return Promise.race([ - manualConfirmationPromise, - this.confirmWithWebOTP(auth, 30) - ]); - } + // Immediately invoke confirmWithWebOTP to start the WebOTP process + const webOTPConfirmationPromise = this.confirmWithWebOTP(auth, 30); - async confirm(verificationCode: string): Promise { - const authCredential = PhoneAuthCredential._fromVerification( - this.verificationId, - verificationCode - ); - return this.onConfirmation(authCredential); + // Race the manual confirmation promise against the WebOTP confirmation promise + return Promise.race([ + manualConfirmationPromise, + webOTPConfirmationPromise + ]); + } } async confirmWithWebOTP( @@ -225,18 +240,18 @@ class ConfirmationResultImpl implements ConfirmationResult { export async function signInWithPhoneNumber( auth: Auth, phoneNumber: string, - appVerifier: ApplicationVerifier, + appVerifier: ApplicationVerifier ): Promise { const authInternal = _castAuth(auth); - const verificationId = await _verifyPhoneNumber( - authInternal, - phoneNumber, - getModularInstance(appVerifier as ApplicationVerifierInternal), - ); - return new ConfirmationResultImpl(verificationId, cred => - signInWithCredential(authInternal, cred) - ); - } + const verificationId = await _verifyPhoneNumber( + authInternal, + phoneNumber, + getModularInstance(appVerifier as ApplicationVerifierInternal) + ); + return new ConfirmationResultImpl(verificationId, cred => + signInWithCredential(authInternal, cred) + ); +} /** * Links the user account with the given phone number. @@ -304,7 +319,7 @@ export async function reauthenticateWithPhoneNumber( export async function _verifyPhoneNumber( auth: AuthInternal, options: PhoneInfoOptions | string, - verifier: ApplicationVerifierInternal, + verifier: ApplicationVerifierInternal ): Promise { const recaptchaToken = await verifier.verify();