From e3576b4b3cb0428a6cc738289a66b7951a133320 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 27 Apr 2026 07:10:41 +0900 Subject: fix(auth): redirect to login page on session expiry Previously when the session expired, the API client cleared tokens but the UI displayed "Invalid or expired token" instead of redirecting to the login page. The root cause was that isAuthenticatedAtom was derived from userAtom only as a re-evaluation trigger, while the actual value came from apiClient.isAuthenticated(). On page reload userAtom is null, so setting it to null on session expiry did not trigger a re-render and ProtectedRoute never redirected. Make userAtom (persisted via atomWithStorage) the single source of truth for auth state, derive isAuthenticatedAtom from it, drop the redundant apiClient.isAuthenticated(), and explicitly navigate to /login on session expiry. Also trigger session expiry when a 401 comes back with no refresh token available. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/client/pages/DeckDetailPage.test.tsx | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/client/pages/DeckDetailPage.test.tsx') diff --git a/src/client/pages/DeckDetailPage.test.tsx b/src/client/pages/DeckDetailPage.test.tsx index c473275..a63d8a9 100644 --- a/src/client/pages/DeckDetailPage.test.tsx +++ b/src/client/pages/DeckDetailPage.test.tsx @@ -20,7 +20,6 @@ vi.mock("../api/client", () => ({ apiClient: { login: vi.fn(), logout: vi.fn(), - isAuthenticated: vi.fn(), getTokens: vi.fn(), getAuthHeader: vi.fn(), onSessionExpired: vi.fn(() => vi.fn()), @@ -165,7 +164,6 @@ describe("DeckDetailPage", () => { accessToken: "access-token", refreshToken: "refresh-token", }); - vi.mocked(apiClient.isAuthenticated).mockReturnValue(true); vi.mocked(apiClient.getAuthHeader).mockReturnValue({ Authorization: "Bearer access-token", }); -- cgit v1.3-3-g829e