TradingAgents/.claude/skills/github-workflow/examples/pr-template.md

12 KiB

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

# 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

# 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):

# 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):

# 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

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

  • Penetration testing completed (no critical issues)
  • Security review completed (approved by security team)
  • OWASP Top 10 compliance verified
  • Token storage best practices followed

Documentation Updates

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

  • Tests added for new functionality
  • All tests pass locally (pytest tests/)
  • Integration tests pass (pytest tests/integration/)
  • Load testing completed (10K concurrent users)
  • Security audit completed
  • API documentation updated
  • Migration guide published
  • CHANGELOG.md updated
  • Breaking changes documented
  • No new linter warnings
  • Code coverage >95%
  • Feature flag added for gradual rollout
  • Rollback plan documented
  • 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

Co-Authored-By: Claude noreply@anthropic.com