Ship a new app version with broader analytics, restructured dashboards, and a server-rendered risk map. Frontend: - Add Overview, Demographic, Disease, and Environmental Health analysis pages - Add AnomalyMarkers, CalendarHeatmap, and MetricHeatmapTable components - Rebuild Alerts map onto server-rendered raster risk tiles; expand Monitoring, Trend, and District Comparison views - Extend API client, stores, and TypeScript types Backend: - Add environment router (pollutants, lag correlations) - Add risk_raster util serving XYZ 100m risk tiles - Expand cases endpoints (demographics, seasonality, diagnoses) and insights; harden auth and file-based loaders Data & tooling: - Add processed outpatient/inpatient/combined case parquet (LFS) - Add nested CLAUDE.md guides, pyrightconfig, and test updates
1.4 KiB
1.4 KiB
Auth — JWT Authentication
Stack
python-jose (JWT signing/verification) + passlib (bcrypt password hashing). Token-based, stateless.
Structure
auth/
models.py # Pydantic models: UserCreate, UserLogin, Token, UserOut
service.py # Business logic: authenticate_user, create_user, create_access_token
dependencies.py # FastAPI Depends: get_current_user, require_admin
middleware.py # ASGI middleware (if any global auth checks)
router.py # APIRouter: /login, /register, /whoami
Patterns
- Passwords hashed with bcrypt via
passlib— never store plaintext - JWT tokens signed with
python-jose, includesub(username) andexp get_current_user()is the standardDepends()to inject user into endpoints- Auth endpoints return Pydantic models:
Token(access_token=...),UserOut(username=...) - HTTP status codes: 401 for bad credentials, 409 for duplicate user
Usage in Routers
from auth.dependencies import get_current_user
@router.get("/protected")
async def protected_route(current_user = Depends(get_current_user)):
...
Anti-Patterns
- Don't hardcode secret keys — use
Settingsfrom environment - Don't store tokens client-side without HttpOnly cookies
- Don't skip
response_modelon auth endpoints - Don't leak whether username or password was wrong — always "incorrect username or password"
- Don't bypass
Depends(get_current_user)for protected routes