""" app/api/auth.py Optional Bearer-token authentication middleware. When API_TOKEN is set in the environment, every request to a protected path must include the header: Authorization: Bearer Public paths (no auth required even when a token is configured): GET / (serves index.html — UI bootstrap) GET /healthz (health check) GET /static/* (JS / CSS / fonts — UI assets) GET /auth-check (lets the UI know whether auth is required) All other paths — /api/*, /mcp, /docs, /openapi.json — are protected. """ from __future__ import annotations import logging from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request from starlette.responses import JSONResponse logger = logging.getLogger(__name__) # Paths that are always accessible without a token _PUBLIC_EXACT = frozenset({"/", "/healthz", "/auth-check"}) _PUBLIC_PREFIXES = ("/static/",) class TokenAuthMiddleware(BaseHTTPMiddleware): def __init__(self, app, token: str) -> None: super().__init__(app) self._token = token if token: logger.info("Token authentication ENABLED") else: logger.info("Token authentication disabled (set API_TOKEN to enable)") async def dispatch(self, request: Request, call_next): # Auth disabled → pass through if not self._token: return await call_next(request) path = request.url.path # Always-public paths if path in _PUBLIC_EXACT: return await call_next(request) for prefix in _PUBLIC_PREFIXES: if path.startswith(prefix): return await call_next(request) # Validate Bearer token auth_header = request.headers.get("Authorization", "") if auth_header == f"Bearer {self._token}": return await call_next(request) logger.warning("Unauthorized request: %s %s", request.method, path) return JSONResponse( {"detail": "Unauthorized — provide a valid Bearer token"}, status_code=401, )