diff --git a/backend/deps/auth.py b/backend/deps/auth.py index ad6a5e2..248a422 100644 --- a/backend/deps/auth.py +++ b/backend/deps/auth.py @@ -20,6 +20,7 @@ Usage in route handlers: ): ... """ +import hmac import logging import uuid @@ -84,6 +85,24 @@ async def get_current_user( _logger.warning("Redis user_nbf check failed (fail-open): %s", exc) # ── end user_nbf check ────────────────────────────────────────────────────── + # ── fgp check (D-06, Phase 7.4) ──────────────────────────────────────────── + # Validates the fgp claim embedded by create_access_token. Empty claim means + # the token predates Phase 7.4 — allow gracefully (migration window, D-06). + # NOT wrapped in try/except: _compute_fgp is pure computation with no I/O. + fgp_claim = payload.get("fgp", "") + if fgp_claim: + fgp_actual = auth_service._compute_fgp( + request.headers.get("User-Agent", ""), + request.headers.get("Accept-Language", ""), + ) + if not hmac.compare_digest(fgp_claim, fgp_actual): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Token fingerprint mismatch", + headers={"WWW-Authenticate": "Bearer"}, + ) + # ── end fgp check ─────────────────────────────────────────────────────────── + try: user_uuid = uuid.UUID(payload["sub"]) except (KeyError, ValueError) as exc: