ZuploZuplo
LoginSign Up
  • Documentation
  • API Reference
Introduction
Getting Started
    Develop using the Portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingMCP - Quick start
    Develop Locally
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth
Concepts
Development
Policies
Handlers
API Keys
MCP Server
MCP Gateway
    IntroductionBetaQuickstartQuickstart (Local Dev)How it works
    Connect MCP clients
    Authentication
      OverviewUpstream OAuthConnect an upstream OAuth provider
      Identity providers
      Manual OAuth testing
    Configuration
    Observability
    ReferenceTroubleshooting
AI Gateway
Developer Portal
Monetization
Deploying & Source Control
Observability
Networking & Infrastructure
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsSecuritySupportTrust & ComplianceChangelog
powered by Zudoku
Authentication

Authentication overview

The Zuplo MCP Gateway sits in the middle of two independent OAuth relationships. MCP clients connect to the gateway and authenticate against it. The gateway, in turn, connects to each upstream MCP server and authenticates against it on the user's behalf. Both halves follow the same spec — the MCP authorization model at revision 2025-11-25 — but they're configured separately and use different policies.

This page explains both layers, the standards involved, and the moving parts (sessions, scopes, token lifetimes) you'll see throughout the gateway. The identity provider catalog below points at per-IdP setup guides; the upstream side has its own pages:

  • Per-user OAuth to upstream MCP servers — the upstream side, conceptually
  • Connect a gateway to an upstream OAuth provider — how-to
  • Manual OAuth testing — how-to

The two layers

Every authenticated MCP request involves two distinct OAuth surfaces.

Downstream: gateway as OAuth server

When a client like Claude Desktop, Cursor, or Claude Code connects to a /mcp/{slug} route on the gateway, the client is the OAuth client and the gateway is both the OAuth 2.1 Resource Server (RS) and the OAuth 2.1 Authorization Server (AS).

The gateway publishes everything an MCP client needs to discover and complete an OAuth flow:

  • An RFC 9728 Protected Resource Metadata document per route.
  • An RFC 8414 Authorization Server Metadata document, both gateway-wide and per route.
  • An RFC 7591 Dynamic Client Registration endpoint.
  • An OAuth Client ID Metadata Document (CIMD) acceptor.
  • /oauth/authorize, /oauth/token, /oauth/revoke, and /oauth/callback endpoints.

Browser identity is delegated to an OIDC identity provider you configure — Auth0, Okta, or any OIDC discovery-compatible IdP. The IdP authenticates the user; the gateway then issues its own bearer access token to the MCP client. The IdP's token never reaches the MCP client.

Upstream: gateway as OAuth client

When the gateway forwards a request to an upstream MCP server (Linear, Stripe, Notion, GitHub, your internal service, and so on), the gateway is the OAuth client and the upstream MCP server is the resource server. On behalf of each user, the gateway runs through the upstream provider's OAuth discovery, registers itself (preferring OIDC Client ID Metadata Documents, falling back to RFC 7591 Dynamic Client Registration), redirects the user through the upstream /authorize, captures the upstream tokens, and stores them encrypted at rest.

On subsequent MCP requests, the gateway resolves the stored upstream credential per user, refreshes it if necessary, and injects it as an Authorization: Bearer ... header when proxying to the upstream.

Token passthrough is forbidden

The MCP authorization spec explicitly forbids forwarding an inbound bearer token to an upstream API. Inbound auth headers don't leak to the upstream — the gateway always uses an independent upstream credential. The gateway-issued token a client presents and the upstream token the gateway forwards are never the same token.

Standards observed

The gateway implements the following standards in their MCP-mandated subsets.

StandardPurpose
OAuth 2.1 (draft)Core authorization framework.
RFC 7636 — PKCERequired on every authorization code flow. S256 is required when technically capable.
RFC 8414 — Authorization Server MetadataPublished at /.well-known/oauth-authorization-server[/{routePath}].
OpenID Connect Discovery 1.0Accepted alongside RFC 8414 as authorization-server discovery (added in the 2025-11-25 MCP revision).
RFC 9728 — Protected Resource MetadataPublished at /.well-known/oauth-protected-resource/{routePath} per MCP route.
RFC 7591 — Dynamic Client RegistrationAccepted at /oauth/register.
OAuth Client ID Metadata Documents (CIMD)Recommended client identification path per the 2025-11-25 MCP revision. The gateway advertises client_id_metadata_document_supported: true and accepts URLs as client_id values when CIMD is enabled.
RFC 8707 — Resource IndicatorsMCP clients MUST include the resource parameter on every authorization and token request. The gateway validates that incoming bearer tokens were minted for the route's canonical resource URI.
RFC 6750 — Bearer tokensAuthorization: Bearer ... only, header position only — tokens in query strings are rejected.
RFC 7009 — Token RevocationPublished at /oauth/revoke.

