A OAuth 2.1 authorization server that provides transparent authentication and authorization for Model Context Protocol (MCP) services.
MCP OAuth Gateway
A OAuth 2.1 authorization server that provides transparent authentication and authorization for Model Context Protocol (MCP) services.
Features
- Transparent MCP Access: Users access MCP services via simple URLs without manual OAuth setup
- Single OAuth Provider: Uses one OAuth provider for all services (Google, GitHub, Okta, or custom)
- Full MCP Compliance: Implements complete MCP authorization specification with OAuth 2.1
- Dynamic Client Registration: Automatic client registration per RFC 7591
- User Context Injection: Seamless user context headers for backend MCP services
- Resource-Specific Tokens: RFC 8707 audience binding prevents token misuse
- Configurable Storage: Memory (dev), Redis (production), Vault (enterprise) backends
- Production Ready: Comprehensive testing, Docker support, scalable architecture
📖 View Detailed Architecture | 📚 Developer Guide
Quick Start with Docker (Recommended)
Pre-built Image
Use the pre-built Docker image from GitHub Container Registry:
# Run with memory storage (development)
docker run -p 8080:8080 \
-v $(pwd)/config.yaml:/app/config.yaml \
-e GOOGLE_CLIENT_ID="your-google-client-id" \
-e GOOGLE_CLIENT_SECRET="your-google-client-secret" \
ghcr.io/akshay5995/mcp-oauth-gateway:latest
Docker Compose (Full Stack)
# Copy environment template
cp .env.example .env
# Edit .env with your OAuth credentials
# Start all services (gateway + Redis + demo calculator)
docker-compose up -d
# Test the setup
curl http://localhost:8080/health
curl http://localhost:8080/calculator/mcp # Should return 401 with OAuth info
Local Development Setup
1. Install Dependencies
pip install -r requirements.txt
# Optional: For Redis storage backend with modern library
pip install -r requirements-redis.txt
2. Configure OAuth Provider
Important: Configure only ONE OAuth provider per gateway instance.
Set up environment variables for Google OAuth:
export GOOGLE_CLIENT_ID="your-google-client-id"
export GOOGLE_CLIENT_SECRET="your-google-client-secret"
📚 Other providers: See Configuration Guide for GitHub, Okta, and custom OAuth providers
3. Create Basic Configuration
Create a config.yaml
file:
# Gateway settings
host: "localhost"
port: 8080
issuer: "http://localhost:8080"
session_secret: "your-dev-secret-change-in-production"
debug: true
# OAuth provider
oauth_providers:
google:
client_id: "${GOOGLE_CLIENT_ID}"
client_secret: "${GOOGLE_CLIENT_SECRET}"
scopes: ["openid", "email", "profile"]
# Example service (replace with your MCP service)
mcp_services:
calculator:
name: "Calculator Service"
url: "http://localhost:3001"
oauth_provider: "google"
auth_required: true
scopes: ["read", "calculate"]
4. Run the Gateway
python -m src.gateway --config config.yaml --debug
5. Test the Setup
Access your service to verify it's working:
curl http://localhost:8080/calculator/mcp
# Should return 401 with OAuth authentication info
6. Add Your Services
Replace the example service in config.yaml
with your actual MCP services. All services must use the same OAuth provider.
📚 Complete Configuration Guide - Detailed service configuration options
MCP Client Integration
1. Discovery
MCP clients start by accessing a service endpoint:
GET /calculator/mcp HTTP/1.1
Host: localhost:8080
The gateway responds with OAuth metadata:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource"
2. Metadata Retrieval
Clients fetch OAuth metadata:
curl http://localhost:8080/.well-known/oauth-authorization-server
curl http://localhost:8080/.well-known/oauth-protected-resource
3. Dynamic Client Registration
Clients register automatically:
curl -X POST http://localhost:8080/oauth/register \
-d "client_name=My MCP Client" \
-d "redirect_uris=http://localhost:8080/callback"
4. Authorization Flow
Clients follow standard OAuth 2.1 with PKCE:
- Authorization request with resource parameter
- User authentication via configured provider
- Authorization code exchange for access token
- Authenticated MCP requests
Configuration
Gateway Settings
host: "0.0.0.0"
port: 8080
issuer: "https://mcp-gateway.example.com"
session_secret: "production-secret-key"
debug: false
CORS Configuration
Configure Cross-Origin Resource Sharing (CORS) for web clients:
cors:
allow_origins: ["*"] # Allowed origins (use specific domains in production)
allow_credentials: true # Allow credentials in CORS requests
allow_methods: # Allowed HTTP methods
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allow_headers: ["*"] # Allowed headers (use specific headers in production)
For production deployments, restrict CORS settings:
cors:
allow_origins:
- "https://myapp.example.com"
- "https://dashboard.example.com"
allow_credentials: true
allow_methods: ["GET", "POST", "OPTIONS"]
allow_headers:
- "Authorization"
- "Content-Type"
- "MCP-Protocol-Version"
OAuth Provider Configuration
Important: Configure only ONE OAuth provider per gateway instance due to OAuth 2.1 resource parameter constraints.
oauth_providers:
google:
client_id: "${GOOGLE_CLIENT_ID}"
client_secret: "${GOOGLE_CLIENT_SECRET}"
scopes: ["openid", "email", "profile"]
📚 Alternative providers: See Configuration Guide for GitHub, Okta, and custom OAuth provider examples
MCP Services
mcp_services:
calculator:
name: "Calculator Service"
url: "http://calculator:3001"
oauth_provider: "google" # Must match the configured OAuth provider
auth_required: true
scopes: ["read", "calculate"]
timeout: 30000
# All authenticated services must use the same OAuth provider
weather:
name: "Weather Service"
url: "http://weather:3002"
oauth_provider: "google" # Same as above
auth_required: true
scopes: ["read"]
Backend Service Integration
Backend MCP services receive requests with user context headers:
GET /mcp HTTP/1.1
Host: calculator:3001
x-user-id: google_user_123456
x-user-email: user@example.com
x-user-name: John Doe
x-user-provider: google
x-user-avatar: https://example.com/avatar.jpg
Services can use these headers for:
- User identification and authorization
- Audit logging
- Personalized responses
- User-specific data access
Advanced Docker Deployment
Build from Source
# Build image locally
docker build -t mcp-oauth-gateway .
# Run with custom build
docker run -p 8080:8080 \
-v $(pwd)/config.yaml:/app/config.yaml \
-e GOOGLE_CLIENT_ID="your-google-client-id" \
-e GOOGLE_CLIENT_SECRET="your-google-client-secret" \
mcp-oauth-gateway
Production with Redis Storage
# Start Redis container
docker run -d --name redis \
-p 6379:6379 \
redis:alpine redis-server --requirepass mypassword
# Update config.yaml for Redis
cat >> config.yaml << EOF
storage:
type: "redis"
redis:
host: "host.docker.internal" # or Redis container IP
port: 6379
password: "\${REDIS_PASSWORD}"
EOF
# Run gateway with Redis
docker run -p 8080:8080 \
-v $(pwd)/config.yaml:/app/config.yaml \
-e GOOGLE_CLIENT_ID="your-google-client-id" \
-e GOOGLE_CLIENT_SECRET="your-google-client-secret" \
-e REDIS_PASSWORD="mypassword" \
ghcr.io/akshay5995/mcp-oauth-gateway:latest
Enterprise with Vault Storage
# Start Vault container (dev mode)
docker run -d --name vault \
-p 8200:8200 \
-e VAULT_DEV_ROOT_TOKEN_ID="myroot" \
vault:latest
# Update config.yaml for Vault
cat >> config.yaml << EOF
storage:
type: "vault"
vault:
url: "http://host.docker.internal:8200"
token: "\${VAULT_TOKEN}"
mount_point: "secret"
path_prefix: "mcp-gateway"
EOF
# Run gateway with Vault
docker run -p 8080:8080 \
-v $(pwd)/config.yaml:/app/config.yaml \
-e GOOGLE_CLIENT_ID="your-google-client-id" \
-e GOOGLE_CLIENT_SECRET="your-google-client-secret" \
-e VAULT_TOKEN="myroot" \
ghcr.io/akshay5995/mcp-oauth-gateway:latest
API Endpoints
OAuth 2.1 Endpoints
GET /.well-known/oauth-authorization-server
- Server metadataGET /.well-known/oauth-protected-resource
- Resource metadataGET /oauth/authorize
- Authorization endpointPOST /oauth/token
- Token endpointPOST /oauth/register
- Dynamic client registration
Service Endpoints
GET /services
- List available servicesGET /services/{service-id}
- Get service infoALL /{service-id}/mcp
- MCP service proxy
Utility Endpoints
GET /
- Gateway informationGET /health
- Health check
Security Features
OAuth 2.1 Compliance
- PKCE required for all authorization code flows
- Resource parameter binding per RFC 8707
- Proper token audience validation
- Secure redirect URI validation
Token Security
- JWT tokens with service-specific audience claims
- Short-lived access tokens (1 hour)
- Refresh token rotation for public clients
- Token revocation support
Provider Security
- Single OAuth provider per gateway instance
- Provider-specific user authentication
- Secure credential storage
- State parameter CSRF protection
Development
Running Tests
# Install test dependencies (included in requirements.txt)
pip install pytest pytest-asyncio pytest-httpx
# Run all tests
pytest tests/
# Run with coverage
pytest tests/ --cov=src
# Run specific test file
pytest tests/test_oauth_server.py -v
Code Style
# Format and lint code
ruff check src/ demo/ --fix
ruff format src/ demo/
Environment Variables
Gateway Configuration
MCP_CONFIG_PATH
- Path to config fileMCP_GATEWAY_HOST
- Host overrideMCP_GATEWAY_PORT
- Port overrideMCP_DEBUG
- Debug mode
OAuth Providers
GOOGLE_CLIENT_ID
- Google OAuth client IDGOOGLE_CLIENT_SECRET
- Google OAuth client secretGITHUB_CLIENT_ID
- GitHub OAuth client IDGITHUB_CLIENT_SECRET
- GitHub OAuth client secretOKTA_CLIENT_ID
- Okta OAuth client IDOKTA_CLIENT_SECRET
- Okta OAuth client secretOKTA_DOMAIN
- Okta domain (e.g., dev-123.okta.com)
Storage Backends
REDIS_HOST
- Redis server hostREDIS_PORT
- Redis server portREDIS_PASSWORD
- Redis authentication passwordREDIS_SSL
- Enable Redis SSL (true/false)VAULT_URL
- Vault server URLVAULT_TOKEN
- Vault authentication tokenVAULT_MOUNT_POINT
- Vault KV mount pointVAULT_PATH_PREFIX
- Vault secret path prefix
Storage Backends
Choose the appropriate storage backend for your deployment:
Memory Storage (Default)
storage:
type: "memory"
✅ Best for: Development, testing, single-instance demos
❌ Limitations: Data lost on restart, single-instance only
Redis Storage (Production)
storage:
type: "redis"
redis:
host: "${REDIS_HOST:-localhost}"
port: 6379
password: "${REDIS_PASSWORD}"
ssl: true
max_connections: 20
✅ Best for: Production deployments, horizontal scaling
✅ Features: Persistent storage, multi-instance support, connection pooling
✅ Compatibility: Uses modern redis-py library for Python 3.11+ compatibility
Vault Storage (Enterprise)
storage:
type: "vault"
vault:
url: "${VAULT_URL}"
token: "${VAULT_TOKEN}"
mount_point: "secret"
path_prefix: "mcp-gateway"
auth_method: "token" # or "approle", "kubernetes"
✅ Best for: Enterprise environments, compliance requirements
✅ Features: Encrypted at rest, audit logging, fine-grained access control
Architecture
The gateway implements a clean separation of concerns:
- OAuth Server: Core OAuth 2.1 authorization server
- Provider Manager: External OAuth provider integration
- Client Registry: Dynamic client registration and management
- Token Manager: JWT token creation and validation
- Storage Manager: Configurable storage backends with fallback
- MCP Proxy: Request forwarding with user context injection
- Metadata Provider: OAuth metadata endpoint implementation
📖 View Complete Architecture Documentation
Troubleshooting
Having issues? Check the troubleshooting guide:
📚 Troubleshooting Guide - Common issues and solutions including:
- Origin validation errors (403 responses)
- MCP protocol version issues (400 responses)
- Token audience validation problems (401 responses)
- Configuration and deployment issues
Quick Links
- 📖 Architecture Documentation - Comprehensive system design and data flows
- 📚 Developer Guide - Detailed development instructions and API reference
- 🧪 Testing Guide - 197+ test cases covering all components
- 🐳 Docker Examples - Production deployment patterns
License
MIT License - see LICENSE file for details.