juanfont/headscale · warning
empty OIDC callback params
Error message
empty OIDC callback params
What it means
Returned by extractCodeAndStateParamFromRequest (hscontrol/oidc.go:449-451) when the OIDC callback request lacks the 'code' or 'state' query parameter. HTTP 400 'missing code or state parameter'. It is the first validation on the callback path before any IdP interaction happens.
Source
Thrown at hscontrol/oidc.go:47
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 {
AuthID types.AuthID
Verifier *string
Registration boolView on GitHub (pinned to 565fd254d0)
Solutions
- Handle IdP error redirects upstream: if the callback carries error/error_description, surface that to the user instead of retrying
- Ensure headscale is registered with the IdP with the exact issuer/client/redirect configuration so successful logins always include code and state
- Exclude the callback endpoint from health probes
Defensive patterns
Strategy: validation
Validate before calling
if r := mux.CurrentRoute(r); isCallback(r) { q := r.URL.Query(); if q.Get("code") == "" || q.Get("state") == "" { http.Error(w, "login incomplete", http.StatusBadRequest); return } } Prevention
- Handle IdP error redirects (?error=...) before reaching headscale
- Keep monitors and bookmarks off the callback endpoint
- Register the exact callback URL with the IdP
When it happens
Trigger: GET /oidc/callback with no query string, or with only one of code/state — e.g. an IdP error redirect (?error=access_denied) that omits both, or a user bookmarking the bare callback URL.
Common situations: IdP redirects with an error payload instead of code/state (user cancelled login, client misregistration); direct navigation/bookmark of the callback; monitoring probes hitting the endpoint.
Related errors
- oidc state parameter is too short
- 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/fab72c4438ace2ef.
Report an issue: GitHub.