306 lines
12 KiB
Markdown
306 lines
12 KiB
Markdown
# Example Pull Request Description
|
|
|
|
## Summary
|
|
Add JWT-based authentication to replace session-based auth, enabling horizontal scaling and stateless API architecture.
|
|
|
|
## Motivation
|
|
Current session-based authentication requires sticky sessions in the load balancer, preventing horizontal scaling and blocking mobile app launch. This migration enables:
|
|
- Dynamic server scaling during traffic spikes
|
|
- Mobile app support (no cookie requirement)
|
|
- Reduced infrastructure costs (~30%)
|
|
|
|
## Changes
|
|
|
|
### Added
|
|
- JWT token generation and validation service (`src/auth/jwt_service.py`)
|
|
- Authentication endpoints (`/auth/login`, `/auth/refresh`, `/auth/logout`)
|
|
- JWT validation middleware for API route protection
|
|
- Refresh token mechanism with 7-day expiry
|
|
- Rate limiting on authentication endpoints (10 req/min per IP)
|
|
- Token revocation support for logout
|
|
|
|
### Changed
|
|
- API middleware now checks for JWT in `Authorization: Bearer <token>` header
|
|
- Error responses return 401 Unauthorized for invalid/expired tokens
|
|
- Configuration updated with JWT_SECRET and TOKEN_EXPIRY settings
|
|
|
|
### Removed
|
|
- ~~Session management middleware~~ (deprecated, will be removed in v3.0)
|
|
|
|
## Test plan
|
|
|
|
### Manual Testing
|
|
```bash
|
|
# 1. Install dependencies
|
|
pip install -r requirements.txt
|
|
|
|
# 2. Set JWT_SECRET environment variable
|
|
export JWT_SECRET="your-secret-key-here"
|
|
|
|
# 3. Start server
|
|
python run.py
|
|
|
|
# 4. Test login endpoint
|
|
curl -X POST http://localhost:5000/auth/login \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"username":"test","password":"test123"}'
|
|
|
|
# Expected response:
|
|
# {
|
|
# "access_token": "eyJ...",
|
|
# "refresh_token": "eyJ...",
|
|
# "expires_in": 900
|
|
# }
|
|
|
|
# 5. Test protected endpoint with token
|
|
curl http://localhost:5000/api/users \
|
|
-H "Authorization: Bearer <access_token>"
|
|
|
|
# Expected: 200 OK with user list
|
|
|
|
# 6. Test with invalid token
|
|
curl http://localhost:5000/api/users \
|
|
-H "Authorization: Bearer invalid_token"
|
|
|
|
# Expected: 401 Unauthorized
|
|
|
|
# 7. Test refresh token
|
|
curl -X POST http://localhost:5000/auth/refresh \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"refresh_token":"<refresh_token>"}'
|
|
|
|
# Expected: New access token
|
|
```
|
|
|
|
### Automated Tests
|
|
- **Unit tests**: `tests/test_jwt_service.py` (15 tests, all passing)
|
|
- Token generation
|
|
- Token validation
|
|
- Token expiration
|
|
- Token revocation
|
|
|
|
- **Integration tests**: `tests/integration/test_auth_endpoints.py` (12 tests, all passing)
|
|
- Login flow
|
|
- Token refresh flow
|
|
- Protected endpoint access
|
|
- Error handling
|
|
|
|
- **Coverage**: 96% of auth module (78/81 lines)
|
|
|
|
### Load Testing
|
|
```bash
|
|
# Test with 10,000 concurrent users
|
|
locust -f tests/load/test_auth.py --users 10000 --spawn-rate 100
|
|
|
|
Results:
|
|
- Average response time: 45ms
|
|
- 99th percentile: 120ms
|
|
- Error rate: 0%
|
|
- Memory usage: Stable at ~500MB (vs 2GB with sessions)
|
|
```
|
|
|
|
### Edge Cases Tested
|
|
- Expired access token → Returns 401, client refreshes successfully
|
|
- Expired refresh token → Returns 401, client re-authenticates
|
|
- Malformed JWT → Returns 401 with clear error message
|
|
- Missing Authorization header → Returns 401
|
|
- Token revoked via logout → Subsequent requests fail with 401
|
|
- Concurrent requests with same token → All succeed (no race conditions)
|
|
|
|
## Breaking Changes
|
|
|
|
### Migration Required
|
|
**Sessions will be deprecated in v3.0** (90 days from now). During the transition period, both session-based and JWT-based authentication are supported.
|
|
|
|
#### For API Clients
|
|
|
|
**Before (session-based):**
|
|
```python
|
|
# Login
|
|
response = requests.post("/api/login", json={"username": "user", "password": "pass"})
|
|
session_cookie = response.cookies["session_id"]
|
|
|
|
# Authenticated request
|
|
requests.get("/api/users", cookies={"session_id": session_cookie})
|
|
```
|
|
|
|
**After (JWT-based):**
|
|
```python
|
|
# Login
|
|
response = requests.post("/auth/login", json={"username": "user", "password": "pass"})
|
|
access_token = response.json()["access_token"]
|
|
refresh_token = response.json()["refresh_token"]
|
|
|
|
# Authenticated request
|
|
requests.get("/api/users", headers={"Authorization": f"Bearer {access_token}"})
|
|
|
|
# Refresh when access token expires
|
|
response = requests.post("/auth/refresh", json={"refresh_token": refresh_token})
|
|
new_access_token = response.json()["access_token"]
|
|
```
|
|
|
|
#### Migration Steps
|
|
1. Update client code to use `/auth/login` endpoint
|
|
2. Store `access_token` and `refresh_token` from login response
|
|
3. Send `Authorization: Bearer <access_token>` header with all API requests
|
|
4. Implement token refresh logic when access token expires (15 min)
|
|
5. Handle 401 responses by refreshing or re-authenticating
|
|
|
|
#### Migration Guide
|
|
See full migration guide: [docs/auth-migration-guide.md](docs/auth-migration-guide.md)
|
|
|
|
## Performance Impact
|
|
|
|
### Improvements
|
|
- **Memory usage**: 75% reduction (2GB → 500MB for 10K users)
|
|
- **Response time**: 20% faster (session lookup eliminated)
|
|
- **Scalability**: Supports 5x more concurrent users (10K → 50K)
|
|
- **Cost**: ~30% infrastructure savings (no sticky sessions)
|
|
|
|
### Regressions
|
|
None identified
|
|
|
|
### Benchmarks
|
|
| Metric | Before (Sessions) | After (JWT) | Change |
|
|
|--------|-------------------|-------------|--------|
|
|
| Avg response time | 55ms | 45ms | -18% ⬇️ |
|
|
| P99 response time | 180ms | 120ms | -33% ⬇️ |
|
|
| Memory (10K users) | 2GB | 500MB | -75% ⬇️ |
|
|
| Max concurrent users | 10,000 | 50,000 | +400% ⬆️ |
|
|
| Infrastructure cost | $5,000/mo | $3,500/mo | -30% ⬇️ |
|
|
|
|
## Security Considerations
|
|
|
|
### Security Measures Implemented
|
|
- **RS256 algorithm**: Asymmetric signing (public/private key pair)
|
|
- **Short token expiry**: Access tokens expire after 15 minutes
|
|
- **Refresh token rotation**: New refresh token issued on each refresh
|
|
- **Rate limiting**: 10 login attempts per minute per IP
|
|
- **Secure storage**: Refresh tokens HttpOnly, Secure flags
|
|
- **No sensitive data**: JWT payload contains only user_id, role
|
|
- **Token revocation**: Logout invalidates refresh tokens
|
|
|
|
### Security Audit
|
|
- [x] Penetration testing completed (no critical issues)
|
|
- [x] Security review completed (approved by security team)
|
|
- [x] OWASP Top 10 compliance verified
|
|
- [x] Token storage best practices followed
|
|
|
|
## Documentation Updates
|
|
- [x] API documentation updated with JWT examples ([docs/api/authentication.md](docs/api/authentication.md))
|
|
- [x] Migration guide published ([docs/auth-migration-guide.md](docs/auth-migration-guide.md))
|
|
- [x] Admin guide for token management added ([docs/admin/token-management.md](docs/admin/token-management.md))
|
|
- [x] Security best practices documented ([docs/security/jwt-best-practices.md](docs/security/jwt-best-practices.md))
|
|
- [x] CHANGELOG.md updated with breaking changes
|
|
|
|
## Rollout Plan
|
|
|
|
### Phase 1: Internal Beta (Week 1)
|
|
- Deploy to staging environment
|
|
- Dev team tests JWT authentication
|
|
- Monitor for issues
|
|
|
|
### Phase 2: Gradual Rollout (Week 2-3)
|
|
- Enable JWT for 10% of users (feature flag)
|
|
- Monitor error rates, performance
|
|
- Increase to 50% if no issues
|
|
- Increase to 100% by end of Week 3
|
|
|
|
### Phase 3: Deprecation Notice (Week 4)
|
|
- Display migration banner for session-based users
|
|
- Send email notification to API clients
|
|
- Update documentation with deprecation timeline
|
|
|
|
### Phase 4: Session Removal (90 days)
|
|
- Remove session-based authentication code
|
|
- Release v3.0 with breaking changes
|
|
|
|
## Checklist
|
|
- [x] Tests added for new functionality
|
|
- [x] All tests pass locally (`pytest tests/`)
|
|
- [x] Integration tests pass (`pytest tests/integration/`)
|
|
- [x] Load testing completed (10K concurrent users)
|
|
- [x] Security audit completed
|
|
- [x] API documentation updated
|
|
- [x] Migration guide published
|
|
- [x] CHANGELOG.md updated
|
|
- [x] Breaking changes documented
|
|
- [x] No new linter warnings
|
|
- [x] Code coverage >95%
|
|
- [x] Feature flag added for gradual rollout
|
|
- [x] Rollback plan documented
|
|
|
|
## Related
|
|
- Closes #123 (JWT authentication feature request)
|
|
- Blocks #125 (Mobile app launch)
|
|
- Related to #124 (Horizontal scaling infrastructure)
|
|
- Follow-up to #100 (Session management refactoring)
|
|
|
|
## Screenshots / Diagrams
|
|
|
|
### Authentication Flow
|
|
```
|
|
┌─────────┐ ┌─────────────┐
|
|
│ Client │ │ Auth API │
|
|
└─────────┘ └─────────────┘
|
|
│ │
|
|
│ 1. POST /auth/login │
|
|
│ { username, password } │
|
|
│──────────────────────────────────────────> │
|
|
│ │
|
|
│ │ 2. Validate credentials
|
|
│ │ 3. Generate tokens
|
|
│ │
|
|
│ 4. Return tokens │
|
|
│ { access_token, refresh_token } │
|
|
│ <──────────────────────────────────────── │
|
|
│ │
|
|
│ 5. Store tokens │
|
|
│ │
|
|
│ ┌───────────────┐
|
|
│ 6. API request + access_token │ API Server │
|
|
│──────────────────────────────────> └───────────────┘
|
|
│ │
|
|
│ │ 7. Validate JWT
|
|
│ │ 8. Process request
|
|
│ │
|
|
│ 9. Response │
|
|
│ <──────────────────────────────────────── │
|
|
```
|
|
|
|
### Token Refresh Flow
|
|
```
|
|
┌─────────┐ ┌─────────────┐
|
|
│ Client │ │ Auth API │
|
|
└─────────┘ └─────────────┘
|
|
│ │
|
|
│ 1. API request with expired token │
|
|
│──────────────────────────────────────────> │
|
|
│ │
|
|
│ 2. 401 Unauthorized │
|
|
│ <──────────────────────────────────────── │
|
|
│ │
|
|
│ 3. POST /auth/refresh │
|
|
│ { refresh_token } │
|
|
│──────────────────────────────────────────> │
|
|
│ │
|
|
│ │ 4. Validate refresh_token
|
|
│ │ 5. Generate new access_token
|
|
│ │
|
|
│ 6. Return new access_token │
|
|
│ { access_token } │
|
|
│ <──────────────────────────────────────── │
|
|
│ │
|
|
│ 7. Retry API request with new token │
|
|
│──────────────────────────────────────────> │
|
|
│ │
|
|
│ 8. Success response │
|
|
│ <──────────────────────────────────────── │
|
|
```
|
|
|
|
---
|
|
|
|
Generated with [Claude Code](https://claude.com/claude-code)
|
|
|
|
Co-Authored-By: Claude <noreply@anthropic.com>
|