semaphoreui/semaphore · error

OIDC sign-in failed: state mismatch. Try signing in again.

Error message

OIDC sign-in failed: state mismatch. Try signing in again.

What it means

The final CSRF check of the OIDC flow: the decoded state's Csrf field is compared to the value stored in the state cookie (oauthState.Value). If they differ, the handler returns HTTP 400 'OIDC sign-in failed: state mismatch. Try signing in again.' This is the standard OAuth2 state-mismatch protection against CSRF/login-replay attacks and aborts the flow whenever the returned state was not the one issued to this browser.

Solutions

  1. Close other login tabs and restart the sign-in flow so only one state cookie/flow exists
  2. Don't use the back button or bookmarked callback URLs — start a fresh login
  3. Ensure all Semaphore replicas share the same cookie secret/config so state validation is consistent
  4. If it recurs legitimately, check for proxies sharing/mixing cookies across users
Defensive patterns

Strategy: retry

Validate before calling

// before opening a second login, note that concurrent logins invalidate each other's state cookie:
// serialize logins: only one tab may run /api/auth/oidc/<pid> at a time

Try / catch

// on 'state mismatch' at the callback, restart sign-in from the beginning:
window.location.href = '/api/auth/oidc/' + pid // fresh state + cookie pair

Prevention

When it happens

Trigger: Callback whose state payload is valid but does not equal the value in the browser's state cookie — user has multiple concurrent login tabs overwriting the state cookie; cookie for a different/older flow; replayed callback URL from another session; cross-site callback forgery attempt.

Common situations: User opening the login page in two tabs (each starts a flow, last cookie wins, first IdP return mismatches); load-balanced instances with different cookie signing; user using back button to redo an old callback; a deliberate CSRF probe (expected rejection).

Related errors


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/c9ffdd1c96e01e38. Report an issue: GitHub.

Appendix: source

Thrown at api/login.go:878

	b, err := base64.URLEncoding.DecodeString(s)

	if err != nil {
		log.Error(err.Error())
		http.Error(w, "OIDC sign-in failed: invalid state. Try signing in again.", http.StatusBadRequest)
		return
	}

	var stateData oAuthState
	err = json.Unmarshal(b, &stateData)

	if err != nil {
		log.Error(err.Error())
		http.Error(w, "OIDC sign-in failed: invalid state. Try signing in again.", http.StatusBadRequest)
		return
	}

	if stateData.Csrf != oauthState.Value {
		http.Error(w, "OIDC sign-in failed: state mismatch. Try signing in again.", http.StatusBadRequest)
		return
	}

	ctx := context.Background()

	_oidc, oauth, err := getOidcProvider(pid, ctx, r.URL.Path)
	if err != nil {
		log.Error(err.Error())
		http.Error(w, "Failed to initialize OIDC provider. Contact your administrator.", http.StatusInternalServerError)
		return
	}

	provider, ok := util.Config.OidcProviders[pid]
	if !ok {
		log.Error(fmt.Errorf("no such provider: %s", pid))
		http.Error(w, "Unknown OIDC provider.", http.StatusNotFound)
		return
	}

View on GitHub (pinned to 1774ccb71a)