CIMD is the recommended client identification path going forward; DCR is retained for backwards compatibility with older MCP clients. Both work against the same /oauth/register and AS metadata surface — clients that support either are accommodated.

Downstream flow

The downstream OAuth flow follows the spec's authorization-code grant with PKCE plus the MCP resource parameter binding.

Zuplo Gateway
OAuth endpoints
MCP route
MCP Client
Identity Provider
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

The flow is the standard MCP authorization handshake. The McpGatewayPlugin registers the /.well-known/... and /oauth/... endpoints automatically.

Upstream flow

The first request to a route whose upstream needs OAuth produces a connect-required error. The MCP client is expected to surface the returned URL to the user; the user completes upstream OAuth in a browser; the next MCP request succeeds.

Zuplo Gateway
Upstream connect
MCP route
MCP Client
Upstream Provider
Upstream MCP server
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

The connect-required JSON-RPC error wraps an MCP UrlElicitationRequiredError, so clients that implement the URL-elicitation extension open the URL in a browser automatically. Older clients surface the URL as text for the user to open manually.

When the gateway has a stored upstream connection for the user, no connect-required error is returned — the proxy forwards transparently.

Identity providers

The gateway ships a generic OIDC policy plus first-class wrappers for the identity providers most teams use. You configure exactly one of these policies per project — the gateway rejects projects that declare more than one MCP OAuth policy.

Each wrapper has the same shape: it takes a small set of provider-specific fields (a domain, a tenant ID, a subdomain, and so on) plus clientId and clientSecret, and derives the OIDC issuer, JWKS URL, and browser-login endpoints automatically. Under the hood every wrapper composes the generic mcp-oauth-inbound policy with provider-flavored URLs.

ProviderPolicyRequired optionsSetup guide
Auth0mcp-auth0-oauth-inboundauth0Domain, clientId, clientSecretConfiguring Auth0
Amazon Cognitomcp-cognito-oauth-inboundawsRegion, userPoolId, userPoolDomain, client credsCognito
Clerkmcp-clerk-oauth-inboundfrontendApiUrl, clientId, clientSecretClerk
Googlemcp-google-oauth-inboundclientId, clientSecretGoogle
Keycloakmcp-keycloak-oauth-inboundkeycloakBaseUrl, realm, client credsKeycloak
Logtomcp-logto-oauth-inboundlogtoEndpoint, clientId, clientSecretLogto
Microsoft Entra IDmcp-entra-oauth-inboundtenantId, clientId, clientSecretMicrosoft Entra
Oktamcp-okta-oauth-inboundoktaDomain, client credsOkta
OneLoginmcp-onelogin-oauth-inboundoneLoginSubdomain, client credsOneLogin
PingOnemcp-ping-oauth-inboundenvironmentId (or customDomain), client credsPingOne
WorkOSmcp-workos-oauth-inboundclientId, clientSecretWorkOS
Any other OIDC IdP (Ory Hydra, Authentik, custom…)mcp-oauth-inboundoidc.issuer, oidc.jwksUrl, browserLogin.urlGeneric OIDC

The upstream side uses a separate policy, mcp-token-exchange-inbound, one per upstream MCP route. The downstream OAuth policy and the upstream token-exchange policy are usually paired on the same route.

Which wrapper should I pick?

  • Use a first-class wrapper when one exists for your IdP. The wrappers validate provider-specific inputs at boot (Entra rejects multi-tenant aliases like common, Cognito separates the IDP service domain from the hosted UI domain, OneLogin asks for the bare subdomain) so most misconfigurations fail fast with an obvious error instead of a confusing OIDC failure later.
  • Use mcp-oauth-inbound for any OIDC provider that doesn't ship a dedicated wrapper. Ory Hydra, Authentik, ZITADEL, FusionAuth, custom OIDC servers, and PingFederate (as opposed to PingOne) all work this way. You provide the OIDC URLs explicitly.

Sessions, scopes, and TTLs

The defaults below affect user-visible behavior and come up often in configuration.

Browser session

After the user completes browser login, the gateway sets a zuplo_mcp_session cookie. The cookie persists for 8 hours by default (browserLogin.sessionTtlSeconds). During that window, the user doesn't need to re-authenticate against the IdP for additional OAuth grants — the consent page renders immediately.

Gateway-issued tokens

The gateway-issued bearer access token defaults to 15 minutes of lifetime (gateway.accessTokenTtlSeconds = 900). MCP clients refresh as needed.

