From c2f1fbc5ed9a144b96c0e7cae4ab84d616f3e04b Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Wed, 13 May 2026 16:39:14 +0530 Subject: [PATCH] docs(mcp): add delegate_auth_to_upstream / PKCE passthrough section Documents the new `delegate_auth_to_upstream` flag for oauth2 MCP servers. Covers setup, how the PKCE passthrough flow works (with sequence diagram), fail-closed behavior, security trade-offs, and the config reference table. Co-authored-by: Cursor --- docs/mcp_oauth.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/docs/mcp_oauth.md b/docs/mcp_oauth.md index 5919bdb60..a60774bc5 100644 --- a/docs/mcp_oauth.md +++ b/docs/mcp_oauth.md @@ -336,3 +336,74 @@ Check that: **Symptom:** `x-mcp-debug-auth-resolution` shows `m2m-client-credentials`. The server has `client_id`/`client_secret`/`token_url` configured so LiteLLM is fetching a machine-to-machine token instead of using the per-user OAuth2 token. To use per-user tokens, remove the client credentials from the server config. + + +## Delegate Auth to Upstream (PKCE Passthrough) + +For OAuth2 MCP servers where the client (Claude Code, Cursor, ChatGPT, etc.) already authenticates directly against the upstream server's own OAuth issuer, you can opt the route into **upstream-delegated auth**: LiteLLM stops checking its own API key / SSO and lets the client's PKCE flow run end-to-end with the upstream MCP server. + +Use this when the upstream server is the source of truth for who can access it and you don't want LiteLLM to gate the route a second time. + +### Setup + +```yaml title="config.yaml" showLineNumbers +mcp_servers: + notion_mcp: + url: "https://mcp.notion.com/mcp" + auth_type: oauth2 + delegate_auth_to_upstream: true +``` + +That's the entire change. The flag is honored **only** when `auth_type: oauth2`; setting it on any other auth type is silently ignored. + +### How It Works + +1. Client sends an MCP request to LiteLLM with no `x-litellm-api-key` (and optionally no `Authorization` header). +2. LiteLLM detects that every target server in the request is `auth_type: oauth2` AND has `delegate_auth_to_upstream: true`, and skips its own API-key/SSO check. +3. LiteLLM also skips its pre-emptive 401, so the upstream MCP server's own `401` + `WWW-Authenticate` flows back to the client. +4. The client completes PKCE directly with the upstream OAuth issuer. +5. The client retries with `Authorization: Bearer `. LiteLLM forwards it untouched. + +```mermaid +sequenceDiagram + participant Client + participant LiteLLM as LiteLLM Proxy + participant MCP as Upstream MCP Server + participant Auth as Upstream OAuth Server + + Client->>LiteLLM: MCP request (no LiteLLM key) + LiteLLM->>MCP: Forward request (no Authorization) + MCP-->>LiteLLM: 401 + WWW-Authenticate + LiteLLM-->>Client: 401 + WWW-Authenticate (passthrough) + + Note over Client,Auth: Client runs PKCE directly with upstream + Client->>Auth: Authorize + token exchange (PKCE) + Auth-->>Client: access_token + + Client->>LiteLLM: MCP request + Bearer access_token + LiteLLM->>MCP: Forward request + Bearer access_token + MCP-->>LiteLLM: MCP response + LiteLLM-->>Client: MCP response +``` + +### Fail-Closed Behavior + +The bypass only fires when **every** target the request resolves to opts in. It fails closed and runs normal LiteLLM auth in any of these cases: + +- The server's `auth_type` is anything other than `oauth2`. +- `delegate_auth_to_upstream` is not explicitly `true`. +- The request targets multiple servers (`x-mcp-servers: a,b`) and any one of them is not delegated. +- The target server cannot be resolved from the URL path or `x-mcp-servers` header. + +### Security Trade-offs + +- This flag turns the MCP route into an **unauthenticated** ingress at the LiteLLM layer. Spend tracking, per-key rate limits, and any guardrails that depend on `user_api_key_auth.user_id` will not run for these requests. +- LiteLLM cannot tell who the caller is — that's the entire point — so per-user auditing must come from the upstream MCP server's own logs. +- Only enable this on servers whose upstream OAuth issuer you trust to enforce access control. + +### Config Reference + +| Field | Required | Description | +|-------|----------|-------------| +| `auth_type` | Yes | Must be `oauth2`. The flag is ignored otherwise. | +| `delegate_auth_to_upstream` | Yes | Set to `true` to opt this server into PKCE passthrough. | \ No newline at end of file