From 444e1eaca86a2136889ad097d6152c01d62fa3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 12 Nov 2026 14:02:30 +0100 Subject: [PATCH] fix(auth): replace the page with the sign-in page on sign-out The menu's sign-out pushed the sign-in page onto the history. Back then reopened the page just signed out of, and the edge sent the next person to the sign-in page with that page as `next`, so their first sign-in on the device landed where the last person had been. The sign-out now replaces the page. --- src/hooks/__tests__/use-logout.test.ts | 14 +++++++------- src/hooks/use-auth.ts | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/hooks/__tests__/use-logout.test.ts b/src/hooks/__tests__/use-logout.test.ts index 45fb6b415..b6d0483b9 100644 --- a/src/hooks/__tests__/use-logout.test.ts +++ b/src/hooks/__tests__/use-logout.test.ts @@ -14,7 +14,7 @@ interface LogoutOptions { } const captured: { options: LogoutOptions | null } = { options: null }; -const push = vi.fn(); +const replace = vi.fn(); const clear = vi.fn(); const showErrorToast = vi.fn(); @@ -27,7 +27,7 @@ vi.mock("@tanstack/react-query", () => ({ }, })); vi.mock("next/navigation", () => ({ - useRouter: () => ({ push, replace: vi.fn() }), + useRouter: () => ({ push: vi.fn(), replace }), })); vi.mock("@/lib/i18n/context", () => ({ useTranslations: () => ({ t: (key: string) => key }), @@ -73,7 +73,7 @@ afterEach(() => { }); describe("useLogout", () => { - it("clears the caches and opens the sign-in page once the server signed out", async () => { + it("clears the caches and replaces the page with the sign-in page once the server signed out", async () => { fetchMock.mockResolvedValue(json(200, { data: { loggedOut: true } })); useLogout(); @@ -84,7 +84,7 @@ describe("useLogout", () => { expect.objectContaining({ method: "POST" }), ); expect(clear).toHaveBeenCalledTimes(1); - expect(push).toHaveBeenCalledWith("/auth/login"); + expect(replace).toHaveBeenCalledWith("/auth/login"); expect(showErrorToast).not.toHaveBeenCalled(); }); @@ -137,7 +137,7 @@ describe("useLogout", () => { expect(JSON.parse(String(deleteCall[1]?.body))).toEqual({ endpoint: subscription.endpoint, }); - expect(push).toHaveBeenCalledWith("/auth/login"); + expect(replace).toHaveBeenCalledWith("/auth/login"); }); it("keeps push working when the server refuses the sign-out", async () => { @@ -180,7 +180,7 @@ describe("useLogout", () => { "/api/auth/logout", expect.objectContaining({ method: "POST" }), ); - expect(push).toHaveBeenCalledWith("/auth/login"); + expect(replace).toHaveBeenCalledWith("/auth/login"); }); it("stays on the page and says so when the server refuses", async () => { @@ -191,7 +191,7 @@ describe("useLogout", () => { useLogout(); await settle(captured.options!); - expect(push).not.toHaveBeenCalled(); + expect(replace).not.toHaveBeenCalled(); expect(clear).not.toHaveBeenCalled(); expect(showErrorToast).toHaveBeenCalledWith( "auth.signOutFailed", diff --git a/src/hooks/use-auth.ts b/src/hooks/use-auth.ts index 98d242366..e67541b74 100644 --- a/src/hooks/use-auth.ts +++ b/src/hooks/use-auth.ts @@ -672,7 +672,9 @@ export function useLogout() { mutationFn: signOutRequest, onSuccess: () => { clearCachesForSessionEnd(queryClient); - router.push("/auth/login"); + // Replace, so Back does not reopen the last page signed out, and the + // next sign-in on this device is not sent there. + router.replace("/auth/login"); }, onError: (failure) => showErrorToast(t("auth.signOutFailed"), { cause: failure }),