{"record":{"id":"4d1671d82043b58d","repo":"siyuan-note/siyuan","slug":"oidc-authorization-code-is-missing","errorCode":null,"errorMessage":"OIDC authorization code is missing","messagePattern":"OIDC authorization code is missing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc.go","lineNumber":903,"sourceCode":"\tif err := authenticateOIDCSession(c, transaction.RememberMe); err != nil {\n\t\twriteOIDCCallbackPage(c, false, oidcUserMessage())\n\t\treturn\n\t}\n\tc.Redirect(http.StatusFound, safeOIDCRedirectTarget(transaction.To))\n}\n\nfunc cleanupOIDCTransactionsLocked() {\n\tnow := time.Now()\n\tfor state, transaction := range oidcTransactions.byState {\n\t\tif now.After(transaction.ExpiresAt) {\n\t\t\tdeleteOIDCTransactionLocked(state)\n\t\t}\n\t}\n}\n\nfunc finishOIDCExchange(c *gin.Context, transaction *oidcTransaction, code string) error {\n\tif code == \"\" {\n\t\treturn errors.New(\"OIDC authorization code is missing\")\n\t}\n\tconfig := Conf.GetOIDC()\n\tprovider := transaction.Provider\n\tif transaction.Flow == oidcFlowValidate {\n\t\tif transaction.Config == nil || provider == nil {\n\t\t\treturn errors.New(\"OIDC validation configuration is missing\")\n\t\t}\n\t\tconfig = transaction.Config\n\t} else {\n\t\tvar err error\n\t\tprovider, err = getOIDCProvider(c.Request.Context(), transaction.RedirectURL)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\texchangeContext, cancel := context.WithTimeout(c.Request.Context(), oidcExchangeTimeout)\n\tdefer cancel()\n\tclaims, err := provider.Exchange(exchangeContext, code, transaction.CodeVerifier, transaction.Nonce)","sourceCodeStart":885,"sourceCodeEnd":921,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/oidc.go#L885-L921","documentation":"finishOIDCExchange was invoked with an empty authorization code. The IdP is supposed to redirect with ?code=...; an absent code usually means the IdP sent an error/error_description instead, or the wrong redirect URI was used so the code was dropped.","triggerScenarios":"IdP redirects to /api/system/oidc/callback with no code parameter (commonly with ?error=...); client forwards a callback that lost the code; user denied consent at the IdP.","commonSituations":"Redirect URI mismatch at the IdP (IdP rejects and returns an error); IdP scope/client misconfiguration; user clicked Deny on the consent screen.","solutions":["Check the IdP's redirect URI list contains exactly the configured public URL (https://<host>/api/system/oidc/callback).","Inspect the full callback URL the IdP produced - look for error=/error_description= and address the IdP-side cause.","Restart the flow and complete consent."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Surface IdP errors clearly before the kernel's generic 'code missing'.\nif c.Query(\"error\") != \"\" {\n    return fmt.Errorf(\"IdP returned error: %s (%s)\", c.Query(\"error\"), c.Query(\"error_description\"))\n}\nif c.Query(\"code\") == \"\" {\n    return errors.New(\"IdP callback missing authorization code\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the IdP redirect URI list matches the configured SiYuan URL exactly.","Inspect full callback URLs for error=/error_description= when debugging.","Confirm the user actually consents at the IdP rather than denying."],"tags":["oidc","authentication","idp","redirect-url"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}