config/auth.ts:57 sets a one-hour access token:
tokenExpiry: env.AUTH_TOKEN_EXPIRY || 60 * 60 * 1000,config/auth.ts:60 documents a 30-day refresh token right below it. LoginAction returns one. And it is never used:
$ grep -rn 'refresh_token\|refreshToken' resources/ app/ routes/ types/ | wc -l
0Not "used incorrectly" — zero references anywhere in the app. login.stx reads the access token off the response and discards the rest.
What the user sees
After 60 minutes the token is dead, but localStorage still holds it, so the pre-paint guard on /dashboard and /account sees a truthy value and does not bounce to /login. The server, meanwhile, cannot authenticate the request and renders the signed-out gate. The user sits on a "Sign in to view analytics" wall on a page they are signed in to, and the only way out is to sign in again.
This happens to every signed-in user, once an hour, forever.
Fix
- Capture
refresh_tokeninsession.signIn()alongside the access token. - On a 401 from any authenticated fetch, exchange it at the existing
POST /auth/refreshand retry once. - Only sign out when the refresh itself fails.
Related
- The cookie mirror has a matching gap — see the server-set-cookie issue filed alongside this one. Both live in the same code, so do them together.
- Worth deciding HttpOnly at the same time:
@stacksjs/auth's cookie helper hardcodes HttpOnly, whichdocument.cookiecannot overwrite, so going HttpOnly means deleting the session store's mirror effect and both pre-paint blocks rather than adding to them.