feat(auth): verify machine tokens for admin read APIs
CI / validate (push) Successful in 6m49s
Release / release (push) Successful in 11m24s

This commit is contained in:
dmg
2026-09-10 14:53:47 -04:00
parent 2402e9e42d
commit c2ac2ad16b
12 changed files with 524 additions and 12 deletions
+8 -2
View File
@@ -1,6 +1,6 @@
# Admin OIDC setup
The admin console will use Keycloak OIDC and JWT-backed Auth.js sessions, following the established pattern in the sibling Retro application.
The admin console uses Keycloak OIDC and JWT-backed NextAuth sessions.
## Application environment
@@ -21,4 +21,10 @@ Allow exact callback and logout URLs for each environment. Avoid wildcard origin
Create the realm role `minecraft-account-manager-admin` and assign it directly or through an admin group. Ensure realm roles are emitted in `realm_access.roles`.
The admin console will reject sign-in when the required role is absent, even when Keycloak authentication itself succeeds.
The admin console rejects sign-in when the required role is absent, even when Keycloak authentication itself succeeds. Existing browser sign-in accepts realm or configured-client roles; this behavior is unchanged.
## Read-only machine API access
The [admin API guard](admin-api-authentication.md) independently verifies signed Keycloak access tokens. Machine tokens must include `KEYCLOAK_CLIENT_ID` in `aud` and `KEYCLOAK_REQUIRED_ROLE` in `resource_access[KEYCLOAK_CLIENT_ID].roles`. A realm role alone is **not** sufficient for bearer access. The identity provider must emit both the portal audience and this client role; a token's `azp` is not an audience substitute.
Verification uses the HTTPS issuer's `/protocol/openid-connect/certs` JWKS endpoint and RS256 only. It does not use `KEYCLOAK_CLIENT_SECRET`, exchange tokens, or create a browser session. Browser client configuration above remains required for interactive SSO. Provisioning or changing machine clients, role/audience mappers, credentials, and production deployment requires separate operational approval; this source implementation performs none of those operations.