Nginx already forwards X-Forwarded-For and X-Real-IP, but the backend
read request.client.host directly — always returning 172.18.0.x. Added
get_client_ip() helper to audit service; updated all 13 call sites.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add max_length constraints to all string fields in request schemas,
matching DB column limits (title:255, description:5000, etc.)
- Add min_length=1 to required name/title fields
- Add ConfigDict(extra="forbid") to all request schemas to reject
unknown fields (prevents silent field injection)
- Add Path(ge=1, le=2147483647) to all integer path parameters across
all routers to prevent integer overflow → 500 errors
- Add max_length to TOTP inline schemas (code:6, mfa_token:256, etc.)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- backend: add POST /auth/totp/enforce-setup and /auth/totp/enforce-confirm
endpoints that operate on mfa_enforce_token (not session cookie), generate
TOTP secret/QR/backup codes, verify confirmation code, enable TOTP, clear
mfa_enforce_pending flag, and issue a full session cookie
- frontend: expand LockScreen to five modes — login, first-run setup,
open registration, TOTP challenge, MFA enforcement setup (QR -> verify ->
backup codes), and forced password change; all modes share AmbientBackground
and the existing card layout; registration visible only when
authStatus.registration_open is true
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>