v1 runs on static tokens only (API_PRINCIPALS). The Keycloak path was built,
tested, and deliberately parked (2026-07-14) as closed draft PRs with branches
kept — extending is “reopen + rebase”, not redesign.
Design (already validated)
The resolver in api/auth.py accepts one credential kind today. The hybrid was:
same Authorization: Bearer header; a credential with two dots is verified as a
Keycloak JWT (PyJWT + cached JWKS, RS256, iss/aud=vxdata-api/exp), then
claims map to the same Policy: groups claim via group_grants, azp via
client_grants. Unknown claims = empty policy (authenticated, reads nothing).
Config lives in an optional oidc block inside API_PRINCIPALS:
{"tokens": {...}, "oidc": {"issuer": ".../realms/virdx", "audience": "vxdata-api",
"group_grants": {"vxdata-admins": [...], "vxdata-users": [...]},
"client_grants": {"some-bot": [...]}}}Keycloak stays pure authentication; permissions never leave vxData config.
The parked pieces (reopen in this order)
- API resolver — mono PR #236 (branch
feat/vxdata-oidc-resolver): JWT verification + claims mapping + per-request principal logging; fake-JWKS test matrix (expired/wrong-aud/wrong-iss/tampered). Dormant without theoidcblock, so it can merge any time. - Realm clients — infra_ansible PR #75 (
felix/vxdata-keycloak-clients):vxdata-clipublic client with device grant,vxdata-apiaudience-only client, audience mappers ondashboard/vxdata-cli, templateattributespassthrough. Applies on Keycloak restart; coordinate with anything touching thedashboardclient (e.g. old #59). - Groups — infra_iam PR #28:
vxdata-admins(felix, moritz) /vxdata-users(eng roster). Propagates via auth-sync -> FreeIPA -> LDAP mapper (~5 min; tokens pick groups up on next login). - Human CLI login — mono #237’s dropped commit (
vxdata login, device grant, token cache/refresh in~/.config/vxdata/, transport fallback): recover from the #237 branch history. - Dashboard PKCE — mono PR #238 (
feat/dashboard-keycloak-login): oidc-spa + fetch-wrapper bearer + dashboard-api user-token forwarding. Known issue to fix on reopen:VITE_OIDC_ISSUERis baked at build time but one image serves staging+prod — needs runtime injection (thewindow.__APP_BASE__pattern). Also add the staging redirect URI in #75.
Until then
Humans and bots use static tokens (VXDATA_TOKEN); audit granularity is the
token subject. When enforcement reaches the dashboard: reopen #238, or interim
edge-injected static token (identity collapses to “dashboard”).