## 🚀 Authentication Fix - Deployment Guide **IMPORTANT**: Follow these steps in order to avoid downtime. --- ## 📋 Pre-Deployment Checklist - [ ] Backup database - [ ] Review all changes in [AUTHENTICATION-FIX-IMPLEMENTATION.md](AUTHENTICATION-FIX-IMPLEMENTATION.md) - [ ] Read [AUTHENTICATION-AUDIT-REPORT.md](AUTHENTICATION-AUDIT-REPORT.md) for context - [ ] Ensure Redis is running - [ ] Test in staging environment first (if available) --- ## 🔧 Step-by-Step Deployment ### Step 1: Install Backend Dependencies ```bash cd /data/app/igny8/backend pip install -r requirements.txt # Specifically: django-redis>=5.4.0 ``` ### Step 2: Run Database Migrations ```bash cd /data/app/igny8/backend python manage.py makemigrations python manage.py migrate # You should see a new migration for RefreshToken model: # - Create model RefreshToken # - Add indexes ``` ### Step 3: Verify Redis is Running ```bash # Check Redis is accessible redis-cli -h redis ping # Should return: PONG # Or check via Docker: docker ps | grep redis ``` ### Step 4: Test Backend Changes (Optional but Recommended) ```bash cd /data/app/igny8/backend python manage.py shell # In the shell: from igny8_core.auth.models_refresh_token import RefreshToken from django.contrib.auth import get_user_model User = get_user_model() # Test token creation: user = User.objects.first() token = RefreshToken.create_token(user, remember_me=True) print(f"Created token: {token.token_id}") print(f"Expires: {token.expires_at}") print(f"Valid: {token.is_valid}") ``` ### Step 5: Restart Backend ```bash cd /data/app/igny8 docker-compose -f docker-compose.app.yml restart igny8_backend # Wait for healthy status: docker-compose -f docker-compose.app.yml ps igny8_backend ``` ### Step 6: Replace Frontend API Service ```bash cd /data/app/igny8/frontend/src/services # Backup old api.ts cp api.ts api-old-backup.ts # Replace with new implementation mv api-new.ts api.ts ``` ### Step 7: Update Frontend authStore (CRITICAL) Open `frontend/src/store/authStore.ts` and make these changes: #### 7a. Remove localStorage access_token persistence Find and REMOVE these lines from login/register: ```typescript // ❌ REMOVE THIS: localStorage.setItem('access_token', newToken); localStorage.setItem('refresh_token', newRefreshToken); ``` Access token should ONLY be in Zustand state (memory). #### 7b. Update persist configuration Ensure `partialize` does NOT include `token`: ```typescript partialize: (state) => ({ user: state.user, // ❌ DO NOT persist token: state.token, refreshToken: state.refreshToken, isAuthenticated: state.isAuthenticated, }) ``` #### 7c. Update refresh logic Ensure refresh updates BOTH access and refresh tokens: ```typescript // In refreshToken action: const data = await response.json(); set({ token: data.data?.access || data.access, refreshToken: data.data?.refresh || data.refresh // NEW: update refresh token too }); ``` #### 7d. Remove proactive refresh interval (Optional) The new api.ts handles token refresh automatically on 401. Remove this from AppLayout.tsx: ```typescript // ❌ REMOVE OR COMMENT OUT: // const tokenRefreshInterval = setInterval(async () => { // await authState.refreshToken(); // }, 720000); ``` ### Step 8: Add Remember-Me Checkbox to Login Form Open your login form component (e.g., `pages/SignIn.tsx`) and add: ```typescript // Add state: const [rememberMe, setRememberMe] = useState(false); // Add checkbox before submit button: // Update login call: await login(email, password, rememberMe); ``` Update authStore login method signature: ```typescript login: async (email: string, password: string, rememberMe = false) => { // ... body: JSON.stringify({ email, password, remember_me: rememberMe }), // ... } ``` ### Step 9: Rebuild and Restart Frontend ```bash cd /data/app/igny8/frontend docker-compose -f ../docker-compose.app.yml restart igny8_frontend # Or rebuild if needed: docker build -t igny8-frontend-dev:latest -f Dockerfile.dev . docker-compose -f ../docker-compose.app.yml up -d igny8_frontend ``` ### Step 10: Verify Everything Works #### Test 1: Fresh Login 1. Clear browser cache and cookies 2. Navigate to login page 3. Login with remember-me checked 4. Verify you stay logged in 5. Check Redis: `redis-cli keys "*"` should show session #### Test 2: Token Refresh 1. Login 2. Wait 1 hour (access token expires) 3. Make an API call 4. Should automatically refresh and succeed 5. Check network tab - should see refresh call #### Test 3: Multi-Tab 1. Open app in two tabs 2. Trigger token refresh in one tab 3. Make API call in other tab 4. Should work without re-auth #### Test 4: Error Handling 1. Login 2. Trigger a 403 error (access denied page) 3. Verify you are NOT logged out 4. Navigate to dashboard - should still work #### Test 5: Logout 1. Login 2. Click logout 3. Verify all tabs logout 4. Check Redis - session should be gone --- ## 🐛 Troubleshooting ### "RefreshToken model not found" ```bash cd /data/app/igny8/backend python manage.py migrate ``` ### "Redis connection refused" ```bash # Check Redis is running: docker ps | grep redis docker-compose -f docker-compose.app.yml ps # Restart Redis: cd /data/app docker-compose -f docker-compose.yml restart redis ``` ### "django-redis not installed" ```bash cd /data/app/igny8/backend pip install django-redis>=5.4.0 docker-compose -f docker-compose.app.yml restart igny8_backend ``` ### "Still getting logged out on 403 errors" - Check that `frontend/src/services/api.ts` is the NEW version - Old api.ts has logout on 403, new one doesn't - Verify file was replaced: `grep "NEVER logout on 403" frontend/src/services/api.ts` ### "Token refresh not working" - Check backend logs: `docker logs igny8_backend -f` - Verify RefreshToken records in database - Check that refresh endpoint returns new refresh token ### "Multi-tab logout" - Verify BroadcastChannel is supported (Chrome/Firefox/Edge) - Check browser console for errors - Fallback: Polling localStorage for token changes --- ## 📊 Monitoring Post-Deployment ### Key Metrics to Watch: 1. **Logout rate**: Should drop to near-zero (except explicit logouts) 2. **Token refresh success rate**: Should be >99.9% 3. **403 errors**: Should NOT cause logout 4. **Session duration**: Average should increase to ~20 days with remember-me ### Database Queries: ```sql -- Check active refresh tokens SELECT COUNT(*) FROM auth_refresh_token WHERE revoked_at IS NULL AND expires_at > NOW(); -- Check token rotation (should increase over time) SELECT AVG(rotation_count) FROM auth_refresh_token WHERE created_at > NOW() - INTERVAL '7 days'; -- Check remember-me usage SELECT remember_me, COUNT(*) as count, AVG(EXTRACT(EPOCH FROM (expires_at - created_at))/86400) as avg_days FROM auth_refresh_token WHERE created_at > NOW() - INTERVAL '7 days' GROUP BY remember_me; ``` --- ## 🔄 Rollback Plan (If Needed) If something goes wrong: ### 1. Restore old api.ts: ```bash cd /data/app/igny8/frontend/src/services mv api.ts api-new-broken.ts mv api-old-backup.ts api.ts ``` ### 2. Revert settings.py changes: ```python # Change back: SESSION_COOKIE_SAMESITE = 'Strict' SESSION_SAVE_EVERY_REQUEST = False JWT_ACCESS_TOKEN_EXPIRY = timedelta(minutes=15) # Comment out Redis cache config ``` ### 3. Revert middleware.py: - Add `from django.contrib.auth import logout` back - Restore logout calls in validation failures ### 4. Restart services: ```bash docker-compose -f docker-compose.app.yml restart igny8_backend igny8_frontend ``` --- ## ✅ Success Indicators You'll know the fix is working when: - ✅ Users report no random logouts - ✅ Remember-me users stay logged in for 20 days - ✅ 403/402 errors don't log users out - ✅ Network hiccups don't cause logout - ✅ Multi-tab usage works smoothly - ✅ Token refresh is invisible to users --- ## 📞 Support If you encounter issues: 1. Check logs: `docker logs igny8_backend -f` 2. Review error messages in browser console 3. Verify migrations ran successfully 4. Check Redis connectivity 5. Test with fresh user account --- **Last Updated:** December 15, 2025 **Estimated Deployment Time:** 30-45 minutes **Risk Level:** Low (all changes backward compatible)