siyuan-note/siyuan · error

A loopback OIDC redirect URL is required for local access

Error message

A loopback OIDC redirect URL is required for local access

What it means

When building the desktop loopback redirect URL, effectiveOIDCRedirectURL double-checks that the request's Host is a local address via util.IsLocalHost; a non-loopback Host (e.g. a LAN IP or domain) is rejected because desktop login must redirect back to the same machine.

Solutions

  1. Open SiYuan using http://127.0.0.1:<port> (or localhost) before starting desktop OIDC login
  2. Fix reverse-proxy Host header preservation (proxy_set_header Host 127.0.0.1 or original local host)
  3. For non-local access, switch to the web/mobile flow with a validated public redirect URL

Example fix

// before
Host: 192.168.1.10:6806
// after
Host: 127.0.0.1:6806
Defensive patterns

Strategy: validation

Validate before calling

const hostOk = /^(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/.test(location.host);
if (!hostOk) console.warn('use http://127.0.0.1:<port> for desktop OIDC login');

Try / catch

if !util.IsLocalHost(c.Request.Host) {
    // surface guidance: open via http://127.0.0.1:<port> before OIDC login
}

Prevention

When it happens

Trigger: effectiveOIDCRedirectURL on the desktop flow with a request whose Host header is not local (util.IsLocalHost(host) == false) even if the remote-IP check passed; called from OIDCStart and oidcValidationRedirectURL.

Common situations: Reaching the kernel through a hostname, docker container name, or LAN IP while still being treated as an intra-LAN client; proxy rewriting the Host header away from 127.0.0.1.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/b8a839c852f17ca9. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/oidc.go:557

}

func effectiveOIDCRedirectURL(c *gin.Context, flow string) (string, error) {
	if flow == oidcFlowMobile {
		return oidcMobileRedirectURL, nil
	}
	if flow == oidcFlowWeb && !IsLocalRequest(c) {
		return validatePublicOIDCRedirectURL(Conf.GetOIDC().RedirectURL)
	}
	if !IsLocalRequest(c) {
		return "", errors.New("Desktop OIDC login requires a loopback listener")
	}
	scheme := "http"
	if c.Request.TLS != nil || c.GetHeader("X-Forwarded-Proto") == "https" {
		scheme = "https"
	}
	host := c.Request.Host
	if !util.IsLocalHost(host) {
		return "", errors.New("A loopback OIDC redirect URL is required for local access")
	}
	return scheme + "://" + host + "/api/system/oidc/callback", nil
}

func oidcValidationRedirectURL(c *gin.Context, config *conf.OIDC, mobile bool) (string, error) {
	if mobile {
		return oidcMobileRedirectURL, nil
	}
	if config.RedirectURL != "" {
		return validatePublicOIDCRedirectURL(config.RedirectURL)
	}
	return effectiveOIDCRedirectURL(c, oidcFlowDesktop)
}

func validatePublicOIDCRedirectURL(redirectURL string) (string, error) {
	if redirectURL == "" {
		return "", errors.New("A public HTTPS OIDC redirect URL is required for remote access")
	}

View on GitHub (pinned to 9f775e8a12)