Token Exchange
The token exchange flow (RFC 8693) swaps the credential an AI agent presented to AI Gateway for a different, upstream-scoped token, before Zilla forwards the request to an upstream MCP server or HTTP API. This is the outbound side of the connection, complementing JWT Bearer, which authenticates the agent's inbound session on the mcp server binding. Token exchange lets one authenticated agent identity map to whatever credential each upstream actually expects, without the agent ever requesting or holding those downstream credentials itself.
Configuring the Guard
Define an oauth guard with grant: token-exchange, pointing via at the guard that already authenticated the inbound session, then reference the oauth guard from the upstream-facing mcp client binding's options.authorization:
stores:
cache:
type: memory
guards:
agent_jwt:
type: jwt
options:
issuer: https://auth.example.com
audience: https://mcp.example.com
github_oauth:
type: oauth
options:
grant: token-exchange
endpoint: https://github.com/login/oauth/access_token
audience: https://api.github.com
scope: repo read:user
via: "${guarded['agent_jwt'].credentials}"
callback: /mcp/auth/github/callback
store: cache
bindings:
north_mcp_server:
type: mcp
kind: server
options:
authorization:
agent_jwt:
credentials: "Bearer {credentials}"
exit: north_mcp_proxy
north_mcp_proxy:
type: mcp
kind: proxy
routes:
- exit: github_mcp_client
when:
- toolkit: github
github_mcp_client:
type: mcp
kind: client
options:
server: https://api.githubcopilot.com/mcp
authorization:
github_oauth:
credentials: "Bearer {credentials}"
exit: sys:http_clientvia: "${guarded['agent_jwt'].credentials}" resolves the subject token from the agent_jwt guard that already authenticated north_mcp_server, exactly the pattern in JWT Bearer. The github_oauth guard exchanges that token for one scoped to audience and scope, and github_mcp_client attaches the exchanged token to its outbound request.
The first time a given caller needs a token for a new audience/scope pair, there's nothing to exchange yet: the guard triggers an interactive OAuth consent flow instead of failing outright. The identity provider redirects to the callback path once the user grants consent, the guard exchanges the resulting authorization code for a token, and caches it. A retried call then succeeds without exchanging again, until the cached token expires.
See the oauth guard reference for the full set of options fields, including the elicitation flow, and the mcp client binding reference for options.authorization.
Zilla Plus
The oauth guard, all three of its grants, requires Zilla Plus. It's a separate guard type from the open-source jwt guard used by JWT Bearer.
See Secure Agent Access for how this maps one agent credential to many per-upstream downstream tokens, and JWT Bearer and Client Credentials for the other two flows.