Refresh tokens default to roughly 10 years (gateway.refreshTokenTtlSeconds). This is intentional: the gateway is not the system of record for the user's session — the upstream IdP is. Imposing a shorter refresh-token lifetime than the IdP's own session policy forces the user back through browser login when the IdP would still accept a silent renewal. Customers who want a tighter ceiling can override the default in policy options.

Refresh tokens rotate on every use, and presenting a previously rotated refresh token revokes the entire grant (with a short grace window to handle concurrent refreshes).

Gateway scope

There is exactly one downstream OAuth scope today: mcp:tools. The gateway issues every access token with this scope, and the PRM advertises it as the only entry in scopes_supported. Future capability scopes will appear alongside mcp:tools rather than replacing it.

Upstream OAuth scopes are independent — they're whatever the upstream provider requires, configured per upstream on mcp-token-exchange-inbound.

Authentication binding

Every gateway-issued access token is bound to:

  • The canonical resource URI of the MCP route the user authorized for, derived from the request origin and the route path.
  • The operationId of the route, set in routes.oas.json.

The gateway rejects a token presented at a different route or a different canonical resource. A token issued for /mcp/linear-v1 cannot be reused on /mcp/stripe-v1.

The canonical resource URI is constructed from the incoming request origin. If you front the gateway with a custom domain or a proxy, the gateway derives its origin from the Host or X-Forwarded-Host header. A misconfigured proxy that strips or overwrites these headers makes the gateway advertise the wrong issuer in AS metadata. See Troubleshooting for symptoms.

Custom domain caveat

The gateway's issuer URL — the value that appears as issuer in AS metadata and as the authority of all generated endpoint URLs — is derived from the incoming request's origin. The gateway honors the Host and X-Forwarded-Host headers in that order.

When you put the gateway behind a custom domain (gateway.example.com), ensure your fronting proxy or CDN forwards the original Host (or sets X-Forwarded-Host) so the issuer in AS metadata matches the URL the MCP client connected to. A mismatch makes OAuth clients reject the gateway's metadata.

Endpoints reference

The gateway exposes the following authorization endpoints automatically once an MCP OAuth policy is configured. See the reference for the full URL catalog.

PathMethodPurpose
/.well-known/oauth-authorization-serverGETRFC 8414 AS metadata (gateway-wide).
/.well-known/oauth-authorization-server/{routePath}GETRFC 8414 AS metadata (per route, rebinds issuer).
/.well-known/oauth-protected-resource/{routePath}GETRFC 9728 PRM (per route).
/oauth/registerPOSTRFC 7591 Dynamic Client Registration.
/oauth/authorizeGETGateway-wide authorize endpoint. Requires the resource parameter.
/oauth/authorize/{routePath}GETPer-route authorize endpoint.
/oauth/callbackGETBrowser-login callback from the IdP.
/oauth/setupGET, POSTConsent and multi-upstream connect page.
/oauth/tokenPOSTToken endpoint. Accepts authorization_code and refresh_token grants.
/oauth/revokePOSTRFC 7009 revocation.
/.well-known/oauth-client/{connection}GETOIDC Client ID Metadata Document for the upstream OAuth client (per upstream).
/auth/connections/{connection}/connectGETStart the upstream OAuth flow.
/auth/connections/{connection}/callbackGETUpstream OAuth callback.

The well-known metadata endpoints serve CORS-permissive responses (Access-Control-Allow-Origin: *) because browser-resident MCP clients fetch them cross-origin. The token, register, revoke, callback, setup, and connect endpoints reject ambient credentials.

Related

  • Per-user OAuth to upstream MCP servers — the upstream side, conceptually: discovery, client registration modes, per-user storage, refresh, and reconsent.
  • Connect a gateway to an upstream OAuth provider — how to attach the mcp-token-exchange-inbound policy.
  • Manual OAuth testing — verify the gateway's OAuth surface end to end with curl and openssl.
  • Identity provider setup guides — Auth0, Cognito, Clerk, Google, Keycloak, Logto, Microsoft Entra, Okta, OneLogin, PingOne, WorkOS, and any other OIDC provider.
  • Reference — full URL catalog, default TTLs, and OAuth metadata extensions.
Edit this page
Last modified on May 27, 2026
Test clientsUpstream OAuth
On this page
  • The two layers
    • Downstream: gateway as OAuth server
    • Upstream: gateway as OAuth client
  • Standards observed
  • Downstream flow
  • Upstream flow
  • Identity providers
    • Which wrapper should I pick?
  • Sessions, scopes, and TTLs
    • Browser session
    • Gateway-issued tokens
    • Gateway scope
    • Authentication binding
  • Custom domain caveat
  • Endpoints reference
  • Related