bookmyservice-ums / QUICK_START_ROTATION.md
MukeshKapoor25's picture
remember me
9b51d59
# Quick Start: Refresh Token Rotation & Remember Me
## Setup
### 1. Update Environment Variables
Add to your `.env` file:
```bash
JWT_REMEMBER_ME_EXPIRE_DAYS=30
```
### 2. Create Database Indexes
Run the index creation script:
```bash
cd bookmyservice-ums
python scripts/create_indexes.py
```
### 3. Restart Your Service
```bash
# If using uvicorn directly
uvicorn app.app:app --reload
# If using docker
docker-compose restart ums
```
## Testing
### Test 1: Login with Remember Me
```bash
# 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)
```bash
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
```bash
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
```bash
curl -X DELETE http://localhost:8000/sessions/<token_id> \
-H "Authorization: Bearer <access_token>"
```
### Test 5: Logout from All Devices
```bash
curl -X POST http://localhost:8000/logout-all \
-H "Authorization: Bearer <access_token>"
```
## Verify Token Rotation
### Check Token is Marked as Used
```python
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
```bash
# 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:
```bash
# 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:
```python
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
```bash
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
```javascript
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**:
1. `JWT_REMEMBER_ME_EXPIRE_DAYS` is set in .env
2. `remember_me: true` is sent in login request
3. Token metadata shows `remember_me: true`
### Issue: Sessions not showing up
**Check**:
1. Database indexes are created
2. MongoDB connection is working
3. 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
1. Implement httpOnly cookies for refresh tokens (more secure than localStorage)
2. Add device fingerprinting for additional security
3. Set up monitoring and alerting
4. Implement push notifications for new logins
5. Add geolocation-based anomaly detection
## Support
For issues or questions:
1. Check the logs: `tail -f app.log`
2. Run diagnostics: `python scripts/cleanup_tokens.py`
3. Review the full documentation: `REFRESH_TOKEN_ROTATION.md`