Overview
This document describes the migration to a secure, enterprise-grade authentication system following BigTech standards (Google, AWS, Auth0). This is a BREAKING CHANGE that will require all users to re-login.
What Changed?
Security Improvements
- httpOnly Cookies for Refresh Tokens
- Refresh tokens are now stored in httpOnly cookies (instead of localStorage)
- XSS attacks can no longer steal long-lived refresh tokens
-
Automatic cookie handling by browser (no JS access)
-
In-Memory Access Tokens
- Access tokens stored only in memory (class field)
- Tokens disappear when tab/window closes
-
No persistence in localStorage
-
CSRF Protection
- CSRF tokens for cookie-based authentication
- Double-submit cookie pattern
-
Protection against cross-site request forgery
-
Token Rotation
- Refresh tokens rotated on every use
- Replay detection with automatic family revocation
-
Minimizes window of compromise
-
Security Headers
- Content Security Policy (CSP)
- HTTP Strict Transport Security (HSTS)
- X-Frame-Options, X-Content-Type-Options, etc.
API Changes
Backend (/v1/token endpoint)
New Query Parameter: response_mode
1 2 3 | |
Response Changes for Web Clients (response_mode=cookie):
Before:
1 2 3 4 5 6 | |
After:
1 2 3 4 5 6 | |
Set-Cookie: refresh_token=abc123...; HttpOnly; Secure; SameSite=Strict; Path=/v1/token; Max-Age=2592000
Database Changes:
New field in refresh_sessions table:
1 | |
Frontend
Removed:
- All localStorage operations for tokens
- httpClient.setRefreshToken() method
- httpClient.getRefreshToken() method
Added:
- httpClient.getCsrfToken() - get CSRF token from sessionStorage
- httpClient.setCsrfToken() - store CSRF token in sessionStorage
- Automatic X-CSRF-Token header on state-changing requests (POST/PUT/PATCH/DELETE)
- withCredentials: true on all API requests (sends cookies)
Token Storage:
- Access token: Memory only (httpClient.accessToken private field)
- CSRF token: sessionStorage (csrf_token key)
- Refresh token: httpOnly cookie (managed by browser, not accessible via JS)
Configuration
Environment Variables
Add these settings to your .env file or environment:
1 2 3 4 5 6 7 8 9 10 11 12 | |
For development:
1 | |
For production:
1 2 | |
Database Migration
-
Apply database migration:
1 2
# Add metadata column to refresh_sessions kubectl exec -it session-token-service-pod -- python -m alembic upgrade head -
Deploy session-token-service with new code:
1 2 3 4 5 6 7
cd services/auth/session-token-service uv sync # Run tests pytest # Build and deploy docker build -t session-token-service:v2 . kubectl apply -f k8s/session-token-service/ -
Verify CORS configuration:
1 2 3 4 5
curl -H "Origin: https://stage.ai-ops.tech" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: X-CSRF-Token" \ -X OPTIONS \ https://api.stage.ai-ops.tech/v1/token
Should return:
1 2 3 4 | |
2. Deploy Frontend Changes
-
Build and deploy web-app:
1 2 3 4 5
cd services/web-app npm install npm run build docker build -t web-app:v2 . kubectl apply -f k8s/web-app/ -
Clear browser storage (optional, but recommended for testing):
1 2 3
// In browser console localStorage.clear(); sessionStorage.clear();
3. User Communication
Announcement Template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Testing Checklist
Security Testing
- [ ] XSS Protection
- Open DevTools Console
- Try
document.cookie- should NOT seerefresh_token -
Try
localStorage.getItem("refresh_token")- should benull -
[ ] CSRF Protection
- Remove
X-CSRF-Tokenheader in DevTools -
Try POST request - should fail with 403
-
[ ] Token Rotation
- Login successfully
- Get refresh_token cookie value from DevTools
- Wait for access_token to expire
- Make API request (triggers refresh)
-
Check refresh_token cookie - should have NEW value
-
[ ] Session Expiration
- Wait 30 days (or manually delete cookie)
- Try to make API request
-
Should redirect to login
-
[ ] Logout
- Click logout
- Check DevTools: no
access_tokenin memory, nocsrf_tokenin sessionStorage - Check cookies:
refresh_tokenshould be deleted
Functional Testing
- [ ] Login flow works
- [ ] Logout flow works
- [ ] Token refresh works (automatic on 401)
- [ ] API requests include
Authorization: Bearer <token> - [ ] API requests include
X-CSRF-Tokenheader - [ ] Browser sends
refresh_tokencookie automatically
Browser Compatibility
Test in: - [ ] Chrome/Edge (latest) - [ ] Firefox (latest) - [ ] Safari (latest)
Rollback Plan
If critical issues found:
-
Revert frontend to previous version:
1kubectl rollout undo deployment/web-app -
Users will need to re-login again (acceptable for security fix)
-
Backend remains backward compatible (supports both old and new flows)
Monitoring
Watch these metrics after deployment:
-
Token endpoint errors:
1rate(http_requests_total{endpoint="/v1/token", status=~"4.."}[5m]) -
Refresh success rate:
1rate(token_refreshed_total[5m]) / rate(token_refresh_attempts_total[5m]) -
CSRF errors:
1rate(csrf_errors_total[5m]) -
User re-logins:
1rate(user_login_total[1h])
FAQ
Q: Why do all users need to re-login? A: We're changing how tokens are stored (localStorage → httpOnly cookies). Old tokens in localStorage won't work with the new system.
Q: Will this affect mobile apps?
A: No. Mobile apps can continue using JSON-based flow with response_mode=json or default behavior.
Q: How long do sessions last now? A: Same as before - 15 minutes for access tokens, 30 days for refresh tokens.
Q: Can users stay logged in forever? A: No. After 30 days of inactivity, refresh token expires and re-login is required.
Q: What if I clear cookies? A: You'll need to re-login. This is expected behavior for security.