{"record":{"id":"47089e6b3dbee950","repo":"siyuan-note/siyuan","slug":"oidc-state-is-missing","errorCode":null,"errorMessage":"OIDC state is missing","messagePattern":"OIDC state is missing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc.go","lineNumber":738,"sourceCode":"\t\t}\n\t\tif transaction.Binding != \"\" && candidate.Binding == transaction.Binding {\n\t\t\tperBinding++\n\t\t}\n\t}\n\tif perIP >= oidcTransactionPerIP || perBinding >= oidcTransactionPerBind {\n\t\treturn errors.New(\"too many pending OIDC login transactions\")\n\t}\n\toidcTransactions.byState[transaction.State] = transaction\n\tif transaction.PollToken != \"\" {\n\t\toidcTransactions.byPoll[transaction.PollToken] = transaction.State\n\t}\n\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}","sourceCodeStart":720,"sourceCodeEnd":756,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/oidc.go#L720-L756","documentation":"claimOIDCTransaction received an empty state string. The state correlates the IdP callback to the stored transaction; without it nothing can be looked up and the request is rejected immediately before any locking.","triggerScenarios":"A request reaches /api/system/oidc/callback or the desktop/validate poll without a state query/body parameter; the IdP dropped state from its redirect; a manual/curled callback omitted it.","commonSituations":"IdP mis-configured to strip state; client bug losing the parameter between start and callback; direct browser hit on the callback URL.","solutions":["Restart the OIDC flow via /api/system/oidc/start to obtain a fresh state.","Verify the IdP redirect configuration preserves the state parameter end to end.","Check the client passes the state through unchanged from start to callback."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Reject callbacks/polls with no state before reaching the kernel's claim path.\nif c.Query(\"state\") == \"\" {\n    return errors.New(\"missing state parameter\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always begin a flow with /api/system/oidc/start to obtain a state.","Verify the IdP preserves the state parameter in its redirect.","Pass state through the client unchanged from start to callback."],"tags":["oidc","authentication","protocol","state"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}