Spaces:
Runtime error
Runtime error
File size: 2,249 Bytes
16c19b8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | """Autenticação por API key (header X-API-Key ou Authorization: Bearer)."""
from __future__ import annotations
import secrets
from typing import Callable
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from config import settings
_PUBLIC_PREFIXES = (
"/health/live",
"/docs",
"/redoc",
"/openapi.json",
)
def valid_api_keys() -> frozenset[str]:
raw = settings.api_key
if not raw or not raw.strip():
return frozenset()
return frozenset(part.strip() for part in raw.split(",") if part.strip())
def api_key_enabled() -> bool:
return bool(valid_api_keys())
def extract_api_key(request: Request) -> str | None:
header = request.headers.get("X-API-Key")
if header and header.strip():
return header.strip()
auth = request.headers.get("Authorization")
if auth and auth.lower().startswith("bearer "):
token = auth[7:].strip()
return token or None
return None
def is_public_path(path: str) -> bool:
return any(path == prefix or path.startswith(f"{prefix}/") for prefix in _PUBLIC_PREFIXES)
def verify_api_key(provided: str | None, *, keys: frozenset[str] | None = None) -> bool:
expected = keys if keys is not None else valid_api_keys()
if not expected:
return True
if not provided:
return False
return any(secrets.compare_digest(provided, key) for key in expected)
class ApiKeyMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: Callable) -> Response:
keys = valid_api_keys()
if not keys:
return await call_next(request)
if request.method == "OPTIONS" or is_public_path(request.url.path):
return await call_next(request)
if not verify_api_key(extract_api_key(request), keys=keys):
return JSONResponse(
status_code=401,
content={
"detail": "API key inválida ou ausente. Use o header X-API-Key ou Authorization: Bearer <key>.",
},
headers={"WWW-Authenticate": "Bearer"},
)
return await call_next(request)
|