diff --git a/packages/core/src/auth.test.ts b/packages/core/src/auth.test.ts index 43a4d49b..2526c0e6 100644 --- a/packages/core/src/auth.test.ts +++ b/packages/core/src/auth.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { signInWithEmailAndPassword, createUserWithEmailAndPassword, - signInWithPhoneNumber, + verifyPhoneNumber, confirmPhoneNumber, sendPasswordResetEmail, sendSignInLinkToEmail, @@ -16,7 +16,6 @@ import { vi.mock("firebase/auth", () => ({ signInWithCredential: vi.fn(), createUserWithEmailAndPassword: vi.fn(), - signInWithPhoneNumber: vi.fn(), sendPasswordResetEmail: vi.fn(), sendSignInLinkToEmail: vi.fn(), signInAnonymously: vi.fn(), @@ -26,9 +25,9 @@ vi.mock("firebase/auth", () => ({ credential: vi.fn(), credentialWithLink: vi.fn(), }, - PhoneAuthProvider: { + PhoneAuthProvider: Object.assign(vi.fn(), { credential: vi.fn(), - }, + }), linkWithCredential: vi.fn(), })); @@ -47,15 +46,12 @@ import { EmailAuthProvider, PhoneAuthProvider, createUserWithEmailAndPassword as _createUserWithEmailAndPassword, - signInWithPhoneNumber as _signInWithPhoneNumber, sendPasswordResetEmail as _sendPasswordResetEmail, sendSignInLinkToEmail as _sendSignInLinkToEmail, signInAnonymously as _signInAnonymously, - signInWithRedirect, isSignInWithEmailLink as _isSignInWithEmailLink, UserCredential, Auth, - ConfirmationResult, AuthProvider, TotpSecret, } from "firebase/auth"; @@ -195,7 +191,7 @@ describe("createUserWithEmailAndPassword", () => { const password = "password123"; const credential = EmailAuthProvider.credential(email, password); - vi.mocked(hasBehavior).mockImplementation((ui, behavior) => { + vi.mocked(hasBehavior).mockImplementation((_, behavior) => { if (behavior === "autoUpgradeAnonymousCredential") return true; if (behavior === "requireDisplayName") return false; return false; @@ -221,7 +217,7 @@ describe("createUserWithEmailAndPassword", () => { const password = "password123"; const credential = EmailAuthProvider.credential(email, password); - vi.mocked(hasBehavior).mockImplementation((ui, behavior) => { + vi.mocked(hasBehavior).mockImplementation((_, behavior) => { if (behavior === "autoUpgradeAnonymousCredential") return true; if (behavior === "requireDisplayName") return false; return false; @@ -380,44 +376,54 @@ describe("createUserWithEmailAndPassword", () => { }); }); -describe("signInWithPhoneNumber", () => { +describe("verifyPhoneNumber", () => { beforeEach(() => { vi.clearAllMocks(); }); - it("should update state and call signInWithPhoneNumber successfully", async () => { + it("should update state and call PhoneAuthProvider.verifyPhoneNumber successfully", async () => { const mockUI = createMockUI(); const phoneNumber = "+1234567890"; - const mockRecaptchaVerifier = {} as any; - const mockConfirmationResult = { - verificationId: "test-verification-id", - confirm: vi.fn(), - } as any; - - vi.mocked(_signInWithPhoneNumber).mockResolvedValue(mockConfirmationResult); + const mockAppVerifier = {} as any; + const mockVerificationId = "test-verification-id"; + + const mockVerifyPhoneNumber = vi.fn().mockResolvedValue(mockVerificationId); + vi.mocked(PhoneAuthProvider).mockImplementation( + () => + ({ + verifyPhoneNumber: mockVerifyPhoneNumber, + }) as any + ); - const result = await signInWithPhoneNumber(mockUI, phoneNumber, mockRecaptchaVerifier); + const result = await verifyPhoneNumber(mockUI, phoneNumber, mockAppVerifier); // Verify state management expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]); - // Verify the Firebase function was called with correct parameters - expect(_signInWithPhoneNumber).toHaveBeenCalledWith(mockUI.auth, phoneNumber, mockRecaptchaVerifier); - expect(_signInWithPhoneNumber).toHaveBeenCalledTimes(1); + // Verify the PhoneAuthProvider was created and verifyPhoneNumber was called + expect(PhoneAuthProvider).toHaveBeenCalledWith(mockUI.auth); + expect(mockVerifyPhoneNumber).toHaveBeenCalledWith(phoneNumber, mockAppVerifier); + expect(mockVerifyPhoneNumber).toHaveBeenCalledTimes(1); // Verify the result - expect(result).toEqual(mockConfirmationResult); + expect(result).toEqual(mockVerificationId); }); it("should call handleFirebaseError if an error is thrown", async () => { const mockUI = createMockUI(); const phoneNumber = "+1234567890"; - const mockRecaptchaVerifier = {} as any; + const mockAppVerifier = {} as any; const error = new FirebaseError("auth/invalid-phone-number", "Invalid phone number"); - vi.mocked(_signInWithPhoneNumber).mockRejectedValue(error); + const mockVerifyPhoneNumber = vi.fn().mockRejectedValue(error); + vi.mocked(PhoneAuthProvider).mockImplementation( + () => + ({ + verifyPhoneNumber: mockVerifyPhoneNumber, + }) as any + ); - await signInWithPhoneNumber(mockUI, phoneNumber, mockRecaptchaVerifier); + await verifyPhoneNumber(mockUI, phoneNumber, mockAppVerifier); // Verify error handling expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error); @@ -429,12 +435,18 @@ describe("signInWithPhoneNumber", () => { it("should handle recaptcha verification errors", async () => { const mockUI = createMockUI(); const phoneNumber = "+1234567890"; - const mockRecaptchaVerifier = {} as any; + const mockAppVerifier = {} as any; const error = new Error("reCAPTCHA verification failed"); - vi.mocked(_signInWithPhoneNumber).mockRejectedValue(error); + const mockVerifyPhoneNumber = vi.fn().mockRejectedValue(error); + vi.mocked(PhoneAuthProvider).mockImplementation( + () => + ({ + verifyPhoneNumber: mockVerifyPhoneNumber, + }) as any + ); - await signInWithPhoneNumber(mockUI, phoneNumber, mockRecaptchaVerifier); + await verifyPhoneNumber(mockUI, phoneNumber, mockAppVerifier); // Verify error handling expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error); @@ -453,15 +465,15 @@ describe("confirmPhoneNumber", () => { const mockUI = createMockUI({ auth: { currentUser: null } as Auth, }); - const confirmationResult = { verificationId: "test-verification-id" } as ConfirmationResult; + const verificationId = "test-verification-id"; const verificationCode = "123456"; - const credential = PhoneAuthProvider.credential(confirmationResult.verificationId, verificationCode); + const credential = PhoneAuthProvider.credential(verificationId, verificationCode); vi.mocked(hasBehavior).mockReturnValue(false); vi.mocked(PhoneAuthProvider.credential).mockReturnValue(credential); vi.mocked(_signInWithCredential).mockResolvedValue({ providerId: "phone" } as UserCredential); - const result = await confirmPhoneNumber(mockUI, confirmationResult, verificationCode); + const result = await confirmPhoneNumber(mockUI, verificationId, verificationCode); // Since currentUser is null, the behavior should not called. expect(hasBehavior).toHaveBeenCalledTimes(0); @@ -480,16 +492,16 @@ describe("confirmPhoneNumber", () => { const mockUI = createMockUI({ auth: { currentUser: { isAnonymous: true } } as Auth, }); - const confirmationResult = { verificationId: "test-verification-id" } as ConfirmationResult; + const verificationId = "test-verification-id"; const verificationCode = "123456"; - const credential = PhoneAuthProvider.credential(confirmationResult.verificationId, verificationCode); + const credential = PhoneAuthProvider.credential(verificationId, verificationCode); vi.mocked(hasBehavior).mockReturnValue(true); vi.mocked(PhoneAuthProvider.credential).mockReturnValue(credential); const mockBehavior = vi.fn().mockResolvedValue({ providerId: "phone" } as UserCredential); vi.mocked(getBehavior).mockReturnValue(mockBehavior); - const result = await confirmPhoneNumber(mockUI, confirmationResult, verificationCode); + const result = await confirmPhoneNumber(mockUI, verificationId, verificationCode); expect(hasBehavior).toHaveBeenCalledWith(mockUI, "autoUpgradeAnonymousCredential"); expect(getBehavior).toHaveBeenCalledWith(mockUI, "autoUpgradeAnonymousCredential"); @@ -505,14 +517,14 @@ describe("confirmPhoneNumber", () => { const mockUI = createMockUI({ auth: { currentUser: { isAnonymous: false } } as Auth, }); - const confirmationResult = { verificationId: "test-verification-id" } as ConfirmationResult; + const verificationId = "test-verification-id"; const verificationCode = "123456"; - const credential = PhoneAuthProvider.credential(confirmationResult.verificationId, verificationCode); + const credential = PhoneAuthProvider.credential(verificationId, verificationCode); vi.mocked(PhoneAuthProvider.credential).mockReturnValue(credential); vi.mocked(_signInWithCredential).mockResolvedValue({ providerId: "phone" } as UserCredential); - const result = await confirmPhoneNumber(mockUI, confirmationResult, verificationCode); + const result = await confirmPhoneNumber(mockUI, verificationId, verificationCode); // Behavior should not be called when user is not anonymous expect(hasBehavior).not.toHaveBeenCalled(); @@ -527,14 +539,14 @@ describe("confirmPhoneNumber", () => { const mockUI = createMockUI({ auth: { currentUser: null } as Auth, }); - const confirmationResult = { verificationId: "test-verification-id" } as ConfirmationResult; + const verificationId = "test-verification-id"; const verificationCode = "123456"; - const credential = PhoneAuthProvider.credential(confirmationResult.verificationId, verificationCode); + const credential = PhoneAuthProvider.credential(verificationId, verificationCode); vi.mocked(PhoneAuthProvider.credential).mockReturnValue(credential); vi.mocked(_signInWithCredential).mockResolvedValue({ providerId: "phone" } as UserCredential); - const result = await confirmPhoneNumber(mockUI, confirmationResult, verificationCode); + const result = await confirmPhoneNumber(mockUI, verificationId, verificationCode); // Behavior should not be called when user is null expect(hasBehavior).not.toHaveBeenCalled(); @@ -549,16 +561,16 @@ describe("confirmPhoneNumber", () => { const mockUI = createMockUI({ auth: { currentUser: { isAnonymous: true } } as Auth, }); - const confirmationResult = { verificationId: "test-verification-id" } as ConfirmationResult; + const verificationId = "test-verification-id"; const verificationCode = "123456"; - const credential = PhoneAuthProvider.credential(confirmationResult.verificationId, verificationCode); + const credential = PhoneAuthProvider.credential(verificationId, verificationCode); vi.mocked(hasBehavior).mockReturnValue(true); vi.mocked(PhoneAuthProvider.credential).mockReturnValue(credential); const mockBehavior = vi.fn().mockResolvedValue(undefined); vi.mocked(getBehavior).mockReturnValue(mockBehavior); - await confirmPhoneNumber(mockUI, confirmationResult, verificationCode); + await confirmPhoneNumber(mockUI, verificationId, verificationCode); expect(hasBehavior).toHaveBeenCalledWith(mockUI, "autoUpgradeAnonymousCredential"); expect(getBehavior).toHaveBeenCalledWith(mockUI, "autoUpgradeAnonymousCredential"); @@ -576,14 +588,14 @@ describe("confirmPhoneNumber", () => { const mockUI = createMockUI({ auth: { currentUser: null } as Auth, }); - const confirmationResult = { verificationId: "test-verification-id" } as ConfirmationResult; + const verificationId = "test-verification-id"; const verificationCode = "123456"; const error = new FirebaseError("auth/invalid-verification-code", "Invalid verification code"); vi.mocked(_signInWithCredential).mockRejectedValue(error); - await confirmPhoneNumber(mockUI, confirmationResult, verificationCode); + await confirmPhoneNumber(mockUI, verificationId, verificationCode); expect(handleFirebaseError).toHaveBeenCalledWith(mockUI, error); expect(vi.mocked(mockUI.setState).mock.calls).toEqual([["pending"], ["idle"]]); diff --git a/packages/core/src/auth.ts b/packages/core/src/auth.ts index 13180042..785d2268 100644 --- a/packages/core/src/auth.ts +++ b/packages/core/src/auth.ts @@ -20,18 +20,17 @@ import { sendPasswordResetEmail as _sendPasswordResetEmail, sendSignInLinkToEmail as _sendSignInLinkToEmail, signInAnonymously as _signInAnonymously, - signInWithPhoneNumber as _signInWithPhoneNumber, signInWithCredential as _signInWithCredential, ActionCodeSettings, ApplicationVerifier, AuthProvider, - ConfirmationResult, EmailAuthProvider, linkWithCredential, PhoneAuthProvider, UserCredential, AuthCredential, TotpSecret, + PhoneInfoOptions, } from "firebase/auth"; import QRCode from "qrcode-generator"; import { FirebaseUIConfiguration } from "./config"; @@ -121,14 +120,15 @@ export async function createUserWithEmailAndPassword( } } -export async function signInWithPhoneNumber( +export async function verifyPhoneNumber( ui: FirebaseUIConfiguration, - phoneNumber: string, + phoneNumber: PhoneInfoOptions | string, appVerifier: ApplicationVerifier -): Promise { +): Promise { try { ui.setState("pending"); - return await _signInWithPhoneNumber(ui.auth, phoneNumber, appVerifier); + const provider = new PhoneAuthProvider(ui.auth); + return await provider.verifyPhoneNumber(phoneNumber, appVerifier); } catch (error) { handleFirebaseError(ui, error); } finally { @@ -138,13 +138,13 @@ export async function signInWithPhoneNumber( export async function confirmPhoneNumber( ui: FirebaseUIConfiguration, - confirmationResult: ConfirmationResult, + verificationId: string, verificationCode: string ): Promise { try { ui.setState("pending"); const currentUser = ui.auth.currentUser; - const credential = PhoneAuthProvider.credential(confirmationResult.verificationId, verificationCode); + const credential = PhoneAuthProvider.credential(verificationId, verificationCode); if (currentUser?.isAnonymous && hasBehavior(ui, "autoUpgradeAnonymousCredential")) { const result = await getBehavior(ui, "autoUpgradeAnonymousCredential")(ui, credential); diff --git a/packages/react/src/auth/forms/phone-auth-form.test.tsx b/packages/react/src/auth/forms/phone-auth-form.test.tsx index 0629f4b7..61152e7b 100644 --- a/packages/react/src/auth/forms/phone-auth-form.test.tsx +++ b/packages/react/src/auth/forms/phone-auth-form.test.tsx @@ -40,7 +40,7 @@ vi.mock("@firebase-ui/core", async (importOriginal) => { const mod = await importOriginal(); return { ...mod, - signInWithPhoneNumber: vi.fn(), + verifyPhoneNumber: vi.fn(), confirmPhoneNumber: vi.fn(), formatPhoneNumberWithCountry: vi.fn((phoneNumber, dialCode) => `${dialCode}${phoneNumber}`), }; @@ -69,7 +69,7 @@ vi.mock("~/hooks", async (importOriginal) => { }; }); -import { signInWithPhoneNumber, confirmPhoneNumber } from "@firebase-ui/core"; +import { verifyPhoneNumber, confirmPhoneNumber } from "@firebase-ui/core"; import { createFirebaseUIProvider, createMockUI } from "~/tests/utils"; import { registerLocale } from "@firebase-ui/translations"; import { FirebaseUIProvider } from "~/context"; @@ -102,7 +102,7 @@ describe("usePhoneNumberFormAction", () => { }); it("should return a callback which accepts phone number and recaptcha verifier", async () => { - const signInWithPhoneNumberMock = vi.mocked(signInWithPhoneNumber); + const verifyPhoneNumberMock = vi.mocked(verifyPhoneNumber); const mockUI = createMockUI(); const mockRecaptchaVerifier = { render: vi.fn(), clear: vi.fn(), verify: vi.fn() }; @@ -114,12 +114,12 @@ describe("usePhoneNumberFormAction", () => { await result.current({ phoneNumber: "1234567890", recaptchaVerifier: mockRecaptchaVerifier as any }); }); - expect(signInWithPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), "1234567890", mockRecaptchaVerifier); + expect(verifyPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), "1234567890", mockRecaptchaVerifier); }); - it("should return a confirmation result on success", async () => { - const mockConfirmationResult = { confirm: vi.fn() } as any; - const signInWithPhoneNumberMock = vi.mocked(signInWithPhoneNumber).mockResolvedValue(mockConfirmationResult); + it("should return a verification ID on success", async () => { + const mockVerificationId = "test-verification-id"; + const verifyPhoneNumberMock = vi.mocked(verifyPhoneNumber).mockResolvedValue(mockVerificationId); const mockUI = createMockUI(); const mockRecaptchaVerifier = { render: vi.fn(), clear: vi.fn(), verify: vi.fn() }; @@ -128,18 +128,18 @@ describe("usePhoneNumberFormAction", () => { }); await act(async () => { - const confirmationResult = await result.current({ + const verificationId = await result.current({ phoneNumber: "1234567890", recaptchaVerifier: mockRecaptchaVerifier as any, }); - expect(confirmationResult).toBe(mockConfirmationResult); + expect(verificationId).toBe(mockVerificationId); }); - expect(signInWithPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), "1234567890", mockRecaptchaVerifier); + expect(verifyPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), "1234567890", mockRecaptchaVerifier); }); it("should throw an unknown error when its not a FirebaseUIError", async () => { - const signInWithPhoneNumberMock = vi.mocked(signInWithPhoneNumber).mockRejectedValue(new Error("Unknown error")); + const verifyPhoneNumberMock = vi.mocked(verifyPhoneNumber).mockRejectedValue(new Error("Unknown error")); const mockUI = createMockUI({ locale: registerLocale("es-ES", { @@ -161,7 +161,7 @@ describe("usePhoneNumberFormAction", () => { }); }).rejects.toThrow("Unknown error"); - expect(signInWithPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), "1234567890", mockRecaptchaVerifier); + expect(verifyPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), "1234567890", mockRecaptchaVerifier); }); }); @@ -176,8 +176,8 @@ describe("usePhoneNumberForm", () => { it("should allow the form to be submitted with valid phone number", async () => { const mockUI = createMockUI(); - const mockConfirmationResult = { confirm: vi.fn() } as any; - const signInWithPhoneNumberMock = vi.mocked(signInWithPhoneNumber).mockResolvedValue(mockConfirmationResult); + const mockVerificationId = "test-verification-id"; + const verifyPhoneNumberMock = vi.mocked(verifyPhoneNumber).mockResolvedValue(mockVerificationId); const mockRecaptchaVerifier = { render: vi.fn(), clear: vi.fn(), verify: vi.fn() }; const { result } = renderHook( @@ -199,12 +199,12 @@ describe("usePhoneNumberForm", () => { await result.current.handleSubmit(); }); - expect(signInWithPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), "1234567890", mockRecaptchaVerifier); + expect(verifyPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), "1234567890", mockRecaptchaVerifier); }); it("should not allow the form to be submitted if the form is invalid", async () => { const mockUI = createMockUI(); - const signInWithPhoneNumberMock = vi.mocked(signInWithPhoneNumber); + const verifyPhoneNumberMock = vi.mocked(verifyPhoneNumber); const mockRecaptchaVerifier = { render: vi.fn(), clear: vi.fn(), verify: vi.fn() }; const { result } = renderHook( @@ -229,16 +229,16 @@ describe("usePhoneNumberForm", () => { const fieldMeta = result.current.getFieldMeta("phoneNumber"); expect(fieldMeta?.errors).toBeDefined(); expect(fieldMeta?.errors.length).toBeGreaterThan(0); - expect(signInWithPhoneNumberMock).not.toHaveBeenCalled(); + expect(verifyPhoneNumberMock).not.toHaveBeenCalled(); }); it("should call onSuccess callback when form submission succeeds", async () => { const mockUI = createMockUI(); const mockRecaptchaVerifier = { render: vi.fn(), clear: vi.fn(), verify: vi.fn() }; - const mockConfirmationResult = { confirm: vi.fn() } as any; + const mockVerificationId = "test-verification-id"; const onSuccessMock = vi.fn(); - vi.mocked(signInWithPhoneNumber).mockResolvedValue(mockConfirmationResult); + vi.mocked(verifyPhoneNumber).mockResolvedValue(mockVerificationId); const { result } = renderHook( () => @@ -259,7 +259,7 @@ describe("usePhoneNumberForm", () => { await result.current.handleSubmit(); }); - expect(onSuccessMock).toHaveBeenCalledWith(mockConfirmationResult); + expect(onSuccessMock).toHaveBeenCalledWith(mockVerificationId); }); }); @@ -268,38 +268,38 @@ describe("useVerifyPhoneNumberFormAction", () => { vi.clearAllMocks(); }); - it("should return a callback which accepts confirmation result and code", async () => { + it("should return a callback which accepts verification ID and code", async () => { const confirmPhoneNumberMock = vi.mocked(confirmPhoneNumber); const mockUI = createMockUI(); - const mockConfirmationResult = { confirm: vi.fn() }; + const mockVerificationId = "test-verification-id"; const { result } = renderHook(() => useVerifyPhoneNumberFormAction(), { wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }), }); await act(async () => { - await result.current({ confirmation: mockConfirmationResult as any, code: "123456" }); + await result.current({ verificationId: mockVerificationId, verificationCode: "123456" }); }); - expect(confirmPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), mockConfirmationResult, "123456"); + expect(confirmPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), mockVerificationId, "123456"); }); it("should return a credential on success", async () => { const mockCredential = { credential: true } as unknown as UserCredential; const confirmPhoneNumberMock = vi.mocked(confirmPhoneNumber).mockResolvedValue(mockCredential); const mockUI = createMockUI(); - const mockConfirmationResult = { confirm: vi.fn() }; + const mockVerificationId = "test-verification-id"; const { result } = renderHook(() => useVerifyPhoneNumberFormAction(), { wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }), }); await act(async () => { - const credential = await result.current({ confirmation: mockConfirmationResult as any, code: "123456" }); + const credential = await result.current({ verificationId: mockVerificationId, verificationCode: "123456" }); expect(credential).toBe(mockCredential); }); - expect(confirmPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), mockConfirmationResult, "123456"); + expect(confirmPhoneNumberMock).toHaveBeenCalledWith(expect.any(Object), mockVerificationId, "123456"); }); it("should throw an unknown error when its not a FirebaseUIError", async () => { @@ -313,7 +313,7 @@ describe("useVerifyPhoneNumberFormAction", () => { }), }); - const mockConfirmationResult = { confirm: vi.fn() }; + const mockVerificationId = "test-verification-id"; const { result } = renderHook(() => useVerifyPhoneNumberFormAction(), { wrapper: ({ children }) => createFirebaseUIProvider({ children, ui: mockUI }), @@ -321,11 +321,11 @@ describe("useVerifyPhoneNumberFormAction", () => { await expect(async () => { await act(async () => { - await result.current({ confirmation: mockConfirmationResult as any, code: "123456" }); + await result.current({ verificationId: mockVerificationId, verificationCode: "123456" }); }); }).rejects.toThrow("Unknown error"); - expect(confirmPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), mockConfirmationResult, "123456"); + expect(confirmPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), mockVerificationId, "123456"); }); }); @@ -341,12 +341,12 @@ describe("useVerifyPhoneNumberForm", () => { it("should allow the form to be submitted with valid verification code", async () => { const mockUI = createMockUI(); const confirmPhoneNumberMock = vi.mocked(confirmPhoneNumber); - const mockConfirmationResult = { confirm: vi.fn() } as any; + const mockVerificationId = "test-verification-id"; const { result } = renderHook( () => useVerifyPhoneNumberForm({ - confirmation: mockConfirmationResult, + verificationId: mockVerificationId, onSuccess: vi.fn(), }), { @@ -362,18 +362,18 @@ describe("useVerifyPhoneNumberForm", () => { await result.current.handleSubmit(); }); - expect(confirmPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), mockConfirmationResult, "123456"); + expect(confirmPhoneNumberMock).toHaveBeenCalledWith(mockUI.get(), mockVerificationId, "123456"); }); it("should not allow the form to be submitted if the form is invalid", async () => { const mockUI = createMockUI(); const confirmPhoneNumberMock = vi.mocked(confirmPhoneNumber); - const mockConfirmationResult = { confirm: vi.fn() } as any; + const mockVerificationId = "test-verification-id"; const { result } = renderHook( () => useVerifyPhoneNumberForm({ - confirmation: mockConfirmationResult, + verificationId: mockVerificationId, onSuccess: vi.fn(), }), { @@ -395,7 +395,7 @@ describe("useVerifyPhoneNumberForm", () => { it("should call onSuccess callback when form submission succeeds", async () => { const mockUI = createMockUI(); - const mockConfirmationResult = { confirm: vi.fn() } as any; + const mockVerificationId = "test-verification-id"; const mockCredential = { credential: true } as unknown as UserCredential; const onSuccessMock = vi.fn(); @@ -404,7 +404,7 @@ describe("useVerifyPhoneNumberForm", () => { const { result } = renderHook( () => useVerifyPhoneNumberForm({ - confirmation: mockConfirmationResult, + verificationId: mockVerificationId, onSuccess: onSuccessMock, }), { diff --git a/packages/react/src/auth/forms/phone-auth-form.tsx b/packages/react/src/auth/forms/phone-auth-form.tsx index 826cc4e5..8f9473cd 100644 --- a/packages/react/src/auth/forms/phone-auth-form.tsx +++ b/packages/react/src/auth/forms/phone-auth-form.tsx @@ -17,15 +17,15 @@ "use client"; import { - confirmPhoneNumber, CountryCode, countryData, FirebaseUIError, formatPhoneNumberWithCountry, getTranslation, - signInWithPhoneNumber, + verifyPhoneNumber, + confirmPhoneNumber, } from "@firebase-ui/core"; -import { ConfirmationResult, RecaptchaVerifier, UserCredential } from "firebase/auth"; +import { RecaptchaVerifier, UserCredential } from "firebase/auth"; import { useCallback, useRef, useState } from "react"; import { usePhoneAuthFormSchema, useRecaptchaVerifier, useUI } from "~/hooks"; import { form } from "~/components/form"; @@ -37,7 +37,7 @@ export function usePhoneNumberFormAction() { return useCallback( async ({ phoneNumber, recaptchaVerifier }: { phoneNumber: string; recaptchaVerifier: RecaptchaVerifier }) => { - return await signInWithPhoneNumber(ui, phoneNumber, recaptchaVerifier); + return await verifyPhoneNumber(ui, phoneNumber, recaptchaVerifier); }, [ui] ); @@ -45,7 +45,7 @@ export function usePhoneNumberFormAction() { type UsePhoneNumberForm = { recaptchaVerifier: RecaptchaVerifier; - onSuccess: (confirmationResult: ConfirmationResult) => void; + onSuccess: (verificationId: string) => void; formatPhoneNumber?: (phoneNumber: string) => string; }; @@ -74,7 +74,7 @@ export function usePhoneNumberForm({ recaptchaVerifier, onSuccess, formatPhoneNu } type PhoneNumberFormProps = { - onSubmit: (confirmationResult: ConfirmationResult) => void; + onSubmit: (verificationId: string) => void; }; export function PhoneNumberForm(props: PhoneNumberFormProps) { @@ -133,19 +133,19 @@ export function useVerifyPhoneNumberFormAction() { const ui = useUI(); return useCallback( - async ({ confirmation, code }: { confirmation: ConfirmationResult; code: string }) => { - return await confirmPhoneNumber(ui, confirmation, code); + async ({ verificationId, verificationCode }: { verificationId: string; verificationCode: string }) => { + return await confirmPhoneNumber(ui, verificationId, verificationCode); }, [ui] ); } type UseVerifyPhoneNumberForm = { - confirmation: ConfirmationResult; + verificationId: string; onSuccess: (credential: UserCredential) => void; }; -export function useVerifyPhoneNumberForm({ confirmation, onSuccess }: UseVerifyPhoneNumberForm) { +export function useVerifyPhoneNumberForm({ verificationId, onSuccess }: UseVerifyPhoneNumberForm) { const schema = usePhoneAuthFormSchema().pick({ verificationCode: true }); const action = useVerifyPhoneNumberFormAction(); @@ -158,7 +158,7 @@ export function useVerifyPhoneNumberForm({ confirmation, onSuccess }: UseVerifyP onBlur: schema, onSubmitAsync: async ({ value }) => { try { - const credential = await action({ confirmation, code: value.verificationCode }); + const credential = await action({ verificationId, verificationCode: value.verificationCode }); return onSuccess(credential); } catch (error) { return error instanceof FirebaseUIError ? error.message : String(error); @@ -170,12 +170,12 @@ export function useVerifyPhoneNumberForm({ confirmation, onSuccess }: UseVerifyP type VerifyPhoneNumberFormProps = { onSuccess: (credential: UserCredential) => void; - confirmation: ConfirmationResult; + verificationId: string; }; function VerifyPhoneNumberForm(props: VerifyPhoneNumberFormProps) { const ui = useUI(); - const form = useVerifyPhoneNumberForm({ confirmation: props.confirmation, onSuccess: props.onSuccess }); + const form = useVerifyPhoneNumberForm({ verificationId: props.verificationId, onSuccess: props.onSuccess }); return (
(null); + const [verificationId, setVerificationId] = useState(null); - if (!result) { - return ; + if (!verificationId) { + return ; } return ( { props.onSignIn?.(credential); }}