|
| 1 | +# MCP Agent OAuth Support |
| 2 | + |
| 3 | +## Goals |
| 4 | +- Protect MCP Agent Cloud servers using OAuth 2.1 so MCP clients obtain tokens via standard flows. |
| 5 | +- Enable MCP Agent runtimes to authenticate to downstream MCP servers that require OAuth access tokens. |
| 6 | +- Provide pluggable token storage for both local development (in-memory) and multi-instance deployments (Redis planned). |
| 7 | +- Maintain compatibility with MCP Authorization spec (RFC 8414, RFC 9728, OAuth 2.1 + PKCE, Resource Indicators) and the proposed delegated authorization SEP. |
| 8 | + |
| 9 | +## Architecture Overview |
| 10 | + |
| 11 | +### Components |
| 12 | +1. **Auth Server Integration** – Configure the FastMCP instance with `AuthSettings` and a custom `TokenVerifier` that calls MCP Agent Cloud auth services. |
| 13 | +2. **Protected Resource Metadata** – Serve `/.well-known/oauth-protected-resource` using FastMCP hooks so clients can discover the auth server. |
| 14 | +3. **Access Token Validation** – Enforce bearer tokens on every inbound MCP request via `RequireAuthMiddleware`, populating the request context with the authenticated user. |
| 15 | +4. **OAuth Token Service** – New `mcp_agent.oauth` package with: |
| 16 | + - `TokenStore`/`TokenRecord` abstractions |
| 17 | + - `InMemoryTokenStore` and Redis-backed implementation (optional for multi-instance) |
| 18 | + - `TokenManager` orchestration (acquire, refresh, revoke) |
| 19 | + - `OAuthHttpxAuth` for attaching tokens to downstream HTTP transports |
| 20 | + - `AuthorizationFlowCoordinator` that interacts with the user via MCP `auth/request`. |
| 21 | + When no upstream client session is available, a client-only loopback flow starts a |
| 22 | + temporary local callback listener on 127.0.0.1 using a configurable fixed port list |
| 23 | + (default: 33418, 33419, 33420), opens the browser, and completes the PKCE code flow. |
| 24 | +5. **Delegated Authorization UI Flow** – Extend the gateway/session relay so servers can send `auth/request` messages to MCP clients, capturing authorization codes via either: |
| 25 | + - Client-returned callback URL (preferred, works with SEP-capable clients) |
| 26 | + - MCP Agent hosted callback endpoint (`/internal/oauth/callback/{flow_id}`) as a fallback / native-app style loopback. |
| 27 | +6. **Configuration Surface** – Extend `Settings` and per-server `MCPServerAuthSettings` to describe OAuth behaviour (scopes, preferred auth server, redirect URIs, etc.) and global token-store configuration. |
| 28 | + |
| 29 | +### Key Data Flow |
| 30 | +1. **Inbound Requests** |
| 31 | + - Client presents bearer token ⇒ `BearerAuthBackend` + `MCPAgentTokenVerifier` introspect token. |
| 32 | + - Verified token populates context with `OAuthUserIdentity` (provider + subject + email). |
| 33 | + - Context is propagated into workflows/sessions so downstream OAuth flows know the acting user. |
| 34 | + |
| 35 | +2. **Outbound HTTP (downstream MCP server)** |
| 36 | + - `ServerRegistry` detects `auth.oauth` configuration. |
| 37 | + - Wraps HTTP transport with `OAuthHttpxAuth` which requests an access token from `TokenManager`. |
| 38 | + - `TokenManager` checks store; if missing/expired ⇒ `AuthorizationFlowCoordinator` performs RFC 9728 discovery, PKCE, delegated browser flow through MCP client, exchanges code for tokens, caches result. |
| 39 | + - Requests automatically retry after token refresh when a response returns 401/invalid token. |
| 40 | + |
| 41 | +3. **Token Storage** |
| 42 | + - Tokens stored per `(user_identity, resource, authorization_server)` tuple with metadata (scopes, expiry, refresh token, provider claims). |
| 43 | + - Store implements optimistic locking to avoid concurrent refresh storms. |
| 44 | + - Pluggable backend (`InMemoryTokenStore` initial, Redis follow-up). |
| 45 | + |
| 46 | +## Module Plan |
| 47 | + |
| 48 | +``` |
| 49 | +src/mcp_agent/oauth/ |
| 50 | + __init__.py |
| 51 | + identity.py # OAuthUserIdentity, helpers to extract from auth context |
| 52 | + records.py # TokenRecord dataclass/pydantic model |
| 53 | + store/base.py # TokenStore protocol |
| 54 | + store/in_memory.py # Default store |
| 55 | + manager.py # TokenManager (get/refresh/invalidate) |
| 56 | + flow.py # AuthorizationFlowCoordinator |
| 57 | + http/auth.py # OAuthHttpxAuth (httpx.Auth implementation) |
| 58 | + metadata.py # RFC 8414 + RFC 9728 discovery helpers |
| 59 | + pkce.py # PKCE + state utilities |
| 60 | + errors.py # Custom exception hierarchy |
| 61 | +``` |
| 62 | + |
| 63 | +Integration touchpoints: |
| 64 | +- `mcp_agent/config.py` – add OAuth settings models. |
| 65 | +- `mcp_agent/core/context.py` – add `token_manager`, `token_store`, `oauth_config` fields. |
| 66 | +- `mcp_agent/app.py` – initialize token store/manager based on settings. |
| 67 | +- `mcp_agent/server/app_server.py` – configure FastMCP auth settings, register callback route, surface user identity, extend relay to handle `auth/request`. |
| 68 | +- `mcp_agent/mcp/mcp_server_registry.py` & `mcp_agent/mcp/mcp_connection_manager.py` – wire `OAuthHttpxAuth` into HTTP transports and expose helper for manual token teardown. |
| 69 | +- `mcp_agent/mcp/client_proxy.py` – add proxy helpers for `auth/request`. |
| 70 | +- `SessionProxy` – add direct request helper for `auth/request` and ensure Temporal flow support. |
| 71 | +- `examples/mcp_agent_server/*` – demonstrate configuration changes. |
| 72 | +- Tests – new suite exercising token store, metadata discovery, flow orchestration (with mocked HTTP + client responses). |
| 73 | + |
| 74 | +## OAuth Flow Details |
| 75 | +1. **Discovery** |
| 76 | + - If downstream server responds 401 with `WWW-Authenticate`, parse for `resource_metadata` ⇒ GET metadata ⇒ determine auth server URL(s). |
| 77 | + - Fetch authorization server metadata (RFC 8414). |
| 78 | + - Perform optional dynamic client registration when configured and supported. |
| 79 | + |
| 80 | +2. **Authorization Request** |
| 81 | + - Generate PKCE challenge/verifier, secure `state`, choose `redirect_uri`. |
| 82 | + - Build authorization URL including `resource` parameter (RFC 8707) + requested scopes. |
| 83 | + - Invoke `auth/request` via SessionProxy → MCP client opens browser. |
| 84 | + |
| 85 | +3. **Callback Handling** |
| 86 | + - Preferred: MCP client returns callback URL payload via request result. |
| 87 | + - Fallback: Authorization server redirects to `/internal/oauth/callback/{flow_id}`. |
| 88 | + - Coordinator validates `state`, extracts `code` (and errors). |
| 89 | + |
| 90 | +4. **Token Exchange / Storage** |
| 91 | + - POST token endpoint with code + PKCE verifier + resource. |
| 92 | + - Store access token, refresh token, expiry, scope, provider metadata. |
| 93 | + - Associate tokens with user identity for reuse. |
| 94 | + |
| 95 | +5. **Refresh / Revocation** |
| 96 | + - Manager refreshes when expiry within configurable grace window. |
| 97 | + - Invalidate token on refresh failure or when server responses indicate revocation. |
| 98 | + - Provide method to revoke tokens via authorization server when supported. |
| 99 | + |
| 100 | +## Open Questions / Follow-ups |
| 101 | +- Additional operational hardening (token rotation policies, rate limits). |
| 102 | +- How LastMile auth server exposes token introspection + JWKS; need concrete endpoint specs to finalize `MCPAgentTokenVerifier`. |
| 103 | +- MCP client adoption of `auth/request` SEP – need capability detection; until widely supported we rely on hosted callback fallback & manual instructions. |
| 104 | +- Access control DSL (include/exclude by email/domain) – to be evaluated once token identity payload finalized. |
| 105 | + |
| 106 | +## Testing Strategy |
| 107 | +- Unit tests for token store concurrency + expiry handling. |
| 108 | +- Metadata discovery + PKCE generation (pure python tests). |
| 109 | +- Integration-style test for delegated flow using mocked HTTP server + fake MCP client (ensures `auth/request` plumbing works end-to-end). |
| 110 | +- Tests around server 401 enforcement + WWW-Authenticate header. |
0 commit comments