Skip to content

Commit 2c2adf2

Browse files
committed
feat: init commit
0 parents  commit 2c2adf2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+9093
-0
lines changed

.gitignore

Lines changed: 400 additions & 0 deletions
Large diffs are not rendered by default.

ARCHITECTURE.md

Lines changed: 981 additions & 0 deletions
Large diffs are not rendered by default.

CLAUDE.md

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
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

Dockerfile

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Multi-stage build for smaller production image
2+
FROM python:3.11-slim as builder
3+
4+
# Set working directory
5+
WORKDIR /app
6+
7+
# Install system dependencies for building
8+
RUN apt-get update && apt-get install -y \
9+
build-essential \
10+
curl \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
# Copy requirements and install Python dependencies
14+
COPY requirements.txt .
15+
RUN pip install --no-cache-dir --user -r requirements.txt
16+
17+
# Production stage
18+
FROM python:3.11-slim
19+
20+
# Set working directory
21+
WORKDIR /app
22+
23+
# Create non-root user
24+
RUN groupadd -r mcpuser && useradd -r -g mcpuser mcpuser
25+
26+
# Install runtime dependencies
27+
RUN apt-get update && apt-get install -y \
28+
curl \
29+
&& rm -rf /var/lib/apt/lists/*
30+
31+
# Copy Python packages from builder stage
32+
COPY --from=builder /root/.local /home/mcpuser/.local
33+
34+
# Copy application code
35+
COPY src/ ./src/
36+
COPY demo/ ./demo/
37+
38+
# Copy configuration files
39+
COPY pyproject.toml .
40+
41+
# Create directories and set permissions
42+
RUN mkdir -p /app/logs /app/data && \
43+
chown -R mcpuser:mcpuser /app
44+
45+
# Switch to non-root user
46+
USER mcpuser
47+
48+
# Add local Python packages to PATH
49+
ENV PATH=/home/mcpuser/.local/bin:$PATH
50+
51+
# Set Python path
52+
ENV PYTHONPATH=/app
53+
54+
# Expose port
55+
EXPOSE 8080
56+
57+
# Health check
58+
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
59+
CMD curl -f http://localhost:8080/health || exit 1
60+
61+
# Default command
62+
CMD ["python", "-m", "src.gateway", "--host", "0.0.0.0", "--port", "8080"]

0 commit comments

Comments
 (0)