Use case
When Headscale is configured with Google (or other IdPs that don’t emit preferred_username), the user record may be created without a usable username. This causes friction when:
- Writing ACLs that reference users by username.
- Generating predictable MagicDNS names that depend on usernames.
- Operating CLIs/UX where usernames are shown or selected.
A configurable mapping from OIDC claims would let operators decide which claim(s) should define the username (e.g., email local-part), avoiding incomplete users and ACL hassles.
Description
Introduce an optional setting to control how a username is derived from OIDC claims, trying candidates in order until one is valid:
- Config:
oidc.username_claim_order (array of strings)
- Supported values:
preferred_username (OIDC standard)
email_localpart (portion before @)
email (full email address)
name (display name)
sub (subject/ID)
- Default order (if unset):
[preferred_username, email_localpart, email, name, sub]
- Validation: candidates must pass Headscale’s
ValidateUsername (start with a letter; letters, digits, -, ., _; at most one @). Invalid candidates are skipped.
This keeps current behavior for IdPs that already provide a valid preferred_username, and provides a safe, opinionated fallback path for providers like Google.
Contribution
How can it be implemented?
Suggested approach (for the design doc):
- Add
UsernameClaimOrder []string to OIDCConfig with YAML key username_claim_order.
- Implement a helper that derives a username from
OIDCClaims using the configured order and ValidateUsername.
- Apply the helper in the OIDC callback flow before persisting/updating the user.
- Tests covering: preferred_username present; fallback to email local-part; email/name/sub fallbacks; validation rejections.
- Docs: configuration reference, OIDC guide (with a Google note and example configuration).
Use case
When Headscale is configured with Google (or other IdPs that don’t emit
preferred_username), the user record may be created without a usable username. This causes friction when:A configurable mapping from OIDC claims would let operators decide which claim(s) should define the username (e.g., email local-part), avoiding incomplete users and ACL hassles.
Description
Introduce an optional setting to control how a username is derived from OIDC claims, trying candidates in order until one is valid:
oidc.username_claim_order(array of strings)preferred_username(OIDC standard)email_localpart(portion before@)email(full email address)name(display name)sub(subject/ID)[preferred_username, email_localpart, email, name, sub]ValidateUsername(start with a letter; letters, digits,-,.,_; at most one@). Invalid candidates are skipped.This keeps current behavior for IdPs that already provide a valid
preferred_username, and provides a safe, opinionated fallback path for providers like Google.Contribution
How can it be implemented?
Suggested approach (for the design doc):
UsernameClaimOrder []stringtoOIDCConfigwith YAML keyusername_claim_order.OIDCClaimsusing the configured order andValidateUsername.