Files
bakery-ia/gateway/app/middleware/auth.py

107 lines
3.5 KiB
Python
Raw Normal View History

"""
Authentication middleware for gateway
"""
import logging
2025-07-17 19:54:04 +02:00
from fastapi import Request
from fastapi.responses import JSONResponse
2025-07-17 19:54:04 +02:00
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import Response
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"
]
2025-07-17 19:54:04 +02:00
class AuthMiddleware(BaseHTTPMiddleware):
"""Authentication middleware class"""
2025-07-17 19:54:04 +02:00
async def dispatch(self, request: Request, call_next) -> Response:
"""Process request with authentication"""
2025-07-17 19:54:04 +02:00
# Check if route requires authentication
if self._is_public_route(request.url.path):
return await call_next(request)
2025-07-17 19:54:04 +02:00
# Get token from header
token = self._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:
2025-07-17 19:54:04 +02:00
# Token invalid or expired, verify with auth service
user_info = await self._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"}
)
2025-07-17 19:54:04 +02:00
def _is_public_route(self, path: str) -> bool:
"""Check if route is public"""
return any(path.startswith(route) for route in PUBLIC_ROUTES)
2025-07-17 19:54:04 +02:00
def _extract_token(self, 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
2025-07-17 19:54:04 +02:00
async def _verify_with_auth_service(self, 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}"}
)
2025-07-17 19:54:04 +02:00
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