juanfont/headscale · error

authenticated principal has an unverified email

Error message

authenticated principal has an unverified email

What it means

Returned by the OIDC email verification check (hscontrol/oidc.go:586) when the authenticated principal's email has email_verified=false (or the flag is absent and force_verified_email is enabled). HTTP 401 'unverified email'. Headscale refuses to map an unverified email onto a tailnet identity.

Source

Thrown at hscontrol/oidc.go:57

	// 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

	// authCache holds auth information between the auth and the callback
	// steps. It is a bounded [expirable.LRU] keyed by OIDC state, evicting oldest
	// entries to keep the cache footprint constant under attack.

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Have the user verify their email in the IdP, then retry login
  2. If the IdP cannot provide verification, disable the force-verified-email requirement in headscale.yaml
  3. For dev IdPs, enable the 'email as verified' option (e.g. Keycloak's 'Verify email' off / Dex mock OIDC)
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: oidc.force_verified_email (or equivalent policy requiring verified email) is on and the IdP reports the user's email as not verified, or the IdP omits the email_verified claim entirely.

Common situations: Development IdPs (Keycloak/Dex dev mode) that never mark emails verified; users with pending email confirmation; IdP that does not implement email_verified at all; use_email_as_name or domain restrictions layered on top.

Understand the failure class

Related errors


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