"""Auth middleware — FastAPI dependency for JWT-based authentication.""" from __future__ import annotations from fastapi import Depends, HTTPException, status from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer import jwt as pyjwt from services.api_gateway.auth.jwt import decode_token from services.api_gateway.config import ApiGatewayConfig # Shared config instance — injected via FastAPI dependency override in tests. _config: ApiGatewayConfig | None = None security = HTTPBearer(auto_error=False) def get_config() -> ApiGatewayConfig: """Return the singleton config. Overridden in tests.""" global _config if _config is None: _config = ApiGatewayConfig() return _config _DEV_USER = { "sub": "00000000-0000-0000-0000-000000000000", "username": "dev", "type": "access", } async def get_current_user( credentials: HTTPAuthorizationCredentials | None = Depends(security), config: ApiGatewayConfig = Depends(get_config), ) -> dict: """FastAPI dependency that extracts and validates a Bearer JWT. Returns the decoded token payload (contains ``sub``, ``username``, etc.) on success. Raises a 401 ``HTTPException`` for missing, expired, or invalid tokens. When ``config.dev_mode`` is ``True``, authentication is bypassed and a synthetic dev user is returned. """ if config.dev_mode: return _DEV_USER if credentials is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing authorization header", headers={"WWW-Authenticate": "Bearer"}, ) token = credentials.credentials try: payload = decode_token(token, config) except pyjwt.ExpiredSignatureError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired", headers={"WWW-Authenticate": "Bearer"}, ) except pyjwt.InvalidTokenError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token", headers={"WWW-Authenticate": "Bearer"}, ) if payload.get("type") != "access": raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token type", headers={"WWW-Authenticate": "Bearer"}, ) return payload