{"record":{"id":"c9ffdd1c96e01e38","repo":"semaphoreui/semaphore","slug":"oidc-sign-in-failed-state-mismatch-try-signing-i","errorCode":null,"errorMessage":"OIDC sign-in failed: state mismatch. Try signing in again.","messagePattern":"OIDC sign-in failed: state mismatch\\. Try signing in again\\.","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"api/login.go","lineNumber":878,"sourceCode":"\tb, err := base64.URLEncoding.DecodeString(s)\n\n\tif err != nil {\n\t\tlog.Error(err.Error())\n\t\thttp.Error(w, \"OIDC sign-in failed: invalid state. Try signing in again.\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tvar stateData oAuthState\n\terr = json.Unmarshal(b, &stateData)\n\n\tif err != nil {\n\t\tlog.Error(err.Error())\n\t\thttp.Error(w, \"OIDC sign-in failed: invalid state. Try signing in again.\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif stateData.Csrf != oauthState.Value {\n\t\thttp.Error(w, \"OIDC sign-in failed: state mismatch. Try signing in again.\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tctx := context.Background()\n\n\t_oidc, oauth, err := getOidcProvider(pid, ctx, r.URL.Path)\n\tif err != nil {\n\t\tlog.Error(err.Error())\n\t\thttp.Error(w, \"Failed to initialize OIDC provider. Contact your administrator.\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tprovider, ok := util.Config.OidcProviders[pid]\n\tif !ok {\n\t\tlog.Error(fmt.Errorf(\"no such provider: %s\", pid))\n\t\thttp.Error(w, \"Unknown OIDC provider.\", http.StatusNotFound)\n\t\treturn\n\t}","sourceCodeStart":860,"sourceCodeEnd":896,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/api/login.go#L860-L896","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Close other login tabs and restart the sign-in flow so only one state cookie/flow exists","Don't use the back button or bookmarked callback URLs — start a fresh login","Ensure all Semaphore replicas share the same cookie secret/config so state validation is consistent","If it recurs legitimately, check for proxies sharing/mixing cookies across users"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// before opening a second login, note that concurrent logins invalidate each other's state cookie:\n// serialize logins: only one tab may run /api/auth/oidc/<pid> at a time","typeGuard":null,"tryCatchPattern":"// on 'state mismatch' at the callback, restart sign-in from the beginning:\nwindow.location.href = '/api/auth/oidc/' + pid // fresh state + cookie pair","preventionTips":["Avoid parallel login tabs in the same browser (last cookie wins)","Never reuse bookmarked/back-button callback URLs","Keep all replicas on the same cookie-signing configuration","Complete login promptly and in a single flow to keep state and cookie in sync"],"tags":["http","oidc","csrf","state-mismatch"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}