fix(client): Android app doesn't stay logged in — refresh token is never used #306
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol#306
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
On the Android app (and any native/Tauri build), the user is forced to sign in
again roughly every 15 minutes and on every app reopen, even though a valid
30-day refresh token is sitting in SecureStore.
Root cause
The native auth flow mints a short-lived access token (15 min) + long-lived
refresh token (30 days) and stores both in SecureStore
(
apps/client/lib/auth/storage.ts). But nothing ever consults the refreshtoken:
getAuthHeader()(apps/client/lib/auth/getAuthHeader.ts) attaches theaccess token verbatim. When it expires, the API returns 401.
getRefreshToken()is definedbut never called anywhere in the codebase.
apps/client/app/(app)/_layout.tsx) redirects to/loginwheneveruseMe()errors, so an expired access token → forcedre-login.
The server side is already complete:
POST /api/auth/refreshrotates a refreshtoken into a fresh pair (
apps/api/app/api/auth/refresh/route.ts, ADR-0027§6), with mandatory single-use rotation + reuse detection.
Fix
Add proactive, single-flight refresh on the native auth path:
accessExpiresAt) alongside the tokens.getAuthHeader(), if the access token is missing/expired/near-expiry,call
POST /api/auth/refreshwith the stored refresh token, persist the newpair, and return the fresh access token — before the request goes out.
concurrent
/refreshcalls with the same single-use token would trip theserver's reuse detection and revoke the whole family).
revoked) do we clear tokens and fall back to
/login.Acceptance criteria
refreshes and succeeds — no re-login.
authenticated home screen, not
/login./login./api/auth/refresh.Out of scope
carol_sessioncookie; unaffected.the failure modes; can revisit if server-side early revocation becomes a
concern).