8.3 KiB
8.3 KiB
🚀 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
- Read 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
cd /data/app/igny8/backend
pip install -r requirements.txt
# Specifically: django-redis>=5.4.0
Step 2: Run Database Migrations
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
# 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)
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
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
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:
// ❌ 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:
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:
// 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:
// ❌ 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:
// Add state:
const [rememberMe, setRememberMe] = useState(false);
// Add checkbox before submit button:
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={rememberMe}
onChange={(e) => setRememberMe(e.target.checked)}
className="form-checkbox"
/>
<span>Keep me logged in for 20 days</span>
</label>
// Update login call:
await login(email, password, rememberMe);
Update authStore login method signature:
login: async (email: string, password: string, rememberMe = false) => {
// ...
body: JSON.stringify({ email, password, remember_me: rememberMe }),
// ...
}
Step 9: Rebuild and Restart Frontend
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
- Clear browser cache and cookies
- Navigate to login page
- Login with remember-me checked
- Verify you stay logged in
- Check Redis:
redis-cli keys "*"should show session
Test 2: Token Refresh
- Login
- Wait 1 hour (access token expires)
- Make an API call
- Should automatically refresh and succeed
- Check network tab - should see refresh call
Test 3: Multi-Tab
- Open app in two tabs
- Trigger token refresh in one tab
- Make API call in other tab
- Should work without re-auth
Test 4: Error Handling
- Login
- Trigger a 403 error (access denied page)
- Verify you are NOT logged out
- Navigate to dashboard - should still work
Test 5: Logout
- Login
- Click logout
- Verify all tabs logout
- Check Redis - session should be gone
🐛 Troubleshooting
"RefreshToken model not found"
cd /data/app/igny8/backend
python manage.py migrate
"Redis connection refused"
# 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"
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.tsis 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:
- Logout rate: Should drop to near-zero (except explicit logouts)
- Token refresh success rate: Should be >99.9%
- 403 errors: Should NOT cause logout
- Session duration: Average should increase to ~20 days with remember-me
Database Queries:
-- 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:
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:
# 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 logoutback - Restore logout calls in validation failures
4. Restart services:
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:
- Check logs:
docker logs igny8_backend -f - Review error messages in browser console
- Verify migrations ran successfully
- Check Redis connectivity
- Test with fresh user account
Last Updated: December 15, 2025
Estimated Deployment Time: 30-45 minutes
Risk Level: Low (all changes backward compatible)