"""Shared FastAPI dependencies. ``get_current_user`` decodes the Bearer JWT and returns a ``UserProfile``. Step 9 will layer rate-limiting and sanitization middleware on top of this. Step 12 will add a DB look-up to fetch the live tier from PostgreSQL. """ from __future__ import annotations from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from app.config.settings import settings from app.schemas import BillingTier, UserProfile oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login") async def get_current_user( token: str = Depends(oauth2_scheme), ) -> UserProfile: """Validate a Bearer JWT and return the authenticated user. Raises ``HTTP 401`` on any invalid or expired token. The tier embedded in the JWT is used for feature-gating until Step 12 adds a live DB lookup. """ credentials_exc = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode( token, settings.JWT_SECRET, algorithms=[settings.JWT_ALGORITHM] ) user_id: str | None = payload.get("sub") email: str | None = payload.get("email") tier: str = payload.get("tier", "free") if not user_id or not email: raise credentials_exc except JWTError: raise credentials_exc return UserProfile(id=user_id, email=email, tier=tier) # type: ignore[arg-type]