From 051c95e9cae80117c07f35e5ca13063be2af7698 Mon Sep 17 00:00:00 2001 From: HassanBahati Date: Fri, 21 Feb 2025 12:57:11 +0300 Subject: [PATCH 1/3] feat(react/auth): add useReauthenticateWithCredentialMutation --- packages/react/src/auth/index.ts | 2 +- ...uthenticateWithCredentialMutation.test.tsx | 165 ++++++++++++++++++ ...useReauthenticateWithCredentialMutation.ts | 29 +++ 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx create mode 100644 packages/react/src/auth/useReauthenticateWithCredentialMutation.ts diff --git a/packages/react/src/auth/index.ts b/packages/react/src/auth/index.ts index 2cfdb487..257b8940 100644 --- a/packages/react/src/auth/index.ts +++ b/packages/react/src/auth/index.ts @@ -46,7 +46,7 @@ export { useDeleteUserMutation } from "./useDeleteUserMutation"; // useLinkWithPopupMutation // useLinkWithRedirectMutation // useReauthenticateWithPhoneNumberMutation -// useReauthenticateWithCredentialMutation +export { useReauthenticateWithCredentialMutation } from "./useReauthenticateWithCredentialMutation"; // useReauthenticateWithPopupMutation // useReauthenticateWithRedirectMutation export { useReloadMutation } from "./useReloadMutation"; diff --git a/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx b/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx new file mode 100644 index 00000000..4dc7934a --- /dev/null +++ b/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx @@ -0,0 +1,165 @@ +import { renderHook, waitFor } from "@testing-library/react"; +import { + createUserWithEmailAndPassword, + signOut, + EmailAuthProvider, + User, +} from "firebase/auth"; +import { useReauthenticateWithCredentialMutation } from "./useReauthenticateWithCredentialMutation"; +import { describe, test, expect, beforeEach, afterEach } from "vitest"; +import { wrapper, queryClient } from "../../utils"; +import { auth, expectFirebaseError, wipeAuth } from "~/testing-utils"; + +describe("useReauthenticateWithCredentialMutation", () => { + let user: User; + + const email = "tqf@invertase.io"; + const password = "TanstackQueryFirebase#123"; + + beforeEach(async () => { + queryClient.clear(); + await wipeAuth(); + const userCredential = await createUserWithEmailAndPassword( + auth, + email, + password + ); + user = userCredential.user; + }); + + afterEach(async () => { + await signOut(auth); + queryClient.clear(); + }); + + test("should successfully reauthenticate with correct credentials", async () => { + const { result } = renderHook( + () => useReauthenticateWithCredentialMutation(), + { wrapper } + ); + + const credential = EmailAuthProvider.credential(email, password); + + result.current.mutate({ + user, + credential, + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.data?.user.email).toBe(email); + }); + + test("should fail with incorrect credentials", async () => { + const { result } = renderHook( + () => useReauthenticateWithCredentialMutation(), + { wrapper } + ); + + const wrongCredential = EmailAuthProvider.credential( + email, + "wrongPassword" + ); + + result.current.mutate({ + user, + credential: wrongCredential, + }); + + await waitFor(() => { + expect(result.current.isError).toBe(true); + }); + + expectFirebaseError(result.current.error, "auth/wrong-password"); + }); + + test("should call onSuccess callback after successful reauthentication", async () => { + let callbackCalled = false; + + const { result } = renderHook( + () => + useReauthenticateWithCredentialMutation({ + onSuccess: () => { + callbackCalled = true; + }, + }), + { wrapper } + ); + + const credential = EmailAuthProvider.credential(email, password); + + result.current.mutate({ + user, + credential, + }); + + await waitFor(() => { + expect(callbackCalled).toBe(true); + expect(result.current.isSuccess).toBe(true); + }); + }); + + test("should call onError callback on authentication failure", async () => { + let errorCode: string | undefined; + + const { result } = renderHook( + () => + useReauthenticateWithCredentialMutation({ + onError: (error) => { + errorCode = error.code; + }, + }), + { wrapper } + ); + + const wrongCredential = EmailAuthProvider.credential( + email, + "wrongPassword" + ); + + result.current.mutate({ + user, + credential: wrongCredential, + }); + + await waitFor(() => { + expect(result.current.isError).toBe(true); + expectFirebaseError(result.current.error, "auth/wrong-password"); + }); + }); + + test("should handle multiple reauthentication attempts", async () => { + const { result } = renderHook( + () => useReauthenticateWithCredentialMutation(), + { wrapper } + ); + + // First attempt - successful + const correctCredential = EmailAuthProvider.credential(email, password); + result.current.mutate({ + user, + credential: correctCredential, + }); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + // Second attempt - with wrong password + const wrongCredential = EmailAuthProvider.credential( + email, + "wrongPassword" + ); + result.current.mutate({ + user, + credential: wrongCredential, + }); + + await waitFor(() => { + expect(result.current.isError).toBe(true); + expectFirebaseError(result.current.error, "auth/wrong-password"); + }); + }); +}); diff --git a/packages/react/src/auth/useReauthenticateWithCredentialMutation.ts b/packages/react/src/auth/useReauthenticateWithCredentialMutation.ts new file mode 100644 index 00000000..92ffd0d1 --- /dev/null +++ b/packages/react/src/auth/useReauthenticateWithCredentialMutation.ts @@ -0,0 +1,29 @@ +import { useMutation, type UseMutationOptions } from "@tanstack/react-query"; +import { + type AuthCredential, + reauthenticateWithCredential, + type AuthError, + type User, + type UserCredential, +} from "firebase/auth"; + +type Variables = { + user: User; + credential: AuthCredential; +}; + +type AuthMutationOptions< + TData = unknown, + TError = Error, + TVariables = void +> = Omit, "mutationFn">; + +export function useReauthenticateWithCredentialMutation( + options?: AuthMutationOptions +) { + return useMutation({ + ...options, + mutationFn: ({ user, credential }) => + reauthenticateWithCredential(user, credential), + }); +} From dcec1364d829b37a80ca594eb49a1058281d824c Mon Sep 17 00:00:00 2001 From: HassanBahati Date: Fri, 21 Feb 2025 12:59:29 +0300 Subject: [PATCH 2/3] _ --- .../src/auth/useReauthenticateWithCredentialMutation.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx b/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx index 4dc7934a..7dc4d16f 100644 --- a/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx +++ b/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx @@ -3,7 +3,7 @@ import { createUserWithEmailAndPassword, signOut, EmailAuthProvider, - User, + type User, } from "firebase/auth"; import { useReauthenticateWithCredentialMutation } from "./useReauthenticateWithCredentialMutation"; import { describe, test, expect, beforeEach, afterEach } from "vitest"; From 73987ab1556872b68ac4eede7ce6aeb884f40ab2 Mon Sep 17 00:00:00 2001 From: HassanBahati Date: Fri, 21 Feb 2025 13:00:57 +0300 Subject: [PATCH 3/3] _ --- .../src/auth/useReauthenticateWithCredentialMutation.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx b/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx index 7dc4d16f..f1ee3094 100644 --- a/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx +++ b/packages/react/src/auth/useReauthenticateWithCredentialMutation.test.tsx @@ -29,7 +29,6 @@ describe("useReauthenticateWithCredentialMutation", () => { afterEach(async () => { await signOut(auth); - queryClient.clear(); }); test("should successfully reauthenticate with correct credentials", async () => {