docs/oidc: warn about Authentik's built-in groups scope duplicating claims
Some checks failed
Tests / test (push) Has been cancelled

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).
This commit is contained in:
Ryan Malloy 2026-06-06 10:31:23 -06:00
parent eefc99e807
commit da2878e8fb

View File

@ -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: