Skip to main content

OAuth Support

At a Glance

  • Covers end-to-end OAuth 2.0 Authorization Code with PKCE for upstream MCP servers.
  • Supports automatic discovery from WWW-Authenticate responses and RFC 8414 metadata.
  • Implements dynamic client registration (RFC 7591) and resource indicators (RFC 8707).
  • Persists client credentials and tokens to mcp_settings.json for reconnects.

When MCPHub Switches to OAuth

  1. MCPHub calls an MCP server that requires authorization and receives 401 Unauthorized.
  2. The response exposes a WWW-Authenticate header pointing to protected resource metadata (authorization_server or as_uri).
  3. MCPHub discovers the authorization server metadata, registers (if needed), and opens the browser so the user can authorize once.
  4. After the callback is handled, MCPHub reconnects with fresh tokens and resumes requests transparently.
MCPHub logs each stage (discovery, registration, authorization URL, token exchange) in the server detail view and the backend logs.

Quick Start by Server Type

Servers with Dynamic Registration Support

Some servers expose complete OAuth metadata and allow dynamic client registration. For example, Vercel and Linear MCP servers only need their SSE endpoint configured:
  • MCPHub discovers the authorization server, registers the client, and handles PKCE automatically.
  • Tokens are stored in mcp_settings.json; no additional dashboard configuration is needed.

Servers Requiring Manual Client Provisioning

