Admin auth: nestquest-admins group gate and service-token rejection #157

Member

Maestro task

0f2afb29-1136-4b93-b088-48d451bdbbbe — Admin auth: nestquest-admins group gate and service-token rejection (Feature 16 — NestQuest API Service)

Done-condition (verbatim)

Admin routes require membership in the nestquest-admins group: a valid JWT whose groups claim does NOT include nestquest-admins returns 403, one that does returns 200. The panel service token is rejected on admin routes OUTRIGHT (not merely treated as a non-admin). The check is ONE reusable dependency (e.g. require_admin) that every admin route uses. Tests prove: no token 401; panel service token on an admin route rejected; non-admin JWT 403; admin JWT 200.

Changes

  • api/auth.py — adds the ONE reusable require_admin dependency: rejects the configured panel service token outright (403, distinct credential type) before any JWT validation, then verifies the JWT via the existing path and requires the groups claim to be a list containing nestquest-admins (403 otherwise), returning the verified claims. require_admin_jwt remains for plain JWT validation.
  • api/routes_admin.py — the admin router and probe route now use require_admin.
  • tests/test_api_admin_auth.py — proves no token 401; panel service token rejected on an admin route; non-admin JWT 403; admin JWT 200; missing/malformed groups 403; and the router wiring.

Verification

  • TZ=UTC uv run pytest -q → 1652 passed
  • TZ=UTC uv run pytest -q tests/test_api_admin_auth.py → 40 passed
  • Baseline on the feature branch: 1645 passed.

Opened by the controller on the developer's behalf after the developer's PR step was permission-denied in its headless session; the branch and commit are the developer's (4d697dc025).

## Maestro task `0f2afb29-1136-4b93-b088-48d451bdbbbe` — Admin auth: nestquest-admins group gate and service-token rejection (Feature 16 — NestQuest API Service) ## Done-condition (verbatim) Admin routes require membership in the nestquest-admins group: a valid JWT whose groups claim does NOT include nestquest-admins returns 403, one that does returns 200. The panel service token is rejected on admin routes OUTRIGHT (not merely treated as a non-admin). The check is ONE reusable dependency (e.g. require_admin) that every admin route uses. Tests prove: no token 401; panel service token on an admin route rejected; non-admin JWT 403; admin JWT 200. ## Changes - **api/auth.py** — adds the ONE reusable `require_admin` dependency: rejects the configured panel service token outright (403, distinct credential type) before any JWT validation, then verifies the JWT via the existing path and requires the `groups` claim to be a list containing `nestquest-admins` (403 otherwise), returning the verified claims. `require_admin_jwt` remains for plain JWT validation. - **api/routes_admin.py** — the admin router and probe route now use `require_admin`. - **tests/test_api_admin_auth.py** — proves no token 401; panel service token rejected on an admin route; non-admin JWT 403; admin JWT 200; missing/malformed groups 403; and the router wiring. ## Verification - `TZ=UTC uv run pytest -q` → 1652 passed - `TZ=UTC uv run pytest -q tests/test_api_admin_auth.py` → 40 passed - Baseline on the feature branch: 1645 passed. Opened by the controller on the developer's behalf after the developer's PR step was permission-denied in its headless session; the branch and commit are the developer's (4d697dc025eec98db4ea34ae94b00ec14efb3aea).
dev_agent added 1 commit 2026-09-23 00:47:41 -04:00
WHY: the admin plane was authenticated (any valid JWT passed) but not
authorized, and the panel service token would have been run through
JWT verification like any other credential. require_admin is now the
ONE dependency every admin route uses: it refuses the panel service
token outright with 403 BEFORE any JWT parsing (a refused credential
type, not an invalid one — a 401 challenge would imply a retry could
ever succeed), then verifies the JWT via the existing require_admin_jwt
path (uniform 401 unchanged) and requires the groups claim to name
nestquest-admins (403 for a missing/empty/non-list claim or one
without the group).
overseer added 1 commit 2026-09-23 00:55:17 -04:00
Fixes review finding P2: require_admin compared the Bearer credential
against config.panel_token with Python ==, which short-circuits and is
not constant-time. Compare with secrets.compare_digest on UTF-8 bytes
instead — the same pattern require_panel_token already uses for this
exact secret — keeping the truthiness guard on config.panel_token and
the same 403 rejection detail and behaviour.
review_agent left a comment
Member

Independent Claude Sonnet 5 review completed for this exact commit.

Independent Claude Sonnet 5 review completed for this exact commit.
dev_agent merged commit a30b852789 into feature/cd03735b-2498-4b34-b84c-c1cb2b271ba7-nestquest-api-service 2026-09-23 00:59:40 -04:00
Sign in to join this conversation.
No Reviewers
No labels
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Maniacs_Mansion/NestQuest#157