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).
Add a Groups column on the User model populated from the OIDC
'groups' claim at login, and surface it through the gRPC/REST
User message so external tools (Headplane) can read group
membership without reaching into the database.
- proto: add 'repeated string groups = 9' to v1.User.
- types.User: Groups text column, GetGroups/SetGroups JSON helpers,
FromClaim populates from claims.Groups. The existing
FlexibleStringSlice on OIDCClaims.Groups already handles
JumpCloud-style single-string emission.
- types.User.Proto(): populate v1.User.Groups via GetGroups().
- db migration 202505141323: add the column BEFORE the existing
202505141324 migration that loads users via the struct, so the
schema is in place before any migration touches the User type.
- db migration 202507021200: extend the inline CREATE TABLE users
and INSERT INTO users ... SELECT FROM users_old to carry the
new column through the SQLite schema-recreation step.
- schema.sql: declare the column so squibble.Validate accepts
databases produced by the new migration chain. Verified against
all 7 historical sqlite dumps in hscontrol/db/testdata/sqlite.
- types.UserView, types_clone.go: regenerated to expose Groups.
- config-example.yaml, docs/ref/oidc.md: note the 'groups' scope
and the role the column plays for external integrations.
- integration/oidc_groups_test.go: verify the round-trip via
headscale.ListUsers() for users with multi-group, single-group,
and empty group memberships.
- Mention policy as generic term that covers ACLs or Grants
- Refresh routes policy examples
- Remove Headscale specific exit node separation. Use via instead.
Fixes: #3087
- Rename from acl.md to policy.md and setup redirect links
- Mention both ACLs and Grants
- Remove most old ACL docs and replace with links to Tailscale docs
- Add "Getting started" section
- Add section about notable differences
Introduce a structured NodeConfig that replaces the flat
EphemeralNodeInactivityTimeout field with a nested Node section.
Add node.expiry config (default: no expiry) as the unified default key
expiry for all non-tagged nodes regardless of registration method.
Remove oidc.expiry entirely — node.expiry now applies to OIDC nodes
the same as all other registration methods. Using oidc.expiry in the
config is a hard error. determineNodeExpiry() returns nil (no expiry)
unless use_expiry_from_token is enabled, letting state.go apply the
node.expiry default uniformly.
The old ephemeral_node_inactivity_timeout key is preserved for
backwards compatibility.
Updates #1711
Just the tags tag:router and tag:exit are owned by alice. Upon join,
those nodes will have their ownership transferred from alice to the
system user "tagged-devices".
Document the API endpoint and the built-in swagger docs at /swagger. The
remote control docs are just a use case for gRPC - move it in the API
docs and update links to it.
The UserInfo endpoint is always queried since 5d8a2c2.
This allows to use all OIDC related features without any extra
configuration on Authelia.
For Keycloak, its sufficient to add the groups mapper to the userinfo
endpoint.
Share/Contribute Headscale Zabbix Monitoring scripts and templates.
Thank you for the awesome application to everyone involved in Headscale's development!