Other providers do not support dynamic registration. GitHub’s MCP endpoint (https://api.githubcopilot.com/mcp/) is one example. To connect:
  1. Create an OAuth App in the provider’s console (for GitHub, go to Settings → Developer settings → OAuth Apps).
  2. Set the callback/redirect URL to http://localhost:3000/oauth/callback (or your deployed dashboard domain). Ensure Settings → Installation → Base URL matches this domain so MCP requests and OAuth callbacks resolve correctly.
  3. Copy the issued client ID and client secret.
  4. Supply the credentials through the MCPHub dashboard or by editing mcp_settings.json as shown below.
  • MCPHub skips dynamic registration and uses the credentials you provide to complete the OAuth exchange.
  • Update the dashboard or configuration file whenever you rotate secrets.
  • Replace scopes with the exact scope strings required by the provider.

HTTP Token Endpoints in Private Networks

Since v1.0.42, the MCP SDK rejects non-HTTPS OAuth token endpoints except loopback hosts (localhost, 127.0.0.1, and ::1). This affects both authorization-code exchange and token refresh; reconnecting or re-authorizing does not bypass the check. HTTPS is recommended. Administrators can explicitly allow HTTP token endpoints for one upstream in its dashboard OAuth Configuration section, or in the operator-managed configuration file:
The option defaults to false. Credentials, authorization codes, and tokens can be exposed in transit when enabled; use it only on a trusted network. Each HTTP token request using the exception logs a warning. Non-admin users cannot enable the exception through creation, editing, batch import, or template import, and cannot edit a server while its exception is enabled. Disabling it also blocks pending callbacks and refreshes on existing providers. The exception does not disable SSRF protection, authorize other upstreams, or change MCPHub’s own OAuth endpoints for downstream clients. The SDK client is pinned to 2.1.0 with a pnpm patch adding a per-provider exception hook. The patched client is bundled into the backend build so npm/npx installs also receive it. When upgrading it, port or replace the patch and run tests/integration/oauth-insecure-token-endpoint.test.ts.

Configuration Options

You can rely on auto-detection for most servers or declare OAuth settings explicitly in mcp_settings.json. Only populate the fields you need.

Basic Auto Detection (Minimal Config)

  • MCPHub will discover the authorization server from challenge headers and walk the user through authorization automatically.
  • Tokens (including refresh tokens) are stored on disk and reused on restart.

Static Client Credentials (Bring Your Own Client)

  • Use this when the authorization server requires manual client provisioning.
  • redirectUri overrides the callback derived from Settings → Installation → Base URL for that server.
  • If redirectUri is omitted, MCPHub falls back to ${Base URL}/oauth/callback, then to http://localhost:3000/oauth/callback.
  • Keep Settings → Installation → Base URL aligned with your deployed domain so the default callback URL is correct.

Dynamic Client Registration (RFC 7591)

  • MCPHub discovers endpoints via issuer, registers itself, and persists the issued client_id/client_secret.
  • If oauth.redirectUri is provided, MCPHub uses it as the preferred callback URI and includes it in the dynamically registered redirect_uris.
  • Provide initialAccessToken only when the registration endpoint is protected.

Authorization Flow

  1. Initialization – On startup MCPHub processes every server entry, discovers metadata, and registers the client if dynamicRegistration.enabled is true.
  2. User Authorization – Initiating a connection launches the system browser to the server’s authorize page with PKCE parameters.
  3. Callback Handling – The built-in route (/oauth/callback) verifies the state, completes the token exchange, and saves the tokens via the MCP SDK.
  4. Token Lifecycle – MCPHub persists expires_in as an absolute accessTokenExpiresAt timestamp. Before returning an expired token to the transport, it renews through the SDK and saves the rotated refresh token. Shared tokens use server settings; personal tokens remain in the user’s encrypted binding. Expiry survives runtime recreation, and concurrent personal renewals for the same binding revision within one MCPHub process share one refresh.
Tokens saved before expiry tracking, or issued without expires_in, retain HTTP 401-based SDK recovery; MCPHub cannot infer an opaque token’s expiry. Reauthorize an existing personal connection to establish expiry tracking. Early revocation can still appear as an error inside a successful MCP tool response rather than an HTTP 401. MCPHub returns that error without automatically replaying the tool, because a retry could duplicate side effects. Reconnect through the dashboard for supported recovery; there is no separate personal refresh endpoint.

Tips & Troubleshooting

  • Confirm that the redirect URI used during authorization exactly matches one of the redirect_uris registered with the authorization server.
  • When running behind HTTPS, expose the callback URL publicly or configure a reverse proxy at /oauth/callback.
  • If discovery fails, supply authorizationEndpoint and tokenEndpoint explicitly to bypass metadata lookup.
  • Remove stale tokens from mcp_settings.json if an authorization server revokes access—MCPHub will prompt for a fresh login on the next request.

Explicit authorization server metadata

For a private HTTP MCP resource whose authorization server must be reached at a different address, set oauth.authorizationServerMetadata in the server JSON configuration. This is a complete, explicit replacement for authorization-server discovery in the SDK OAuth flow, for both shared and personal identities. It takes precedence over discovered authorization-server metadata and the legacy authorizationEndpoint / tokenEndpoint fields; omitting it retains the existing discovery behavior.
Use the provider’s actual issuer and endpoints, register MCPHub’s callback URI with that provider, and choose scopes and client authentication matching the application registration. A public client omits clientSecret and uses none; a confidential client can use client_secret_post or client_secret_basic as supported by its token endpoint. For SDK dynamic registration, provide registration_endpoint instead of a pre-registered client. The override is available through JSON configuration/API and is retained by dashboard edits and template export/import; it has no dedicated form editor. MCP protected-resource discovery and the SDK’s resource indicator validation still run against the MCP URL. Resource indicators are not rewritten or suppressed, so a provider must accept the resulting resource parameter and issue tokens the upstream MCP server accepts. This setting does not make an internal broker browser-accessible or guarantee that bypassing its token transformations is compatible. For Microsoft deployments, verify the tenant issuer, Graph scopes, resource parameter and token audience for the chosen upstream mode before using direct Microsoft endpoints. The selected metadata is captured with pending PKCE/state for callback recovery; authorization, code exchange and refresh use the same configured authority. Callback iss validation remains enabled, including required iss when metadata declares authorization_response_iss_parameter_supported: true. HTTP token endpoints still require the separate administrator-controlled allowInsecureTokenEndpoint exception. Personal token encryption, refresh and user isolation are unchanged; changing the metadata invalidates personal bindings and requires reconnecting. This per-server setting is independent of MCPHub’s downstream systemConfig.oauthServer and the legacy global systemConfig.oauth proxy. Neither global setting is required for this flow.

Private broker authorization relay

Administrators can also enable this in the server form: select a personal OAuth identity, expand OAuth Configuration → Advanced settings, enable Relay authorization through MCPHub, and enter the allowed HTTPS origins and broker metadata JSON. Disabling the switch removes the relay configuration; changing to shared identity also disables it. For a private OAuth broker whose /authorize endpoint immediately redirects to an external identity provider, administrators can enable oauth.authorizationRelay on a personal-OAuth HTTP connector. Supply explicit broker metadata and an exact list of allowed HTTPS redirect origins:
Use the broker’s actual issuer, endpoints, supported scopes and client-authentication metadata. Configure its callback allow-list for MCPHub’s public callback. The HTTP token exception is needed only for an HTTP broker; credentials then travel over that private connection without TLS. Only administrators can create or edit a relay connector. Operator-owned file declarations without an owner are also trusted; a declared owner must remain an administrator. The dashboard returns a connector-specific MCPHub /oauth/relay/<name>?state=… URL instead of the private authorization URL. It uses the public callback’s base path, including any installation prefix. MCPHub forwards the saved authorization request once per relay request, without browser cookies or authorization headers, and returns only an allowed external redirect. Token exchange and refresh continue through the broker; its issuer, resource, state and PKCE handling remain unchanged. Relay access requires an unexpired personal flow bound to the current connector declaration and user permissions. Disconnecting or completing a flow invalidates the relay link. Treat the link as a temporary credential and redact its query from reverse-proxy access logs. This is an opt-in authorization-entry relay, separate from the legacy global OAuth proxy and direct Microsoft Graph-token integration. It does not proxy HTML, cookies, relative internal redirects, broker callback pages or arbitrary URLs. A broker that requires the browser to revisit its private hostname needs a different deployment. The authorization endpoint must not contain a query; upstream responses have a 10-second timeout and bounded headers, their bodies are discarded, and only 302/303/307/308 redirects to listed HTTPS origins are accepted. Unconfigured connectors retain their existing behavior. Mocked tests cover independent users, callback recovery, code exchange, refresh and authenticated MCP requests. Live Microsoft compatibility remains unverified; test your application registration, broker mode, scopes and refresh lifecycle before production use. MCPHub’s own OAuth authorization server displays a consent screen on every authorization by default. Operators can enable Remember user consent and edit Consent lifetime (seconds) under Settings → OAuth Server. The same settings are available in systemConfig.oauthServer (also accepted as an oauthServer object by PUT /api/system-config):
approvalLifetime is a fixed lifetime in seconds from explicit approval (default 14 days; accepted range 1–31536000). Reauthorization and refresh-token rotation do not extend it. Expiry requires confirmation on the next authorization request; existing access and refresh tokens retain their normal lifetimes. Setting alwaysShowApprovalScreen to true restores every-time confirmation without revoking existing access. Consent is specific to the current user credential version, client ID and metadata, exact redirect URI, and exact resource value (including its absence). Only requests whose effective scopes are a subset of previously approved scopes can skip the screen. Omitted scopes use the configured allowedScopes; changes that add permissions require confirmation. Public clients still need S256 PKCE, and every successful authorization issues a fresh authorization code. Clients that register a new client ID on reconnect must be approved again. The dashboard-authenticated management APIs expose only the current user’s approvals:
  • GET /api/oauth/approvals returns { "success": true, "data": [...] }; each entry includes id, clientId, scopes, resource, redirectUri, approvedAt, and expiresAt.
  • DELETE /api/oauth/approvals/:approvalId revokes all approvals and existing tokens for that entry’s application and current user, including pending authorization codes. Another user’s entry returns 404. MCP OAuth access tokens cannot authenticate these management APIs.
Open the user menu at the bottom of the sidebar and choose Authorized applications to inspect your permissions, resource, callback URL, and consent dates. Choose Revoke application and confirm to remove all of that application’s authorizations, pending codes, and tokens for your account. This page is available to every signed-in user; global OAuth settings retain their existing settings permission checks. Explicit consent is recorded even when every-time confirmation is enabled, so these grants can still be revoked. Skipping confirmation remains opt-in. In file mode, approvals live in <settings-path>.approvals.json with owner-only permissions; in database mode they live in oauth_client_approvals. Keep the sidecar with runtime backups. Approvals are excluded from configuration exports and declarative reconciliation. Switching from file to database storage requires fresh consent; approval-linked tokens without their grant fail closed. Expired records remain available for revoking still-live tokens and are removed when the user or client is deleted, or the application is revoked. Database replicas read grants directly so revocation applies on their next token use, including to tokens persisted concurrently with revocation. Run pnpm db:setup before upgrading replicas with DB_SYNCHRONIZE=false.