from fastapi import HTTPException, Depends, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from jose import JWTError, jwt import httpx import yaml from typing import Optional, Dict, Any import logging logger = logging.getLogger(__name__) security = HTTPBearer() class AuthManager: def __init__(self, config: Dict[str, Any]): self.algorithm = config.get('algorithm', 'RS256') self.jwks_url = config.get('jwks_url') self.issuer = config.get('issuer') self.audience = config.get('audience') self.jwks_cache: Optional[Dict] = None async def get_jwks(self) -> Dict: """Fetch JWKS from provider""" if self.jwks_cache is None: try: async with httpx.AsyncClient() as client: response = await client.get(self.jwks_url) response.raise_for_status() self.jwks_cache = response.json() except Exception as e: logger.error(f"Failed to fetch JWKS: {e}") raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Authentication service unavailable" ) return self.jwks_cache async def verify_token(self, token: str) -> Dict[str, Any]: """Verify JWT token and return claims""" try: # For development, we'll skip actual JWT verification # In production, implement proper JWKS verification unverified_payload = jwt.get_unverified_claims(token) # Extract user information from token user_info = { 'user_id': unverified_payload.get('sub', 'unknown'), 'email': unverified_payload.get('email'), 'name': unverified_payload.get('name'), 'roles': unverified_payload.get('roles', []) } return user_info except JWTError as e: logger.error(f"JWT verification failed: {e}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication token", headers={"WWW-Authenticate": "Bearer"}, ) # Global auth manager auth_manager: Optional[AuthManager] = None def initialize_auth(config: Dict[str, Any]): global auth_manager auth_manager = AuthManager(config) async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)) -> Dict[str, Any]: """Dependency to get current authenticated user""" if auth_manager is None: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Authentication not configured" ) return await auth_manager.verify_token(credentials.credentials) async def get_current_admin_user(current_user: Dict[str, Any] = Depends(get_current_user)) -> Dict[str, Any]: """Dependency to ensure user has admin role""" if 'admin' not in current_user.get('roles', []): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Admin privileges required" ) return current_user