Keycloak OIDC Provider#
Overview#
--provider=keycloak-oidc is the current, recommended provider for Keycloak integration. It wraps the generic OIDCProvider and adds Keycloak-specific logic for extracting roles from access tokens and injecting them into the session's Groups field alongside OIDC group claims from the ID token. The legacy --provider=keycloak is a separate, simpler implementation with no OIDC support and should not be used for new deployments.
Two Claim Sources: Access Token vs. ID Token#
The key behavioral difference in keycloak-oidc is that it reads claims from two different tokens:
| Claim type | Source token | Mechanism |
|---|---|---|
Roles (realm_access.roles, resource_access.<client>.roles) | Access token (JWT) | extractRoles() parses the raw access token payload directly |
Groups (groups claim) | ID token | buildSessionFromClaims via the GroupsClaim field using --oidc-groups-claim |
Role extraction (access token)#
extractRoles base64-decodes the access token's JWT payload and deserializes it into accessClaims :
- Realm roles come from
realm_access.roles. - Client roles come from
resource_access.<clientName>.rolesformatted as<client>:<role>.
Each role is prefixed with role: (e.g., role:myrole, role:myclient:myrole) via formatRole before being appended to session.Groups. This prefix distinguishes them from OIDC groups and allows the same Authorize() logic to handle both.
extractRoles is called from EnrichSession, RefreshSession, and CreateSessionFromToken, so roles are always kept in sync.
Group extraction (ID token)#
Groups flow through the standard OIDC path: buildSessionFromClaims reads the claim named by GroupsClaim (configured via --oidc-groups-claim) from the ID token and stores the values in session.Groups β without any prefix . Groups are not available by default; a groups client scope must be created in Keycloak and attached to the client .
Authorization Flags#
| Flag | Matches | Stored as |
|---|---|---|
--allowed-role=myrole | Realm role | role:myrole in AllowedGroups |
--allowed-role=myclient:myrole | Client role | role:myclient:myrole in AllowedGroups |
--allowed-group=/groupname | OIDC group | /groupname in AllowedGroups |
Roles specified via --allowed-role are registered (with the role: prefix) into AllowedGroups at startup via addAllowedRoles. The shared Authorize() method does a simple set lookup against session.Groups , so roles and groups are checked with the same mechanism.
Keycloak Configuration Prerequisites#
- Audience mapper (required): The OIDC client must include the client ID in the
audclaim. Without it, token validation fails. Configure in Keycloak: Clients β Client scopes β dedicated β Mappers . - Roles (included by default): A standard Keycloak install ships the "roles" client scope, which adds
realm_accessandresource_accessto access tokens . No extra setup needed for--allowed-role. - Groups (manual setup required): Create a "groups" client scope, attach it to the client, and set
--oidc-groups-claim=groupsto enable--allowed-group. - Issuer URL: Keycloak 17+ uses
https://<host>/realms/<realm>; pre-17 useshttps://<host>/auth/realms/<realm>. - PKCE (recommended):
--code-challenge-method=S256.
Legacy keycloak Provider (Deprecated)#
--provider=keycloak (providers/keycloak.go) does not use OIDC. It calls a userinfo/profile HTTP endpoint at EnrichSession time and reads a groups JSON field from the response. It has no role extraction, no token verification via JWKS, and requires manual URL configuration. Prefer keycloak-oidc for all new setups.
Key Source Files#
| File | Purpose |
|---|---|
providers/keycloak_oidc.go | Main keycloak-oidc provider; role extraction logic |
providers/keycloak.go | Legacy keycloak provider |
providers/provider_data.go | buildSessionFromClaims, GroupsClaim field |
providers/providers.go | Provider factory; OIDC verifier setup |
| Official docs | Keycloak client setup walkthrough |