From da2878e8fbeb5dbc2d61a4ef1112a03398c062e2 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sat, 6 Jun 2026 10:31:23 -0600 Subject: [PATCH] docs/oidc: warn about Authentik's built-in groups scope duplicating claims Authentik 2025.8 ships a default 'groups' scope that emits the user's ak_groups as the 'groups' claim automatically. Adding a custom scope mapping with the same scope_name causes Authentik to concatenate both emitters' output, producing duplicated group names in the users.groups column. Document the symptom and the fix, plus a sibling note that Keycloak needs the opposite (explicit mapper required to emit anything). --- docs/ref/oidc.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/ref/oidc.md b/docs/ref/oidc.md index ab55e28f..78e542b0 100644 --- a/docs/ref/oidc.md +++ b/docs/ref/oidc.md @@ -274,6 +274,38 @@ oidc: scope: ["openid", "profile", "email", "groups"] ``` +### Identity provider configuration + +The `groups` scope is not part of the OpenID Connect core specification, so +different identity providers handle it differently. Headscale stores whatever +the provider emits, so misconfiguration on the IdP side shows up as garbage +in the `users.groups` column. + +!!! warning "Authentik: do not attach a custom `groups` property mapping" + + Authentik ships a built-in `groups` scope that emits the user's + `ak_groups.all()` as the `groups` claim automatically. If you also + attach a custom OAuth2 scope mapping with `scope_name: groups` to the + provider, Authentik runs *both* emitters and concatenates their outputs. + A user in groups `admins` and `users` ends up with + `["admins","users","admins","users"]` in the `users.groups` column. + + The fix is to remove the custom mapping from the provider's property + mappings — the built-in `groups` scope alone produces exactly what + Headscale wants. Verified against Authentik 2025.8. + +!!! info "Keycloak and other providers" + + Keycloak does not emit a `groups` claim by default. Create a "Group + Membership" mapper on the client scope `groups` (or directly on the + client) with token claim name `groups` and tick *Add to ID token*, + *Add to access token*, and *Add to userinfo*. + + Other providers may need similar explicit configuration. If group + membership is missing from the stored `users.groups` column, check + the wire data first — the most common failure mode is "the provider + isn't emitting the claim", not "headscale isn't reading it". + ### Headplane Integration When using [Headplane](https://github.com/tale/headplane) as a web interface for Headscale, OIDC groups enable automatic role-based access control: