Refresh Tokens & Secure Sessions
Refresh Tokens & Secure Sessions
Short-lived access tokens are secure but inconvenient — a 15-minute token means the user is logged out every 15 minutes. Refresh tokens solve this: a long-lived token that can mint new access tokens without re-entering a password. Getting this pattern right is what makes a production auth system both secure and usable.
The two-token pattern
- Access token — short-lived (e.g. 15 min), sent on every request, stateless JWT.
- Refresh token — long-lived (e.g. 7 days), used only to obtain a new access token, and stored server-side so it can be revoked.
When the access token expires, the client calls a /refresh endpoint with the refresh token and receives a fresh access token — no password needed.
Issuing both on login
The refresh endpoint
On refresh, verify the refresh token's signature, confirm it matches the stored hash for that user, then issue new tokens:
Refresh token rotation
Where to store tokens on the client
Storage choice matters for security:
- httpOnly cookie — not readable by JavaScript, so it resists XSS token theft. Preferred for the refresh token (pair with CSRF protection).
- Memory — the access token can live in memory; lost on refresh, which is fine since it is short-lived.
- localStorage — convenient but readable by any script, so vulnerable to XSS. Avoid for long-lived tokens.
Logout
Because access tokens are stateless, "logout" really means invalidating the refresh token server-side: clear the stored refresh hash so it can no longer mint new access tokens. The short-lived access token then simply expires on its own.
Summary
Pair a short-lived access token with a long-lived, server-stored refresh token. Store only the refresh token's hash, verify and rotate it on each /refresh, prefer an httpOnly cookie for it, and "logout" by clearing the stored refresh hash. This delivers security (short access tokens, revocable refresh) and usability (no constant re-login). Next: authorization — deciding what an authenticated user may do.