siyuan-note/siyuan · error

OAuth revocation endpoint must use HTTPS or loopback HTTP

Error message

OAuth revocation endpoint must use HTTPS or loopback HTTP

What it means

The configured OAuth revocation endpoint URL is not HTTPS and is not a loopback HTTP address. `isSecureOAuthEndpoint` requires HTTPS for any non-loopback host so that refresh/access tokens sent during revocation are never transmitted in cleartext over a network.

Source

Thrown at kernel/mcp/client/oauth.go:741

			for _, credential := range credentials {
				if err := revokeOAuthCredential(ctx, client, credential); err != nil {
					revokeErr = errors.Join(revokeErr, err)
				}
			}
			if revokeErr != nil {
				logging.LogWarnf("mcp oauth: revoke credentials for server [%s] failed: %s", serverID, revokeErr)
			}
		}()
	}
	return nil
}

func revokeOAuthCredential(ctx context.Context, client *http.Client, credential oauthCredential) error {
	if credential.RevocationEndpoint == "" {
		return nil
	}
	if !isSecureOAuthEndpoint(credential.RevocationEndpoint) {
		return fmt.Errorf("OAuth revocation endpoint must use HTTPS or loopback HTTP")
	}
	var result error
	for _, token := range []struct {
		value string
		hint  string
	}{{credential.RefreshToken, "refresh_token"}, {credential.AccessToken, "access_token"}} {
		if token.value == "" {
			continue
		}
		values := url.Values{"token": {token.value}, "token_type_hint": {token.hint}}
		applyOAuthClientAuthentication(values, nil, credential)
		req, err := http.NewRequestWithContext(ctx, http.MethodPost, credential.RevocationEndpoint, strings.NewReader(values.Encode()))
		if err != nil {
			result = errors.Join(result, err)
			continue
		}
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		applyOAuthClientAuthentication(nil, req, credential)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Correct the IdP's published revocation endpoint metadata to use `https://`.
  2. If testing locally, point the revocation endpoint at `http://localhost:...` or `http://127.0.0.1:...` (loopback is permitted).
  3. Ensure the reverse proxy advertises the public `https://` URL in metadata even when it forwards internally over HTTP.

Example fix

// before
RevocationEndpoint: "http://idp.internal:8080/revoke"
// after
RevocationEndpoint: "https://idp.internal/revoke"
Defensive patterns

Strategy: validation

Validate before calling

// Enforce HTTPS (or loopback) before storing a revocation endpoint.
func assertSecureRevokeURL(endpoint string) error {
    u, err := url.Parse(endpoint)
    if err != nil {
        return err
    }
    if u.Scheme == "https" {
        return nil
    }
    if u.Scheme == "http" && (strings.EqualFold(u.Hostname(), "localhost") || net.ParseIP(u.Hostname()).IsLoopback()) {
        return nil
    }
    return errors.New("revocation endpoint must use HTTPS or loopback HTTP")
}

Prevention

When it happens

Trigger: `revokeOAuthCredential` is called (during `DisconnectMCPOAuth`) with a credential whose `RevocationEndpoint` scheme is `http://` pointing at a non-localhost host, or an unsupported scheme like `ftp`.

Common situations: On-premise/dev IdP advertising an `http://` revocation endpoint. A mis-pasted revocation URL missing the `s` in `https://`. A reverse proxy that terminates TLS but whose published metadata still lists the internal `http://` URL.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/c40f9a8090040649. Report an issue: GitHub.