|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code when working with the MCP OAuth Gateway codebase. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +The **MCP OAuth Gateway** is a work-in-progress OAuth 2.1 authorization server that provides transparent authentication and authorization for Model Context Protocol (MCP) services. It acts as a secure proxy that handles all OAuth complexity, allowing users to simply access `https://gateway.example.com/<service-id>/mcp` and have authentication handled automatically. |
| 8 | + |
| 9 | +## Codebase Structure |
| 10 | + |
| 11 | +### Directory Layout |
| 12 | +``` |
| 13 | +src/ |
| 14 | +├── gateway.py # Main FastAPI application and CLI entry point |
| 15 | +├── auth/ # OAuth 2.1 authentication system |
| 16 | +│ ├── models.py # Pydantic models for OAuth entities |
| 17 | +│ ├── oauth_server.py # Core OAuth 2.1 server implementation |
| 18 | +│ ├── provider_manager.py # External OAuth provider integration |
| 19 | +│ ├── client_registry.py # Dynamic Client Registration (RFC 7591) |
| 20 | +│ └── token_manager.py # JWT token creation/validation |
| 21 | +├── config/ |
| 22 | +│ └── config.py # YAML configuration management |
| 23 | +├── proxy/ |
| 24 | +│ └── mcp_proxy.py # HTTP proxy with user context injection |
| 25 | +└── api/ |
| 26 | + └── metadata.py # OAuth metadata endpoints |
| 27 | +``` |
| 28 | + |
| 29 | +### Key Dependencies |
| 30 | +- **FastAPI** (≥0.104.1) - Web framework and OpenAPI |
| 31 | +- **Uvicorn** - ASGI server |
| 32 | +- **python-jose** - JWT token handling with cryptography support |
| 33 | +- **httpx** (≥0.25.2) - HTTP client for proxying requests |
| 34 | +- **Pydantic** (≥2.5.0) - Data validation and serialization |
| 35 | +- **PyYAML** - Configuration file parsing |
| 36 | +- **Cryptography** (≥45.0.0) - Security primitives |
| 37 | +- **pytest** (≥7.0.0) - Testing framework with async support |
| 38 | +- **pytest-asyncio** (≥0.23.0) - Async test support |
| 39 | +- **pytest-httpx** (≥0.21.0) - HTTP client mocking for tests |
| 40 | + |
| 41 | +## Core Components |
| 42 | + |
| 43 | +### 1. Main Application (`gateway.py`) |
| 44 | +- FastAPI app with OAuth 2.1 and MCP service endpoints |
| 45 | +- Command-line interface with config file support |
| 46 | +- Health checks and service discovery endpoints |
| 47 | +- CORS middleware and security headers |
| 48 | +- Lifespan management for async resources |
| 49 | + |
| 50 | +**Key functions:** |
| 51 | +- `McpGateway` class - Main gateway orchestrator with all OAuth and MCP functionality |
| 52 | +- `create_app(config: Config) -> FastAPI` - Application factory |
| 53 | +- `main()` - CLI entry point with argument parsing |
| 54 | +- `_determine_provider_for_resource()` - Single provider constraint enforcement |
| 55 | + |
| 56 | +### 2. OAuth Authentication System (`auth/`) |
| 57 | + |
| 58 | +#### Models (`models.py`) |
| 59 | +Complete Pydantic models for OAuth 2.1 entities: |
| 60 | +- `UserInfo` - User profile from OAuth providers |
| 61 | +- `ClientInfo` - OAuth client registration data |
| 62 | +- `AuthorizationCode` - PKCE-enabled authorization codes |
| 63 | +- `AccessToken` - JWT tokens with audience binding |
| 64 | +- Request/response models for all OAuth endpoints |
| 65 | + |
| 66 | +#### OAuth Server (`oauth_server.py`) |
| 67 | +Core OAuth 2.1 authorization server implementation: |
| 68 | +- **Authorization endpoint** with PKCE and resource parameter support |
| 69 | +- **Token endpoint** for authorization code exchange |
| 70 | +- **State management** for OAuth flows with in-memory storage |
| 71 | +- **User session handling** with secure session secrets |
| 72 | + |
| 73 | +**Key methods:** |
| 74 | +- `handle_authorize()` - Authorization endpoint with PKCE and resource parameter support |
| 75 | +- `handle_token()` - Token endpoint supporting authorization_code and refresh_token grants |
| 76 | +- `handle_client_registration()` - Dynamic Client Registration per RFC 7591 |
| 77 | +- `generate_authorization_code()` - Create PKCE-enabled codes with expiration |
| 78 | +- `validate_pkce()` - PKCE code challenge verification |
| 79 | + |
| 80 | +#### Provider Manager (`provider_manager.py`) |
| 81 | +Single OAuth provider integration: |
| 82 | +- **Domain-wide authentication** using one configured OAuth provider |
| 83 | +- **Google OAuth** with OpenID Connect support |
| 84 | +- **GitHub OAuth** with user email scope |
| 85 | +- **Custom OAuth providers** with configurable endpoints |
| 86 | +- **Note**: Due to OAuth 2.1 resource parameter constraints, only one provider can be configured per gateway instance |
| 87 | + |
| 88 | +**Key classes and methods:** |
| 89 | +- `OAuthProvider` - Base provider class with common interface |
| 90 | +- `GoogleOAuthProvider`, `GitHubOAuthProvider`, `OktaOAuthProvider`, `CustomOAuthProvider` - Provider implementations |
| 91 | +- `ProviderManager` - Single provider management with validation |
| 92 | +- `authenticate_user(provider: str, code: str)` - Exchange code for user info |
| 93 | +- `get_provider_for_service(service_id: str)` - Returns the configured provider |
| 94 | + |
| 95 | +#### Token Manager (`token_manager.py`) |
| 96 | +JWT token creation and validation: |
| 97 | +- **Service-specific audience claims** per RFC 8707 |
| 98 | +- **Configurable token expiration** (default 1 hour) |
| 99 | +- **Resource binding** to prevent token misuse |
| 100 | +- **HS256 signing** with shared secret |
| 101 | + |
| 102 | +**Key methods:** |
| 103 | +- `create_access_token(user: UserInfo, client_id: str, resource: str)` - Generate JWT with audience binding |
| 104 | +- `create_refresh_token(user: UserInfo, client_id: str)` - Secure refresh token generation |
| 105 | +- `validate_access_token(token: str, resource: str)` - Verify and decode JWT with audience validation |
| 106 | +- `revoke_tokens()` - Token revocation support for client and token-specific scenarios |
| 107 | + |
| 108 | +#### Client Registry (`client_registry.py`) |
| 109 | +Dynamic Client Registration per RFC 7591: |
| 110 | +- **Automatic client registration** for MCP clients |
| 111 | +- **Client credential generation** with secure random secrets |
| 112 | +- **Redirect URI validation** for security |
| 113 | +- **Comprehensive validation** - Grant types, auth methods, response types |
| 114 | +- **Deduplication support** - Prevents duplicate registrations for same client |
| 115 | +- **In-memory client storage** (suitable for development) |
| 116 | + |
| 117 | +### 3. Configuration (`config/config.py`) |
| 118 | +YAML-based configuration management: |
| 119 | +- **Environment variable substitution** (${VAR_NAME} syntax) |
| 120 | +- **OAuth provider configuration** with credentials and scopes |
| 121 | +- **MCP service definitions** with auth requirements |
| 122 | +- **Gateway settings** (host, port, issuer, session secret) |
| 123 | + |
| 124 | +**Configuration structure:** |
| 125 | +```yaml |
| 126 | +host: "0.0.0.0" |
| 127 | +port: 8080 |
| 128 | +issuer: "http://localhost:8080" |
| 129 | +session_secret: "your-secret-key" |
| 130 | + |
| 131 | +# Single OAuth provider configuration |
| 132 | +# Only one provider can be configured per gateway instance |
| 133 | +oauth_providers: |
| 134 | + google: # Configure only one provider |
| 135 | + client_id: "${GOOGLE_CLIENT_ID}" |
| 136 | + client_secret: "${GOOGLE_CLIENT_SECRET}" |
| 137 | + scopes: ["openid", "email", "profile"] |
| 138 | + |
| 139 | +mcp_services: |
| 140 | + calculator: |
| 141 | + name: "Calculator Service" |
| 142 | + url: "http://localhost:3001/mcp/" |
| 143 | + oauth_provider: "google" # Must match the configured provider |
| 144 | + auth_required: true |
| 145 | + scopes: ["read", "calculate"] |
| 146 | +``` |
| 147 | +
|
| 148 | +### 4. MCP Proxy (`proxy/mcp_proxy.py`) |
| 149 | +HTTP request forwarding with user context injection: |
| 150 | +- **Transparent proxying** to backend MCP services |
| 151 | +- **User context headers** (`x-user-id`, `x-user-email`, etc.) |
| 152 | +- **Streamable HTTP support** for MCP protocol |
| 153 | +- **Timeout handling** and error propagation |
| 154 | + |
| 155 | +**Key features:** |
| 156 | +- Preserves original request method and body |
| 157 | +- Adds user context from validated JWT tokens |
| 158 | +- Handles both JSON-RPC and streaming responses |
| 159 | +- Configurable timeouts per service |
| 160 | + |
| 161 | +### 5. API Endpoints (`api/metadata.py`) |
| 162 | +OAuth metadata endpoints per RFCs: |
| 163 | +- **Authorization Server Metadata** (RFC 8414) at `/.well-known/oauth-authorization-server` |
| 164 | +- **Protected Resource Metadata** (RFC 9728) at `/.well-known/oauth-protected-resource` |
| 165 | +- **Service discovery** endpoints for MCP services |
| 166 | + |
| 167 | +## Development Guidelines |
| 168 | + |
| 169 | +### Configuring OAuth Provider |
| 170 | + |
| 171 | +**Important**: Due to OAuth 2.1 resource parameter constraints, only one OAuth provider can be configured per gateway instance. |
| 172 | + |
| 173 | +1. **Configure single provider** in `config.yaml`: |
| 174 | +```yaml |
| 175 | +oauth_providers: |
| 176 | + # Choose ONE provider only |
| 177 | + google: # OR github, okta, custom - but only one |
| 178 | + client_id: "${GOOGLE_CLIENT_ID}" |
| 179 | + client_secret: "${GOOGLE_CLIENT_SECRET}" |
| 180 | + scopes: ["openid", "email", "profile"] |
| 181 | + |
| 182 | + # For custom providers: |
| 183 | + # custom: |
| 184 | + # authorization_url: "https://provider.com/oauth/authorize" |
| 185 | + # token_url: "https://provider.com/oauth/token" |
| 186 | + # userinfo_url: "https://provider.com/oauth/userinfo" |
| 187 | + # client_id: "${CUSTOM_CLIENT_ID}" |
| 188 | + # client_secret: "${CUSTOM_CLIENT_SECRET}" |
| 189 | + # scopes: ["read", "profile"] |
| 190 | +``` |
| 191 | + |
| 192 | +2. **Update all MCP services** to use the same provider: |
| 193 | +```yaml |
| 194 | +mcp_services: |
| 195 | + service1: |
| 196 | + oauth_provider: "google" # Must match configured provider |
| 197 | + service2: |
| 198 | + oauth_provider: "google" # All services use same provider |
| 199 | +``` |
| 200 | + |
| 201 | +3. **Test the provider** with your MCP services |
| 202 | + |
| 203 | +### Adding New MCP Services |
| 204 | + |
| 205 | +1. **Configure the service** in `config.yaml`: |
| 206 | +```yaml |
| 207 | +mcp_services: |
| 208 | + new_service: |
| 209 | + name: "New MCP Service" |
| 210 | + url: "http://backend:3001/mcp" |
| 211 | + oauth_provider: "google" # Must match the configured OAuth provider |
| 212 | + auth_required: true |
| 213 | + scopes: ["read", "write"] |
| 214 | + timeout: 30000 |
| 215 | +``` |
| 216 | + |
| 217 | +2. **The service will be automatically available** at `/{service-id}/mcp` |
| 218 | +3. **Backend services receive user context** via headers |
| 219 | +4. **All services must use the same OAuth provider** configured in the gateway |
| 220 | + |
| 221 | +### Code Style and Standards |
| 222 | + |
| 223 | +- **Use Pydantic models** for all data validation |
| 224 | +- **Follow FastAPI patterns** for dependency injection |
| 225 | +- **Use async/await** for all I/O operations |
| 226 | +- **Add type hints** to all functions |
| 227 | +- **Use structured logging** with context |
| 228 | +- **Format code** with Black and Ruff |
| 229 | + |
| 230 | +### Testing |
| 231 | + |
| 232 | +- **Unit test suite** - 15 test files covering individual components with mocking |
| 233 | +- **OAuth 2.1 component testing** - PKCE validation, token exchange, metadata endpoints |
| 234 | +- **Security boundary testing** - Token validation, redirect URI validation, error paths |
| 235 | +- **Configuration validation testing** - Single provider constraints, service configuration |
| 236 | +- **Component isolation testing** - Mocked HTTP requests, isolated functionality testing |
| 237 | +- **pytest framework** with async support, HTTP client mocking, and component fixtures |
| 238 | +- **Test utilities** - PKCE generation helpers and crypto validation tools |
| 239 | + |
| 240 | +### Security Considerations |
| 241 | + |
| 242 | +- **NEVER log sensitive data** (tokens, secrets, user data) |
| 243 | +- **Validate all input** using Pydantic models |
| 244 | +- **Use secure random generation** for codes and secrets |
| 245 | +- **Implement proper CORS** for web clients |
| 246 | +- **Enforce HTTPS** in production |
| 247 | +- **Validate redirect URIs** strictly |
| 248 | + |
| 249 | +### Production Deployment |
| 250 | + |
| 251 | +- **Use environment variables** for all secrets |
| 252 | +- **Configure proper logging** (JSON format recommended) |
| 253 | +- **Set up health checks** for monitoring |
| 254 | +- **Use HTTPS** with proper certificates |
| 255 | +- **Configure reverse proxy** if needed |
| 256 | +- **Monitor OAuth flow metrics** |
| 257 | + |
| 258 | +## Common Development Tasks |
| 259 | + |
| 260 | +### Running the Gateway |
| 261 | +```bash |
| 262 | +# Development with auto-reload |
| 263 | +python -m src.gateway --config config.yaml --debug |
| 264 | +
|
| 265 | +# Production mode |
| 266 | +python -m src.gateway --config config.yaml |
| 267 | +``` |
| 268 | + |
| 269 | +### Code Quality |
| 270 | +```bash |
| 271 | +# Format and lint |
| 272 | +ruff check src/ --fix |
| 273 | +ruff format src/ |
| 274 | +
|
| 275 | +# Run tests |
| 276 | +pytest |
| 277 | +
|
| 278 | +# Type checking (if mypy is added) |
| 279 | +mypy src/ |
| 280 | +``` |
| 281 | + |
| 282 | +### Docker Development |
| 283 | +```bash |
| 284 | +# Build image |
| 285 | +docker build -t mcp-oauth-gateway . |
| 286 | +
|
| 287 | +# Run with config |
| 288 | +docker run -p 8080:8080 \ |
| 289 | + -v $(pwd)/config.yaml:/app/config.yaml \ |
| 290 | + mcp-oauth-gateway |
| 291 | +``` |
| 292 | + |
| 293 | +## Architecture Notes |
| 294 | + |
| 295 | +### OAuth 2.1 Compliance |
| 296 | +- **PKCE required** for all authorization code flows |
| 297 | +- **Resource parameter** for audience binding per RFC 8707 |
| 298 | +- **Dynamic Client Registration** per RFC 7591 |
| 299 | +- **Proper metadata endpoints** per RFC 8414 and RFC 9728 |
| 300 | + |
| 301 | +### MCP Protocol Support |
| 302 | +- **Streamable HTTP transport** as specified in MCP |
| 303 | +- **User context injection** for backend authorization |
| 304 | +- **Transparent proxying** maintains MCP protocol semantics |
| 305 | +- **Service-specific token scoping** prevents privilege escalation |
| 306 | + |
| 307 | +### Security Architecture |
| 308 | +- **Service isolation** through audience-bound tokens |
| 309 | +- **Single provider design** ensures consistent authentication |
| 310 | +- **Session management** with secure, signed sessions |
| 311 | +- **State validation** prevents CSRF attacks |
| 312 | + |
| 313 | +## Known Limitations |
| 314 | + |
| 315 | +- **Single OAuth provider** per gateway instance due to OAuth 2.1 resource parameter constraints |
| 316 | +- **In-memory storage** for sessions and clients (not suitable for multi-instance deployment) |
| 317 | +- **Limited refresh token support** - Implemented but not exposed as public endpoint |
| 318 | +- **No public token revocation endpoint** - Functionality exists but not exposed |
| 319 | +- **Limited to HTTP transport** for MCP (WebSocket not supported) |
| 320 | +- **No persistent user storage** (users re-authenticate each session) |
| 321 | +- **No token introspection endpoint** - Functionality exists but not exposed |
| 322 | + |
| 323 | +## Future Enhancements |
| 324 | + |
| 325 | +- **Redis/database backend** for session storage |
| 326 | +- **Public refresh token endpoint** exposure |
| 327 | +- **Public token revocation endpoint** exposure |
| 328 | +- **Token introspection endpoint** exposure |
| 329 | +- **WebSocket transport** for MCP services |
| 330 | +- **User management interface** for administrators |
| 331 | +- **Metrics and observability** integration |
| 332 | +- **Rate limiting** for OAuth endpoints |
| 333 | +- **Multi-instance deployment** support with shared storage |
0 commit comments