{"record":{"id":"7713676e30eb95ed","repo":"semaphoreui/semaphore","slug":"oidc-sign-in-failed-state-cookie-is-missing-try","errorCode":null,"errorMessage":"OIDC sign-in failed: state cookie is missing. Try signing in again.","messagePattern":"OIDC sign-in failed: state cookie is missing\\. Try signing in again\\.","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"api/login.go","lineNumber":855,"sourceCode":"\n\tif webHost == \"\" {\n\t\treturn redirectPath, nil\n\t}\n\n\treturn url.JoinPath(webHost, redirectPath)\n}\n\nfunc oidcRedirect(w http.ResponseWriter, r *http.Request) {\n\tpid := mux.Vars(r)[\"provider\"]\n\toauthState, err := r.Cookie(\"oauthstate\")\n\n\t// Errors are shown as plain text at the current URL instead of a silent\n\t// redirect to the login page, so the user can see what went wrong.\n\t// Details stay in server logs.\n\n\tif err != nil {\n\t\tlog.Error(err.Error())\n\t\thttp.Error(w, \"OIDC sign-in failed: state cookie is missing. Try signing in again.\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\ts := r.FormValue(\"state\")\n\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)","sourceCodeStart":837,"sourceCodeEnd":873,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/api/login.go#L837-L873","documentation":"oidcRedirect is the OIDC callback handler. Before validating the OAuth state it reads the signed state cookie that oidcLogin set earlier (via generateStateOauthCookie). If reading/decoding that cookie fails (err != nil at this point), it responds HTTP 400 'OIDC sign-in failed: state cookie is missing. Try signing in again.' This protects against state-forgery since the cookie carries the signed CSRF value.","triggerScenarios":"Redirecting from the IdP back to the callback URL with no 'state' (oauth state) cookie present in the request — cookie expired, deleted, blocked, or the flow was started in a different browser; calling the callback URL directly without going through oidcLogin.","commonSituations":"User waiting too long so the short-lived state cookie expired; browser blocking third-party/same-site cookies; IdP redirecting to a different domain than the one that set the cookie; reverse proxy stripping cookies; user copying the callback URL into another browser.","solutions":["Restart the sign-in flow from the beginning (click login again) so a fresh state cookie is set","Enable cookies for the Semaphore domain and don't clear them mid-login","Ensure the callback URL uses the same domain/scheme that set the cookie (behind proxy, forward cookies and set correct Cookie domain/Secure settings)","Complete the sign-in promptly to avoid state-cookie expiry"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// detect that a login flow was actually started (state cookie exists) before loading callback URLs:\nif (document.cookie.indexOf('oauth_state') === -1) { /* restart login from /api/auth/oidc/<pid> */ }","typeGuard":null,"tryCatchPattern":"// on the login page, if callback reports missing state cookie:\nif (location.search.includes('error=state_cookie_missing')) {\n    window.location.href = '/api/auth/oidc/' + pid // full restart of the flow\n}","preventionTips":["Complete sign-in in one browser/tab without long pauses (cookie is short-lived)","Don't block or clear cookies for the Semaphore domain during login","Ensure IdP redirect URIs use the same domain that set the state cookie","Configure proxies to pass Set-Cookie/Cookie headers untouched"],"tags":["http","oidc","cookies","callback"],"backgroundTag":"missing-required-argument","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"}