{"record":{"id":"0dd859671bb627b8","repo":"siyuan-note/siyuan","slug":"oidc-configuration-changed-during-login","errorCode":null,"errorMessage":"OIDC configuration changed during login","messagePattern":"OIDC configuration changed during login","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc.go","lineNumber":694,"sourceCode":"\treturn nil\n}\n\nfunc claimOIDCTransaction(ctx context.Context, state, binding string,\n\tallowDesktopWithoutBinding bool) (*oidcTransaction, bool, error) {\n\tif state == \"\" {\n\t\treturn nil, false, errors.New(\"OIDC state is missing\")\n\t}\n\toidcTransactions.Lock()\n\tcleanupOIDCTransactionsLocked()\n\ttransaction := oidcTransactions.byState[state]\n\tif transaction == nil {\n\t\toidcTransactions.Unlock()\n\t\treturn nil, false, errors.New(\"OIDC login transaction was not found or has expired\")\n\t}\n\tif transaction.ConfigVersion != oidcConfigurationVersion(Conf.GetOIDC()) {\n\t\tdeleteOIDCTransactionLocked(state)\n\t\toidcTransactions.Unlock()\n\t\treturn nil, false, errors.New(\"OIDC configuration changed during login\")\n\t}\n\tif !(allowDesktopWithoutBinding && (transaction.Flow == oidcFlowDesktop || transaction.Flow == oidcFlowValidate)) &&\n\t\t(binding == \"\" || binding != transaction.Binding) {\n\t\toidcTransactions.Unlock()\n\t\treturn nil, false, errors.New(\"OIDC login binding does not match\")\n\t}\n\tif !transaction.Claimed {\n\t\ttransaction.Claimed = true\n\t\tcopy := *transaction\n\t\toidcTransactions.Unlock()\n\t\treturn &copy, false, nil\n\t}\n\tdone := transaction.Done\n\toidcTransactions.Unlock()\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn nil, false, fmt.Errorf(\"wait for OIDC login transaction failed: %w\", ctx.Err())","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/oidc.go#L676-L712","documentation":"Each OIDC transaction records the OIDC configuration version at creation time. claimOIDCTransaction compares it against the current configuration version and, on mismatch, deletes the transaction and fails with this error. This prevents a login started under one IdP configuration from being completed after the settings changed (e.g. swapped client ID or issuer), which would otherwise mix tokens/identities across configurations.","triggerScenarios":"User starts an OIDC login, then someone saves new OIDC settings (issuer, client ID/secret, endpoints); the callback from the old authorization request then arrives and is rejected.","commonSituations":"Administrator rotates OIDC credentials or switches IdP while users are mid-login; config sync pushed new settings between login start and callback.","solutions":["Start a new OIDC login after the configuration change — the fresh transaction will carry the new version","Coordinate OIDC setting changes with active users to avoid mid-flight logins","Retry the login once; do not reuse the old authorization URL"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if tx.ConfigVersion != oidcConfigurationVersion(Conf.GetOIDC()) {\n    // transaction is stale; restart the login instead of claiming\n}","typeGuard":null,"tryCatchPattern":"tx, _, err := claimOIDCTransaction(ctx, state, binding, false)\nif err != nil && strings.Contains(err.Error(), \"configuration changed\") {\n    // surface a 'settings changed, please sign in again' message and restart the flow\n}","preventionTips":["Schedule OIDC configuration changes outside active login periods","Notify users to sign in again after OIDC settings change","Avoid automated config rewrites during business hours"],"tags":["oidc","config-change","invalidation"],"backgroundTag":"invalid-state-transition","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}