{"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":"warning","filePath":"kernel/model/oidc.go","lineNumber":750,"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":732,"sourceCodeEnd":768,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/oidc.go#L732-L768","documentation":"At claim time the transaction's ConfigVersion no longer matches the live config version. claimOIDCTransaction deletes the transaction and aborts because claims and token exchange must run against a single consistent OIDC configuration.","triggerScenarios":"Admin saved OIDC settings between a user's /api/system/oidc/start and the IdP callback; config rewritten by automation during a login.","commonSituations":"Operator iterating on OIDC config while users are logging in; scripted config churn racing with real users.","solutions":["Restart the login flow so it binds to the current config version.","Stop editing OIDC settings during active logins.","Schedule config changes in a maintenance window."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Config changed mid-login: restart the flow rather than retrying the stale state.\nif err != nil && strings.Contains(err.Error(), \"configuration changed during login\") {\n    restartOIDCFlow(c)\n}","preventionTips":["Freeze OIDC config edits while users are logging in.","Route config changes through a maintenance window.","Audit for automation that rewrites OIDC config outside change windows."],"tags":["oidc","configuration","concurrency","race-condition"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}