juanfont/headscale · warning

oidc state parameter is too short

Error message

oidc state parameter is too short

What it means

Returned by extractCodeAndStateParamFromRequest (hscontrol/oidc.go:457-459) when the OIDC callback 'state' query parameter is shorter than cookieNamePrefixLen (6 characters). The guard exists because getCookieName splices the first 6 characters of the state into a cookie name; a shorter value would panic with a slice out-of-range. HTTP 400 'invalid state parameter'.

Source

Thrown at hscontrol/oidc.go:44

const (
	randomByteSize           = 16
	defaultOAuthOptionsCount = 3
	authCacheExpiration      = time.Minute * 15

	// authCacheMaxEntries bounds the OIDC state→[AuthInfo] cache to prevent
	// unauthenticated cache-fill DoS via repeated /register/{auth_id} or
	// /auth/{auth_id} GETs that mint OIDC state cookies.
	authCacheMaxEntries = 1024

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

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Do not construct callback URLs by hand; always start login via the /register endpoint so the IdP echoes back the full server-generated state
  2. Check for URL-truncating proxies/load balancers between the browser and headscale
  3. Verify the IdP's redirect/configuration does not rewrite or shorten the state parameter
Defensive patterns

Strategy: validation

Validate before calling

func validStateLen(state string) bool { return len(state) >= 6 }

Prevention

When it happens

Trigger: A GET to /oidc/callback (or /register OIDC flow callback) with ?state=abc or any state under 6 chars, e.g. a hand-edited, truncated, or manually constructed callback URL.

Common situations: Manual testing of the callback endpoint; misconfigured IdP that echoes a custom/truncated state; a proxy or load balancer truncating query parameters; copy-paste of a callback URL cut off mid-state.

Related errors


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