ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Introduction
Getting Started
    Develop on the web portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
    Develop locally with the CLI
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
Concepts
Development
Policies
Handlers
API Keys
Rate Limiting
MCP Server
MCP Gateway
    IntroductionBetaQuickstartQuickstart (Local Dev)How it works
    Connect MCP clients
    Authentication
    Cross App Access
      OverviewQuickstartConfiguration reference
    Configuration
    Observability
    ReferenceTroubleshooting
AI Gateway
Developer Portal
Monetization
GraphQL
Deploying & Source Control
Analytics
Observability
Networking & Infrastructure
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsSecuritySupportTrust & ComplianceChangelog
powered by Zudoku
Cross App Access

Cross App Access configuration reference

Cross App Access is configured on the mcp-token-exchange-inbound policy. Setting authMode: "id-jag" and providing an idJag block makes the gateway act as the XAA requesting app: it mints an ID-JAG from your IdP and redeems it at an upstream resource authorization server. This is the configuration the quickstart uses.

The authoritative source for these options is the policy's runtime schema. The generated mcp-token-exchange-inbound reference page predates id-jag mode; the options below reflect the runtime behavior.

Gateway as requesting app

Set authMode: "id-jag" on a mcp-token-exchange-inbound policy and provide an idJag block. Attach the policy to the upstream route after the inbound MCP OAuth policy.

config/policies.json
{ "name": "id-jag-upstream", "policyType": "mcp-token-exchange-inbound", "handler": { "module": "$import(@zuplo/runtime/mcp-gateway)", "export": "McpTokenExchangeInboundPolicy", "options": { "displayName": "Upstream", "authMode": "id-jag", "idJag": { "scopes": ["mcp:tools"], "scopeDelimiter": " ", "idp": { "tokenUrl": "https://idp.example.com/token", "clientAuth": { "method": "client_secret_post", "clientId": "$env(IDP_CLIENT_ID)", "clientSecret": "$env(IDP_CLIENT_SECRET)", }, }, "resourceAs": { "tokenUrl": "https://upstream.example.com/token", "audience": "https://upstream.example.com", "resource": "https://upstream.example.com/mcp", "clientAuth": { "method": "client_secret_post", "clientId": "$env(RESOURCE_AS_CLIENT_ID)", "clientSecret": "$env(RESOURCE_AS_CLIENT_SECRET)", }, }, }, }, }, }

idJag options

OptionTypeDefaultDescription
scopesstring[][]Scopes requested in both exchanges.
scopeDelimiterstring" "Delimiter used to join scopes.
idpobject—Where the gateway mints the ID-JAG (RFC 8693 token exchange).
resourceAsobject—Where the gateway redeems the ID-JAG for an access token (RFC 7523).

idp

The identity provider that issues the ID-JAG.

OptionTypeDescription
tokenUrlstringThe IdP token endpoint.
clientAuthobjectHow the gateway authenticates to the IdP (see below).

resourceAs

The upstream's resource authorization server that issues the access token.

OptionTypeDescription
tokenUrlstringThe resource authorization server's token endpoint.
audiencestringRequired. The resource AS identifier; sent as the token-exchange audience and becomes the ID-JAG aud.
resourcestringOptional RFC 8707 resource indicator. Defaults to the route's upstream URL.
clientAuthobjectHow the gateway authenticates to the resource AS (see below).

clientAuth

Both idp.clientAuth and resourceAs.clientAuth take the same shape. The method selects how the gateway authenticates:

Code
// client_secret_post (or client_secret_basic) "clientAuth": { "method": "client_secret_post", "clientId": "...", "clientSecret": "$env(...)" }
Code
// private_key_jwt "clientAuth": { "method": "private_key_jwt", "clientId": "...", "privateKeyPem": "$env(...)", "algorithm": "RS256", "keyId": "...", "expiresInSeconds": 300 }
OptionTypeDefaultApplies toDescription
methodenum—allclient_secret_post, client_secret_basic, or private_key_jwt.
clientIdstring—allThe OAuth client ID.
clientSecretstring—secret methodsThe OAuth client secret.
privateKeyPemstring—private_key_jwtPEM private key used to sign the client-assertion JWT.
algorithmenumRS256private_key_jwtRS256/RS384/RS512/ES256/ES384/ES512.
keyIdstring—private_key_jwtOptional kid header on the client assertion.
audiencestring—private_key_jwtOptional audience override for the client assertion.
expiresInSecondsnumber300private_key_jwtClient-assertion lifetime, max 3600.

What the gateway does at request time

On a tool call to the route, the gateway:

  1. Resolves the user's stored IdP identity assertion (bound during the inbound browser login). If absent or expired, it returns a connect-required error and refreshes the subject token when it can.
  2. Runs an RFC 8693 token exchange at idp.tokenUrl, requesting an ID-JAG audience-restricted to resourceAs.audience.
  3. Redeems the ID-JAG at resourceAs.tokenUrl as an RFC 7523 JWT-bearer grant to get the upstream access token.
  4. Caches the upstream token per user and forwards the tool call with Authorization: Bearer <upstream token>.

Notes and limitations

  • The gateway issues opaque access tokens, not JWTs.
  • DPoP is not supported. Requests with a DPoP header are rejected.
  • XAA requires a prior inbound browser login so the gateway has an IdP identity assertion to exchange.

Related

  • Overview — the protocol and the gateway's role.
  • Quickstart — a working id-jag configuration on the playground.
  • mcp-token-exchange-inbound policy — the base token-exchange policy reference.
Edit this page
Last modified on June 22, 2026
QuickstartSet up the gateway
On this page
  • Gateway as requesting app
    • idJag options
    • idp
    • resourceAs
    • clientAuth
    • What the gateway does at request time
  • Notes and limitations
  • Related
JSON
JSON
JSON