Accept Tessera (Keycloak-compatible) OIDC tokens as API bearer

Adds an additive bearer-verification path: verify RS256 access tokens against
Tessera's JWKS (iss/aud/exp), map sub/preferred_username/email + roles
(realm_access.roles, resource_access.<audience>.roles) to the app's identity.
Existing auth (API keys / app JWTs / sessions) is unchanged. Issuer + audience
are env-configurable. Validated end-to-end against the local sim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-06-02 15:11:30 +01:00
parent b02b1706b6
commit 7b2f2fae30
3 changed files with 213 additions and 0 deletions

View File

@@ -71,6 +71,16 @@ type Config struct {
OIDCIssuer string
OIDCClientID string
// OIDCBearerIssuer + OIDCBearerAudience configure acceptance of
// access tokens issued by the external "Tessera" OIDC provider
// (Keycloak-compatible) as API bearer tokens. ADDITIVE to the
// existing agent-key + browser-session auth paths. A request with
// `Authorization: Bearer <jwt>` whose JWT verifies against this
// issuer's JWKS and carries this audience is treated as a CallerUser.
// Env: OIDC_BEARER_ISSUER, OIDC_BEARER_AUDIENCE.
OIDCBearerIssuer string
OIDCBearerAudience string
// OIDC_ONLY: when "true", disables the dev-bypass auth path on
// every browser-facing route. Use this in prod once the OIDC
// realm + client are configured so a leaked dev token can't
@@ -112,6 +122,8 @@ func LoadFromEnv() (*Config, error) {
DialecticAdminAPIKey: os.Getenv("DIALECTIC_ADMIN_API_KEY"),
OIDCIssuer: os.Getenv("OIDC_ISSUER"),
OIDCClientID: os.Getenv("OIDC_CLIENT_ID"),
OIDCBearerIssuer: getenv("OIDC_BEARER_ISSUER", "https://login.hangman-lab.top/realms/Hangman-Lab"),
OIDCBearerAudience: getenv("OIDC_BEARER_AUDIENCE", "dialectic-prod"),
OIDCOnly: os.Getenv("OIDC_ONLY") == "true",
SessionSigningKey: os.Getenv("SESSION_SIGNING_KEY"),
}