Spaces:
Sleeping
Sleeping
Quick Start: Refresh Token Rotation & Remember Me
Setup
1. Update Environment Variables
Add to your .env file:
JWT_REMEMBER_ME_EXPIRE_DAYS=30
2. Create Database Indexes
Run the index creation script:
cd bookmyservice-ums
python scripts/create_indexes.py
3. Restart Your Service
# If using uvicorn directly
uvicorn app.app:app --reload
# If using docker
docker-compose restart ums
Testing
Test 1: Login with Remember Me
# Step 1: Send OTP
curl -X POST http://localhost:8000/send-otp-login \
-H "Content-Type: application/json" \
-d '{
"login_input": "+1234567890"
}'
# Response includes temp_token
# {"message": "OTP sent", "temp_token": "...", "user_exists": true}
# Step 2: Login with OTP and remember_me
curl -X POST http://localhost:8000/otp-login \
-H "Authorization: Bearer <temp_token>" \
-H "Content-Type: application/json" \
-d '{
"login_input": "+1234567890",
"otp": "777777",
"remember_me": true,
"device_info": "Chrome 120 on macOS"
}'
# Response includes access_token and refresh_token
Test 2: Refresh Token (with Rotation)
curl -X POST http://localhost:8000/refresh-token \
-H "Authorization: Bearer <refresh_token>"
# Response includes NEW access_token and NEW refresh_token
# Old refresh_token is now invalid
Test 3: View Active Sessions
curl -X GET http://localhost:8000/sessions \
-H "Authorization: Bearer <access_token>"
# Response shows all active sessions with device info
Test 4: Logout from Specific Device
curl -X DELETE http://localhost:8000/sessions/<token_id> \
-H "Authorization: Bearer <access_token>"
Test 5: Logout from All Devices
curl -X POST http://localhost:8000/logout-all \
-H "Authorization: Bearer <access_token>"
Verify Token Rotation
Check Token is Marked as Used
from app.models.refresh_token_model import RefreshTokenModel
import asyncio
async def check_token():
# After using a refresh token
token_id = "your-token-id"
metadata = await RefreshTokenModel.get_token_metadata(token_id)
print(f"Used: {metadata.get('used')}")
print(f"Used at: {metadata.get('used_at')}")
asyncio.run(check_token())
Test Replay Attack Detection
# Use a refresh token
curl -X POST http://localhost:8000/refresh-token \
-H "Authorization: Bearer <refresh_token>"
# Try to use the SAME refresh token again
curl -X POST http://localhost:8000/refresh-token \
-H "Authorization: Bearer <same_refresh_token>"
# Should return 401 error and revoke entire token family
Maintenance
Daily Cleanup (Cron Job)
Add to your crontab:
# Run cleanup daily at 2 AM
0 2 * * * cd /path/to/bookmyservice-ums && python scripts/cleanup_tokens.py >> /var/log/token_cleanup.log 2>&1
Or use a scheduler in your application:
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from app.models.refresh_token_model import RefreshTokenModel
scheduler = AsyncIOScheduler()
@scheduler.scheduled_job('cron', hour=2)
async def cleanup_job():
await RefreshTokenModel.cleanup_expired_tokens()
scheduler.start()
Monitoring
Check Token Statistics
python scripts/cleanup_tokens.py
Output:
📊 Token Statistics:
Total tokens: 150
Active tokens: 120
Revoked tokens: 20
Expired tokens: 10
Used tokens: 80
Remember me tokens: 45
Monitor Suspicious Activity
The cleanup script automatically checks for:
- Token families with >100 rotations
- Unusual rotation patterns
- Potential security breaches
Frontend Integration Example
React Hook for Token Management
import { useState, useEffect } from 'react';
export function useAuth() {
const [accessToken, setAccessToken] = useState(
localStorage.getItem('access_token')
);
const [refreshToken, setRefreshToken] = useState(
localStorage.getItem('refresh_token')
);
// Auto-refresh before expiry
useEffect(() => {
const interval = setInterval(async () => {
if (refreshToken) {
try {
const response = await fetch('/refresh-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${refreshToken}`
}
});
if (response.ok) {
const data = await response.json();
setAccessToken(data.access_token);
setRefreshToken(data.refresh_token);
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
}
} catch (error) {
console.error('Token refresh failed:', error);
}
}
}, 7 * 60 * 1000); // Refresh every 7 minutes
return () => clearInterval(interval);
}, [refreshToken]);
const login = async (loginInput, otp, rememberMe = false) => {
const response = await fetch('/otp-login', {
method: 'POST',
headers: {
'Authorization': `Bearer ${tempToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
login_input: loginInput,
otp: otp,
remember_me: rememberMe,
device_info: navigator.userAgent
})
});
const data = await response.json();
setAccessToken(data.access_token);
setRefreshToken(data.refresh_token);
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);
};
const logout = async () => {
await fetch('/logout', {
method: 'POST',
headers: {
'Authorization': `Bearer ${refreshToken}`
}
});
setAccessToken(null);
setRefreshToken(null);
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
};
return { accessToken, refreshToken, login, logout };
}
Troubleshooting
Issue: "Invalid refresh token" after first use
Solution: This is expected behavior with rotation. Always use the NEW refresh token from the response.
Issue: All sessions revoked unexpectedly
Cause: Token reuse detected (replay attack prevention) Solution: User must login again. Check for client-side bugs causing token reuse.
Issue: Remember me not working
Check:
JWT_REMEMBER_ME_EXPIRE_DAYSis set in .envremember_me: trueis sent in login request- Token metadata shows
remember_me: true
Issue: Sessions not showing up
Check:
- Database indexes are created
- MongoDB connection is working
- Token hasn't expired
Security Checklist
- HTTPS enabled in production
- Refresh tokens stored securely (httpOnly cookies recommended)
- Access tokens have short expiry (8 hours or less)
- Remember me tokens have reasonable expiry (30 days max)
- Cleanup script runs daily
- Monitoring alerts set up for suspicious activity
- Database indexes created
- Rate limiting enabled on refresh endpoint
Next Steps
- Implement httpOnly cookies for refresh tokens (more secure than localStorage)
- Add device fingerprinting for additional security
- Set up monitoring and alerting
- Implement push notifications for new logins
- Add geolocation-based anomaly detection
Support
For issues or questions:
- Check the logs:
tail -f app.log - Run diagnostics:
python scripts/cleanup_tokens.py - Review the full documentation:
REFRESH_TOKEN_ROTATION.md