diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-01 21:19:30 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-01 21:19:30 +0900 |
| commit | bf286c11d3244afb5132271dac656109934150e0 (patch) | |
| tree | 7188425dbe9652c11bd85bb653bd32ee5d49bd3c /src/client/stores | |
| parent | 803b3dd4a87ba2711605b565ee1eaf5992daf267 (diff) | |
| download | kioku-bf286c11d3244afb5132271dac656109934150e0.tar.gz kioku-bf286c11d3244afb5132271dac656109934150e0.tar.zst kioku-bf286c11d3244afb5132271dac656109934150e0.zip | |
fix(auth): add automatic token refresh on 401 responses
The client session was too short because access tokens (15 min) weren't
being automatically refreshed using the refresh token (7 days). Now the
ApiClient intercepts 401 responses, attempts token refresh, and retries
the original request. This extends effective session duration to 7 days.
Closes #6
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/client/stores')
| -rw-r--r-- | src/client/stores/sync.test.tsx | 3 | ||||
| -rw-r--r-- | src/client/stores/sync.tsx | 19 |
2 files changed, 7 insertions, 15 deletions
diff --git a/src/client/stores/sync.test.tsx b/src/client/stores/sync.test.tsx index fee79d7..20de69d 100644 --- a/src/client/stores/sync.test.tsx +++ b/src/client/stores/sync.test.tsx @@ -16,6 +16,9 @@ global.fetch = mockFetch; vi.mock("../api/client", () => ({ apiClient: { getAuthHeader: vi.fn(() => ({ Authorization: "Bearer token" })), + authenticatedFetch: vi.fn((input: RequestInfo | URL, init?: RequestInit) => + mockFetch(input, init), + ), }, })); diff --git a/src/client/stores/sync.tsx b/src/client/stores/sync.tsx index aea5c16..9b46302 100644 --- a/src/client/stores/sync.tsx +++ b/src/client/stores/sync.tsx @@ -108,15 +108,9 @@ interface PullResponse { } async function pushToServer(data: SyncPushData): Promise<SyncPushResult> { - const authHeader = apiClient.getAuthHeader(); - if (!authHeader) { - throw new Error("Not authenticated"); - } - - const res = await fetch("/api/sync/push", { + const res = await apiClient.authenticatedFetch("/api/sync/push", { method: "POST", headers: { - ...authHeader, "Content-Type": "application/json", }, body: JSON.stringify(data), @@ -135,14 +129,9 @@ async function pushToServer(data: SyncPushData): Promise<SyncPushResult> { async function pullFromServer( lastSyncVersion: number, ): Promise<SyncPullResult> { - const authHeader = apiClient.getAuthHeader(); - if (!authHeader) { - throw new Error("Not authenticated"); - } - - const res = await fetch(`/api/sync/pull?lastSyncVersion=${lastSyncVersion}`, { - headers: authHeader, - }); + const res = await apiClient.authenticatedFetch( + `/api/sync/pull?lastSyncVersion=${lastSyncVersion}`, + ); if (!res.ok) { const errorBody = (await res.json().catch(() => ({}))) as { |
