fire-planner/fire_planner/api/auth.py

43 lines
1.5 KiB
Python
Raw Normal View History

"""Bearer-token auth shared across routers.
Two modes, picked at startup from env:
- API_BEARER_TOKEN set enforce Bearer auth on all write/compute paths
- API_BEARER_TOKEN unset (dev) no auth, log a one-time warning
Read endpoints (`/networth`, `/scenarios`, ...) skip auth entirely so
the frontend can render without juggling tokens during dev. Lock those
down later via Authentik-fronted ingress when we deploy.
"""
from __future__ import annotations
import hmac
import logging
import os
from fastapi import Header, HTTPException
log = logging.getLogger(__name__)
_warned_unauth = False
def _read_token() -> str | None:
return os.environ.get("API_BEARER_TOKEN") or os.environ.get("RECOMPUTE_BEARER_TOKEN")
async def require_bearer(authorization: str | None = Header(default=None)) -> None:
"""FastAPI dependency: enforce bearer auth IF API_BEARER_TOKEN is set."""
expected = _read_token()
if not expected:
global _warned_unauth
if not _warned_unauth:
log.warning("API_BEARER_TOKEN unset — write endpoints are open. "
"Set it before exposing this service.")
_warned_unauth = True
return
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="Missing bearer token")
token = authorization.removeprefix("Bearer ")
if not hmac.compare_digest(token, expected):
raise HTTPException(status_code=401, detail="Invalid token")