From 8cf28b9da3dd64325bd42798fec2ea32ff91830f Mon Sep 17 00:00:00 2001 From: Sergiy Kulanov Date: Tue, 28 Jul 2026 09:34:26 +0300 Subject: [PATCH] EPMDEDP-17184: fix: drop unregistered groups scope that broke Grafana OIDC login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grafana forwards its `scopes` setting verbatim as the OAuth `scope` parameter, and Keycloak validates every entry against the client scopes registered in the realm. No `groups` client scope exists there, so Keycloak rejected the authorize request with "Invalid scopes" and Grafana surfaced "Login provider denied login request" — SSO was unusable. The group membership protocol mapper on the client did not make `groups` a requestable scope; client mappers always emit their claim and are never negotiated via the scope parameter. Since org roles are derived from the `roles` claim and `allowed_groups` gating was never enabled, the groups claim had no consumer, so both the mapper and the unused `groups_attribute_path` are removed alongside the scope. Group-based gating can be reintroduced later by restoring the mapper and setting `allowed_groups`, without listing `groups` in `scopes`. Signed-off-by: Sergiy Kulanov --- deploy-templates/README.md | 3 +-- .../templates/keycloak/keycloakclient-grafana.yaml | 10 ---------- deploy-templates/values.yaml | 5 +---- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/deploy-templates/README.md b/deploy-templates/README.md index 7b03f88..341e701 100644 --- a/deploy-templates/README.md +++ b/deploy-templates/README.md @@ -126,10 +126,9 @@ and `config/grafana/dashboards/`). See the repository root `README.md` for the f | grafana."grafana.ini"."auth.generic_oauth".client_id | string | `"grafana-claude-code-telemetry"` | | | grafana."grafana.ini"."auth.generic_oauth".email_attribute_path | string | `"email"` | | | grafana."grafana.ini"."auth.generic_oauth".enabled | bool | `false` | | -| grafana."grafana.ini"."auth.generic_oauth".groups_attribute_path | string | `"groups"` | | | grafana."grafana.ini"."auth.generic_oauth".name | string | `"SSO"` | | | grafana."grafana.ini"."auth.generic_oauth".role_attribute_path | string | `"contains(roles[*], 'administrator') && 'Admin' || contains(roles[*], 'developer') && 'Editor' || 'Viewer'"` | | -| grafana."grafana.ini"."auth.generic_oauth".scopes | string | `"openid profile email roles groups"` | | +| grafana."grafana.ini"."auth.generic_oauth".scopes | string | `"openid profile email roles"` | | | grafana."grafana.ini"."auth.generic_oauth".token_url | string | `"https://keycloak.example.com/realms/main/protocol/openid-connect/token"` | | | grafana."grafana.ini".analytics.check_for_updates | bool | `false` | | | grafana."grafana.ini".server.root_url | string | `"https://grafana.example.com"` | | diff --git a/deploy-templates/templates/keycloak/keycloakclient-grafana.yaml b/deploy-templates/templates/keycloak/keycloakclient-grafana.yaml index c3c9f27..29629b1 100644 --- a/deploy-templates/templates/keycloak/keycloakclient-grafana.yaml +++ b/deploy-templates/templates/keycloak/keycloakclient-grafana.yaml @@ -29,14 +29,4 @@ spec: "id.token.claim": "true" "userinfo.token.claim": "true" "multivalued": "true" - # Expose group membership so Grafana can gate access (allowed_groups) and map org roles. - - name: groups - protocol: openid-connect - protocolMapper: "oidc-group-membership-mapper" - config: - "access.token.claim": "true" - "claim.name": "groups" - "id.token.claim": "true" - "userinfo.token.claim": "true" - "full.path": "false" {{- end }} diff --git a/deploy-templates/values.yaml b/deploy-templates/values.yaml index 7c26b9a..52856b8 100644 --- a/deploy-templates/values.yaml +++ b/deploy-templates/values.yaml @@ -327,7 +327,7 @@ grafana: allow_sign_up: true client_id: grafana-claude-code-telemetry # Client secret comes from the GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET env var (envValueFrom above) — never inline it here. - scopes: openid profile email roles groups + scopes: openid profile email roles email_attribute_path: email # Keycloak endpoints (any OIDC provider exposes equivalents): auth_url: https://keycloak.example.com/realms/main/protocol/openid-connect/auth @@ -335,9 +335,6 @@ grafana: api_url: https://keycloak.example.com/realms/main/protocol/openid-connect/userinfo # Map provider roles/groups to Grafana org roles (JMESPath over the userinfo/token claims). role_attribute_path: contains(roles[*], 'administrator') && 'Admin' || contains(roles[*], 'developer') && 'Editor' || 'Viewer' - # Restrict sign-in to members of specific groups (requires the `groups` claim): - groups_attribute_path: groups - # allowed_groups: /grafana-users # Grafana admin credentials from the same grafana-sso Secret. # admin: