{"record":{"id":"910f209801dde44f","repo":"siyuan-note/siyuan","slug":"desktop-oidc-login-requires-a-loopback-listener","errorCode":null,"errorMessage":"Desktop OIDC login requires a loopback listener","messagePattern":"Desktop OIDC login requires a loopback listener","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc.go","lineNumber":605,"sourceCode":"\t\treturn nil\n\t}\n\tif requireRemoteRedirect {\n\t\tif _, err := validatePublicOIDCRedirectURL(config.RedirectURL); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn ValidateOIDCProviderConfiguration(ctx, config)\n}\n\nfunc effectiveOIDCRedirectURL(c *gin.Context, flow string) (string, error) {\n\tif flow == oidcFlowMobile {\n\t\treturn oidcMobileRedirectURL, nil\n\t}\n\tif flow == oidcFlowWeb && !IsLocalRequest(c) {\n\t\treturn validatePublicOIDCRedirectURL(Conf.GetOIDC().RedirectURL)\n\t}\n\tif !IsLocalRequest(c) {\n\t\treturn \"\", errors.New(\"Desktop OIDC login requires a loopback listener\")\n\t}\n\tscheme := \"http\"\n\tif c.Request.TLS != nil || c.GetHeader(\"X-Forwarded-Proto\") == \"https\" {\n\t\tscheme = \"https\"\n\t}\n\thost := c.Request.Host\n\tif !util.IsLocalHost(host) {\n\t\treturn \"\", errors.New(\"A loopback OIDC redirect URL is required for local access\")\n\t}\n\treturn scheme + \"://\" + host + \"/api/system/oidc/callback\", nil\n}\n\nfunc oidcValidationRedirectURL(c *gin.Context, config *conf.OIDC, mobile bool) (string, error) {\n\tif mobile {\n\t\treturn oidcMobileRedirectURL, nil\n\t}\n\tif config.RedirectURL != \"\" {\n\t\treturn validatePublicOIDCRedirectURL(config.RedirectURL)","sourceCodeStart":587,"sourceCodeEnd":623,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/oidc.go#L587-L623","documentation":"Returned by effectiveOIDCRedirectURL (kernel/model/oidc.go:605) for the desktop flow when the incoming request is not local (IsLocalRequest(c) is false) and the flow is not the web flow. The desktop OIDC login relies on a loopback listener on the kernel host; a non-local requester cannot reach that listener, so the redirect URL cannot be constructed.","triggerScenarios":"Initiating the desktop OIDC login flow from a browser pointed at a non-loopback SiYuan address (LAN IP or hostname), or via a reverse proxy that obscures the local origin.","commonSituations":"User accesses SiYuan over the LAN/VPN and clicks the desktop OIDC login button; the desktop flow's redirect URL would point at a loopback the user's browser cannot reach.","solutions":["Access SiYuan from the same host via 127.0.0.1/localhost when using desktop OIDC login.","For non-local access, use the web OIDC flow which goes through validatePublicOIDCRedirectURL with a configured public redirect.","Configure the OIDC RedirectURL (public HTTPS) in settings so the web flow works remotely."],"exampleFix":"# before — desktop login over LAN\nopen http://192.168.1.5:6806 -> click desktop OIDC -> error\n# after — desktop login from the kernel host\nopen http://127.0.0.1:6806 -> click desktop OIDC -> ok\n# or — use the web flow with a configured public redirect URL","handlingStrategy":"validation","validationCode":"if flow == oidcFlowDesktop && !model.IsLocalRequest(c) {\n    return errors.New(\"desktop OIDC login must originate from localhost\")\n}","typeGuard":"func desktopOK(c *gin.Context) bool { return model.IsLocalRequest(c) }","tryCatchPattern":null,"preventionTips":["Reserve the desktop OIDC flow for same-host access (127.0.0.1).","For remote users, configure a public HTTPS redirect URL and route them through the web flow."],"tags":["oidc","desktop","loopback","redirect-url","network"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}