Prompt
How do I manage sessions and refresh tokens securely?
Latest observation
Oct 6, 2026
OpenAI APIWeb search: off
Here’s a practical, security-focused approach to sessions and refresh tokens for modern web apps and APIs.
Recommended pattern
For browser-based apps
- Use a short-lived access token (or no access token in the browser at all if you can use cookie-based sessions).
- Store the refresh token in an
HttpOnly,Secure,SameSitecookie. - Keep access tokens:
- in memory if needed on the client, not in
localStorage - very short-lived, e.g. 5–15 minutes
- in memory if needed on the client, not in
For server-rendered apps
- Prefer server-side sessions with a random session ID in a cookie.
- Store session data server-side.
- Rotate session IDs after login and privilege changes.
Key security rules
1) Never store refresh tokens in localStorage
Why:
localStorageis readable by JavaScript.- If you get XSS, the attacker can steal the token and persist access.
Better:
HttpOnlycookie for refresh token.HttpOnlycookie for session ID in traditional session auth.
2) Use HttpOnly, Secure, and SameSite
For cookies:
HttpOnly: JS cannot read itSecure: only sent over HTTPSSameSite=LaxorStrict:Strict= strongest, but can break some flowsLax= usually good defaultNoneonly if you truly need cross-site cookies, and thenSecureis required
Example:
Set-Cookie: refresh_token=...; HttpOnly; Secure; SameSite=Lax; Path=/auth/refresh
3) Keep refresh tokens long-lived but tightly controlled
Refresh tokens are sensitive because they allow obtaining new access tokens.
Best practices:
- Make them rotatable
- Bind them to a session or token family
- Store a hash of the refresh token server-side, not the raw token
- Set an absolute expiration and inactivity expiration
4) Rotate refresh tokens on every use
On each refresh:
- Client sends refresh token.
- Server validates it.
- Server issues:
- new access token
- new refresh token
- Old refresh token is invalidated
Why:
- If a token is stolen, reuse detection helps you catch abuse.
- Limits replay attacks.
5) Detect refresh token reuse
If an old refresh token is used after rotation:
- Treat it as a sign of compromise.
- Revoke the whole session/token family.
- Force re-authentication.
This is one of the most important defenses.
6) Protect against CSRF if using cookies
If authentication relies on cookies, CSRF matters.
Use:
SameSite=LaxorStrictwhere possible- CSRF tokens for state-changing requests if needed
- Double-submit cookie pattern or server-side CSRF verification
Note:
HttpOnlydoes not prevent CSRF- It only prevents token theft via JS
7) Store sessions securely server-side
If using session IDs:
- Generate them with high entropy
- Store only a random opaque ID in the cookie
- Keep session data in Redis/database/memory store
- Rotate session ID on login and privilege elevation
- Invalidate on logout
8) Limit token scope and lifetime
Minimize blast radius:
- Access tokens: short lifetime
- Refresh tokens: longer, but still limited
- Scope them to the minimum permissions needed
- Separate tokens for different apps/devices if appropriate
9) Use HTTPS everywhere
Always:
- Redirect HTTP to HTTPS
- Set
Securecookies - Consider HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains
10) Support logout and revocation
Logout should:
- Delete client cookie
- Revoke the server-side session / refresh token
- Invalidate token family if needed
Also provide:
- “Log out of all devices”
- Device/session management UI
Practical architecture options
Option A: Best for many web apps — cookie-based sessions
- Browser gets a session cookie
- Server maintains session state
- Simple and secure
- Great for traditional web apps and many SPAs
Option B: Access + refresh tokens
- Access token: short-lived, used in API calls
- Refresh token:
HttpOnlycookie, used only at refresh endpoint - Good when you need API auth across services or mobile clients
Option C: BFF pattern
Backend-for-Frontend:
- Browser talks only to your backend
- Backend handles tokens with auth server
- Browser gets only secure session cookie
- Often the safest SPA pattern
Common mistakes to avoid
- Storing refresh tokens in
localStorage - Using long-lived access tokens
- Not rotating refresh tokens
- Not detecting token reuse
- Omitting CSRF protection when using cookies
- Failing to revoke tokens on logout
- Not binding tokens to session/device context
- Logging tokens in app logs or analytics
A good baseline setup
If you want a strong default:
- HTTPS only
HttpOnly,Secure,SameSite=Laxcookies- Short-lived access token: 5–15 min
- Rotating refresh token
- Server-side refresh token storage with hashing
- Reuse detection and family revocation
- CSRF protection for state-changing cookie-authenticated requests
- Session rotation on login and privilege changes
If you want, I can also give you:
- a Node/Express example
- a Python/FastAPI example
- a Spring Boot example
- a recommended architecture for SPAs