can1357/oh-my-pi · error · Error

HTTPS loopback redirect URIs require oauth.callbackPort to p

Error message

HTTPS loopback redirect URIs require oauth.callbackPort to point at the local HTTP callback listener behind your TLS terminator

What it means

validateRedirectConfig handles the case where the redirect URI is an HTTPS loopback URL. The library's local callback listener speaks plain HTTP, so with an https redirect URI you must terminate TLS separately (reverse proxy/TLS terminator) and run the local listener on a different port, which must be declared via oauth.callbackPort. If callbackPort is missing, the library cannot know where to bind the HTTP listener and throws.

Source

Thrown at packages/coding-agent/src/mcp/oauth-flow.ts:164

}

function parseRedirectUri(redirectUri: string | undefined): URL | undefined {
	return redirectUri ? new URL(redirectUri) : undefined;
}

function getUriPort(uri: URL): number {
	if (uri.port !== "") return Number(uri.port);
	return uri.protocol === "https:" ? 443 : 80;
}

function validateRedirectConfig(config: MCPOAuthConfig, redirectUri: string | undefined): void {
	const parsed = parseRedirectUri(redirectUri);
	if (parsed?.protocol !== "https:" || !isLoopbackHostname(parsed.hostname)) {
		return;
	}

	if (config.callbackPort === undefined) {
		throw new Error(
			"HTTPS loopback redirect URIs require oauth.callbackPort to point at the local HTTP callback listener behind your TLS terminator",
		);
	}

	if (config.callbackPort === getUriPort(parsed)) {
		throw new Error(
			"HTTPS loopback redirect URIs cannot reuse the same local port; terminate TLS separately and forward to oauth.callbackPort",
		);
	}
}

function resolveCallbackPort(callbackPort: number | undefined, redirectUri: string | undefined): number {
	if (callbackPort !== undefined) return callbackPort;

	const parsed = parseRedirectUri(redirectUri);
	if (parsed?.protocol !== "http:" || !isLoopbackHostname(parsed.hostname)) {
		return DEFAULT_PORT;
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Set oauth.callbackPort to the port your local HTTP callback listener should bind, distinct from the HTTPS port in the redirect URI
  2. Configure your TLS terminator to forward from the https redirect port to the callbackPort
  3. Alternatively, switch the redirect URI to plain http://localhost:<port> if your provider allows insecure loopback redirects

Example fix

// before
"oauth": { "redirectUri": "https://localhost:8443/auth/callback" }
// after: local HTTP listener on 1455, TLS terminator forwards 8443 -> 1455
"oauth": { "redirectUri": "https://localhost:8443/auth/callback", "callbackPort": 1455 }
Defensive patterns

Strategy: validation

Validate before calling

const uri = config.oauth?.redirectUri;
if (uri) {
  const p = new URL(uri);
  const loopback = ['localhost', '127.0.0.1', '[::1]'].includes(p.hostname);
  if (p.protocol === 'https:' && loopback && config.oauth.callbackPort === undefined) {
    throw new Error('https loopback redirectUri requires oauth.callbackPort for the local HTTP listener');
  }
}

Try / catch

try {
  startOAuthFlow(config);
} catch (e) {
  if (e.message.includes('HTTPS loopback redirect URIs require oauth.callbackPort')) {
    throw new Error('Add oauth.callbackPort (a plain-HTTP port behind your TLS terminator) to your MCP oauth config');
  } else throw e;
}

Prevention

When it happens

Trigger: Configuring an https:// loopback redirect URI (e.g. https://localhost:8443/callback) in oauth.redirectUri while leaving oauth.callbackPort undefined, then resolving callback options via resolveCallbackOptions.

Common situations: Users behind a local TLS terminator (caddy, nginx) who registered an https loopback redirect with their provider but forgot to tell the library which plain-HTTP port the local listener should use; assuming the library can serve HTTPS itself.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/eb8bf9a2c967cb5a. Report an issue: GitHub.