Generalize Keycloak role extraction to a configurable claim path - #750
Open
CodeBuildder wants to merge 1 commit into
Open
Generalize Keycloak role extraction to a configurable claim path#750CodeBuildder wants to merge 1 commit into
CodeBuildder wants to merge 1 commit into
Conversation
The Keycloak authenticator hardcoded roles to realm_access.roles via a typed KeycloakClaim struct, so any deployment putting roles elsewhere in the token (e.g. resource_access.<clientId>.roles, Keycloak's own location for client-scoped roles) had no way to authenticate those roles at all. Add an optional roleclaim config field (dot-separated path) that defaults to realm_access.roles when unset, so existing deployments are unaffected. AuthenticateRequest now parses into generic jwt.MapClaims and walks the configured path via a new extractRoles helper instead of relying on a fixed struct shape. Fixes spiffe#434 Signed-off-by: Kaushik Kumaran <47471121+CodeBuildder@users.noreply.github.com>
CodeBuildder
requested review from
lumjjb,
maia-iyer,
mamy-CS and
mrsabath
as code owners
August 1, 2026 06:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #434
Root cause
pkg/agent/authentication/authenticator/keycloak.gohardcoded role extraction torealm_access.rolesvia a typedKeycloakClaim/RealmAccessSubclaimstruct bound with JSON tags (AuthenticateRequest, usingjwt.ParseWithClaims). There was no way to configure a different claim path, so any deployment where roles live somewhere else in the token - most commonlyresource_access.<clientId>.roles, Keycloak's own default location for client-scoped roles - couldn't authenticate those roles at all.Fix
roleclaimHCL config field (dot-separated path), defaulting torealm_access.roleswhen unset, so no existing deployment's config needs to change.AuthenticateRequestnow parses into genericjwt.MapClaimsinstead of the fixed struct; a newextractRoleshelper walks the configured path. Fails closed (no roles, not a crash) if the path doesn't resolve or resolves to something other than a string list.docs/plugins/plugin_server_authentication_keycloak.mdanddocs/conf/agent/full.confto document the new option.Related prior work:
generalize_auth(a branch pushed but never opened as a PR, last touched 2024-05-02) attempted a similar dot-path approach. This PR takes the same direction but makesroleclaimoptional - the branch made it required, which would have broken every existing deployment on upgrade - and adds test coverage the branch didn't have.How this was verified
realm_access.rolesandresource_access.account.roles, and confirmed the pre-fix code only ever saw the first.keycloak_test.go: default path still extracts roles exactly as before (regression case), an arbitrary nested path extracts correctly, a flat single-segment path works, and fail-closed behavior for a missing/wrong-shaped claim.roleclaim = "resource_access.account.roles"now correctly surfaces the previously-invisible roles.go build ./...,go test ./...,golangci-lint run --timeout 7m(v2.1.6, matching CI) all pass.