""" Authentication middleware for gateway """ import logging from fastapi import Request, HTTPException from fastapi.responses import JSONResponse import httpx from typing import Optional from app.core.config import settings from shared.auth.jwt_handler import JWTHandler logger = logging.getLogger(__name__) # JWT handler jwt_handler = JWTHandler(settings.JWT_SECRET_KEY, settings.JWT_ALGORITHM) # Routes that don't require authentication PUBLIC_ROUTES = [ "/health", "/metrics", "/docs", "/redoc", "/openapi.json", "/api/v1/auth/login", "/api/v1/auth/register", "/api/v1/auth/refresh" ] async def auth_middleware(request: Request, call_next): """Authentication middleware""" # Check if route requires authentication if _is_public_route(request.url.path): return await call_next(request) # Get token from header token = _extract_token(request) if not token: return JSONResponse( status_code=401, content={"detail": "Authentication required"} ) # Verify token try: # First try to verify token locally payload = jwt_handler.verify_token(token) if payload: # Add user info to request state request.state.user = payload return await call_next(request) else: # Token invalid or expired, verify with auth service user_info = await _verify_with_auth_service(token) if user_info: request.state.user = user_info return await call_next(request) else: return JSONResponse( status_code=401, content={"detail": "Invalid or expired token"} ) except Exception as e: logger.error(f"Authentication error: {e}") return JSONResponse( status_code=401, content={"detail": "Authentication failed"} ) def _is_public_route(path: str) -> bool: """Check if route is public""" return any(path.startswith(route) for route in PUBLIC_ROUTES) def _extract_token(request: Request) -> Optional[str]: """Extract JWT token from request""" auth_header = request.headers.get("Authorization") if auth_header and auth_header.startswith("Bearer "): return auth_header.split(" ")[1] return None async def _verify_with_auth_service(token: str) -> Optional[dict]: """Verify token with auth service""" try: async with httpx.AsyncClient(timeout=5.0) as client: response = await client.post( f"{settings.AUTH_SERVICE_URL}/verify", headers={"Authorization": f"Bearer {token}"} ) if response.status_code == 200: return response.json() else: return None except Exception as e: logger.error(f"Auth service verification failed: {e}") return None