from fastapi import HTTPException, Depends, status, Request from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import httpx import structlog from typing import Dict, Any, Optional from app.core.config import settings logger = structlog.get_logger() security = HTTPBearer(auto_error=False) # ✅ Don't auto-error, we'll handle manually class AuthInfo: """Authentication information""" def __init__(self, user_id: str, email: str, tenant_id: str, roles: list): self.user_id = user_id self.email = email self.tenant_id = tenant_id self.roles = roles async def get_current_user( request: Request, credentials: Optional[HTTPAuthorizationCredentials] = Depends(security) ) -> AuthInfo: """Get current user from gateway headers or token verification""" # ✅ OPTION 1: Check for gateway headers (preferred when using gateway) user_id = request.headers.get("X-User-ID") email = request.headers.get("X-User-Email") tenant_id = request.headers.get("X-Tenant-ID") roles_header = request.headers.get("X-User-Roles", "") if user_id and email and tenant_id: # Gateway already authenticated the user roles = roles_header.split(",") if roles_header else ["user"] logger.info("Authenticated via gateway headers", user_id=user_id, email=email) return AuthInfo(user_id, email, tenant_id, roles) # ✅ OPTION 2: Direct token verification (when not using gateway) if not credentials: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required (no token or gateway headers)" ) try: async with httpx.AsyncClient(timeout=5.0) as client: response = await client.post( f"{settings.AUTH_SERVICE_URL}/api/v1/auth/verify", headers={"Authorization": f"Bearer {credentials.credentials}"} ) if response.status_code == 200: user_data = response.json() logger.info("Authenticated via direct token", user_id=user_data.get("user_id")) return AuthInfo( user_id=user_data["user_id"], email=user_data["email"], tenant_id=user_data["tenant_id"], roles=user_data.get("roles", ["user"]) ) else: logger.warning("Token verification failed", status_code=response.status_code) raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials" ) except httpx.RequestError as e: logger.error("Auth service unavailable", error=str(e)) raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Authentication service unavailable" )