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
- Do not construct callback URLs by hand; always start login via the /register endpoint so the IdP echoes back the full server-generated state
- Check for URL-truncating proxies/load balancers between the browser and headscale
- 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
- Never hand-craft OIDC callback URLs
- Test proxies do not truncate query strings
- Unit-test the callback handler with short states (see oidc_test.go:209)
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
- empty OIDC callback params
- extracting ID token
- registration info not in cache
- unexpected end of container wait
- test pattern is required as first argument or use --test fla
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/5f854bb49cfc2c36.
Report an issue: GitHub.