The admin plane had no authentication: any caller could reach its routes once they existed. This adds the plane's ONE reusable FastAPI dependency (require_admin_jwt in api/auth.py) that verifies an Authentik OIDC JWT — Bearer extraction, RS256-only signature check against the provider's JWKS, issuer, audience, required exp, and enforced nbf — and raises the SAME 401 for every failure so the response never leaks which check failed. The JWKS is fetched over httpx once per app (JwksCache, 5-minute TTL, asyncio-lock-guarded, failed fetches never cached) and tests stub the fetcher, so the suite proves signature/expiry/audience enforcement with locally generated RSA key pairs and zero network access. ApiConfig grows the three required OIDC settings (NESTQUEST_OIDC_ISSUER/AUDIENCE/JWKS_URL) under the existing fail-fast discipline: an unconfigured issuer must not silently leave the admin plane open, so from_env raises rather than inventing a default. This is authentication only — the verified claims are the dependency's return value, so the nestquest-admins group check is the next task's addition, not a rewrite. PyJWT[crypto] joins the dev dependency group (the API deployable's runtime manifest lands in Feature 17). Maestro task: e70e6bfb-085a-4781-a3f4-276355bbbd19
32 lines
890 B
TOML
32 lines
890 B
TOML
[project]
|
|
name = "nestquest"
|
|
version = "0.5.0"
|
|
description = "NestQuest Home Assistant Integration"
|
|
readme = "README.md"
|
|
requires-python = ">=3.11"
|
|
dependencies = []
|
|
|
|
[dependency-groups]
|
|
# fastapi and uvicorn live in the DEV group only so the monorepo's test
|
|
# venv can exercise the API app (tests under tests/test_api_health.py).
|
|
# They are NOT runtime dependencies of the NestQuest HA integration.
|
|
# When the API deployable is packaged (Feature 17), its runtime
|
|
# dependencies — fastapi, uvicorn, and any others — MUST be declared in
|
|
# that deployable's own dependency manifest, not here.
|
|
dev = [
|
|
"fastapi>=0.115",
|
|
"httpx>=0.28",
|
|
"jinja2>=3",
|
|
"pyjwt[crypto]>=2.9",
|
|
"pytest>=8.0.0",
|
|
"pytest-asyncio>=0.24",
|
|
"pyyaml>=6",
|
|
"uvicorn>=0.30",
|
|
"voluptuous>=0.14",
|
|
]
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests"]
|
|
pythonpath = ["."]
|
|
asyncio_mode = "auto"
|