Files
igny8/AUTHENTICATION-FIX-DEPLOYMENT.md
IGNY8 VPS (Salman) 4fb3a144d7 messy logout fixing
2025-12-15 12:01:41 +00:00

8.3 KiB

🚀 Authentication Fix - Deployment Guide

IMPORTANT: Follow these steps in order to avoid downtime.


📋 Pre-Deployment Checklist


🔧 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
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

  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"

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.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:

-- 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 logout back
  • 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:

  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)