juanfont/headscale · error

authenticated principal is not in any allowed group

Error message

authenticated principal is not in any allowed group

What it means

Returned by the OIDC group-authorization check (hscontrol/oidc.go:541) when allowed_groups is configured but the authenticated principal's groups (from the ID token or userinfo) intersect none of them. HTTP 401 'unauthorised group'. This is an intentional access denial, not a malfunction.

Source

Thrown at hscontrol/oidc.go:53

	// cookieNamePrefixLen is the number of leading characters from a
	// state/nonce value that [getCookieName] splices into the cookie name.
	// State and nonce values that are shorter than this are rejected at
	// the callback boundary so [getCookieName] cannot panic on a slice
	// out-of-range.
	cookieNamePrefixLen = 6
)

var errOIDCStateTooShort = errors.New("oidc state parameter is too short")

var (
	errEmptyOIDCCallbackParams = errors.New("empty OIDC callback params")
	errNoOIDCIDToken           = errors.New("extracting ID token")
	errNoOIDCRegistrationInfo  = errors.New("registration info not in cache")
	errOIDCAllowedDomains      = errors.New(
		"authenticated principal does not match any allowed domain",
	)
	errOIDCAllowedGroups = errors.New("authenticated principal is not in any allowed group")
	errOIDCAllowedUsers  = errors.New(
		"authenticated principal does not match any allowed user",
	)
	errOIDCUnverifiedEmail = errors.New("authenticated principal has an unverified email")
	errInvalidPKCEMethod   = errors.New("invalid pkce.method")
)

// AuthInfo contains both auth ID and verifier information for OIDC validation.
type AuthInfo struct {
	AuthID       types.AuthID
	Verifier     *string
	Registration bool
}

type AuthProviderOIDC struct {
	h         *Headscale
	serverURL string
	cfg       *types.OIDCConfig

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Decode the ID token (jwt.io or 'headscale' debug logs) and confirm the actual group strings and claim name
  2. Set oidc.allowed_groups to the exact group values the IdP emits, or map the claim via your IdP
  3. Add the user to an allowed group in the IdP, or relax the list if the restriction is unintended

Example fix

# before
oidc:
  allowed_groups: ["Engineering"]

# after (match the exact claim value)
oidc:
  allowed_groups: ["engineering", "idp-engineering"]
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: oidc.allowed_groups set in headscale.yaml and the user's token carries a groups claim that matches none of the configured entries (exact string match).

Common situations: Group claim name mismatch: headscale reads 'groups' by default (configurable), while IdPs may emit 'roles' or 'memberOf'; group strings differ in case or path form (e.g. 'engineering' vs 'idp://engineering'); user genuinely not in any allowed group; allowed_groups set without allowed_domains fallback.

Understand the failure class

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/789b6f35da25278f. Report an issue: GitHub.