messy logout fixing
This commit is contained in:
347
AUTHENTICATION-FIX-DEPLOYMENT.md
Normal file
347
AUTHENTICATION-FIX-DEPLOYMENT.md
Normal file
@@ -0,0 +1,347 @@
|
||||
## 🚀 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:
|
||||
<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:
|
||||
```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)
|
||||
Reference in New Issue
Block a user