Skip to content

[bug] Local Executor behind a public HTTPS reverse proxy uses loopback-only CIMD client metadata #2073

Description

@maciekb

Executor version

1.6.10

How do you run Executor?

CLI

Operating system

Linux

Integration involved

MCP Server

What happened

Local Executor behind a public HTTPS reverse proxy uses loopback-only CIMD client metadata

Summary

When the local/supervised Executor is exposed through a public HTTPS reverse proxy and EXECUTOR_WEB_BASE_URL points to that public origin, OAuth providers supporting Client ID Metadata Documents (CIMD) can reject authorization with:

Invalid redirect URI. The redirect URI provided does not match any registered URI for this client.

I can reproduce this with the official Linear MCP server.

Environment

  • Executor CLI running as the supervised Linux user service
  • Executor itself bound locally
  • Nginx reverse proxy providing public HTTPS
  • EXECUTOR_WEB_BASE_URL=https://executor-mcp.example.com
  • MCP server: https://mcp.linear.app/mcp

Actual behavior

The authorization request generated for Linear contains:

client_id=https://executor.sh/api/oauth/client-id-metadata/local.json
redirect_uri=https://executor-mcp.example.com/api/oauth/callback

Linear rejects the request with:

Invalid redirect URI. The redirect URI provided does not match any registered URI for this client.

This appears correct from the provider's point of view.

local.json is intentionally a native/loopback CIMD document. In packages/core/api/src/server/oauth-client-metadata.ts, localLoopbackRedirectUris() advertises only:

http://127.0.0.1/api/oauth/callback
http://localhost/api/oauth/callback
http://[::1]/api/oauth/callback

However, the local frontend is compiled with:

VITE_EXECUTOR_CIMD_CLIENT_ID_METADATA_BASE_URL=https://executor.sh

and oauthClientIdMetadataDocumentUrl() in packages/react/src/plugins/oauth-sign-in.tsx therefore selects:

https://executor.sh/api/oauth/client-id-metadata/local.json

even when the UI is actually being accessed through a non-loopback public origin.

At the same time, the OAuth callback is correctly derived from the public Executor origin:

https://executor-mcp.example.com/api/oauth/callback

This results in a CIMD client whose declared redirect_uris do not contain the redirect_uri sent in the authorization request.

Root cause

oauthClientIdMetadataDocumentUrl() currently prefers the configured hosted metadata base whenever it exists:

const hostedBaseUrl =
  options?.hostedBaseUrl ?? configuredClientIdMetadataBaseUrl();

if (hostedBaseUrl) {
  return new URL(
    OAUTH_CLIENT_ID_METADATA_DOCUMENT_LOCAL_PATH,
    hostedBaseUrl,
  ).toString();
}

The local Vite build defaults that base to:

https://executor.sh

Consequently, a local Executor accessed through a public reverse-proxy origin still identifies itself as the hosted loopback/native CIMD client.

Suggested fix

Use the hosted local.json document only when the current browser origin is actually loopback.

For non-loopback origins, fall through to the existing current-origin default.json behavior.

Conceptually:

const isLoopbackHostname = (hostname: string): boolean =>
  hostname === "localhost" ||
  hostname === "127.0.0.1" ||
  hostname === "::1" ||
  hostname === "[::1]";

const useHostedLocalDocument =
  hostedBaseUrl &&
  (typeof window === "undefined" ||
    isLoopbackHostname(window.location.hostname));

if (useHostedLocalDocument) {
  return new URL(
    OAUTH_CLIENT_ID_METADATA_DOCUMENT_LOCAL_PATH,
    hostedBaseUrl,
  ).toString();
}

This keeps the existing CLI/Desktop loopback behavior while allowing a reverse-proxied local Executor to expose a CIMD document whose redirect URI matches its public callback.

What you expected

Expected behavior

When local Executor is accessed directly on loopback, the current hosted native-client behavior should remain unchanged:

http://localhost:4788
    ->
https://executor.sh/api/oauth/client-id-metadata/local.json

When the same local Executor is accessed through a non-loopback HTTPS origin, the client metadata document should instead be served from that origin, for example:

https://executor-mcp.example.com/api/oauth/client-id-metadata/default.json

That document then advertises:

https://executor-mcp.example.com/api/oauth/callback

which matches the redirect URI used by the OAuth flow.

Steps to reproduce

Reproduction

  1. Install Executor CLI as the supervised service.

  2. Put it behind an HTTPS reverse proxy.

  3. Configure:

    EXECUTOR_WEB_BASE_URL=https://executor-mcp.example.com
    
  4. Open Executor through that HTTPS URL.

  5. Add the official Linear MCP server:

    https://mcp.linear.app/mcp
    
  6. Start OAuth authorization.

The resulting Linear /authorize request contains approximately:

client_id=https%3A%2F%2Fexecutor.sh%2Fapi%2Foauth%2Fclient-id-metadata%2Flocal.json

redirect_uri=https%3A%2F%2Fexecutor-mcp.example.com%2Fapi%2Foauth%2Fcallback
  1. Linear responds with:
Invalid redirect URI. The redirect URI provided does not match any registered URI for this client.

Diagnostics / logs

No response

Before you submit

  • I searched the open issues for a duplicate.
  • I removed all keys, tokens, and credentials from this report.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions