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
- Have the user verify their email in the IdP, then retry login
- If the IdP cannot provide verification, disable the force-verified-email requirement in headscale.yaml
- For dev IdPs, enable the 'email as verified' option (e.g. Keycloak's 'Verify email' off / Dex mock OIDC)
Defensive patterns
Strategy: validation
Prevention
- Require verified emails at the IdP before enabling the check
- For dev IdPs, mark emails verified by default
- Document the requirement in user onboarding
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- authenticated principal is not in any allowed group
- user mismatch
- oidc state parameter is too short
- empty OIDC callback params
- extracting ID token
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/2c6a5a531e29666e.
Report an issue: GitHub.