A OAuth 2.1 authorization server that provides transparent authentication and authorization for Model Context Protocol (MCP) services.
- 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
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# 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 infopip install -r requirements.txt
# Optional: For Redis storage backend with modern library
pip install -r requirements-redis.txtImportant: 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
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"]python -m src.gateway --config config.yaml --debugAccess your service to verify it's working:
curl http://localhost:8080/calculator/mcp
# Should return 401 with OAuth authentication infoReplace 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 clients start by accessing a service endpoint:
GET /calculator/mcp HTTP/1.1
Host: localhost:8080The gateway responds with OAuth metadata:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource"Clients fetch OAuth metadata:
curl http://localhost:8080/.well-known/oauth-authorization-server
curl http://localhost:8080/.well-known/oauth-protected-resourceClients register automatically:
curl -X POST http://localhost:8080/oauth/register \
  -d "client_name=My MCP Client" \
  -d "redirect_uris=http://localhost:8080/callback"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
 
host: "0.0.0.0"
port: 8080
issuer: "https://mcp-gateway.example.com"
session_secret: "production-secret-key"
debug: falseConfigure 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"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:
  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 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.jpgServices can use these headers for:
- User identification and authorization
 - Audit logging
 - Personalized responses
 - User-specific data access
 
# 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# 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# 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:latestGET /.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
GET /services- List available servicesGET /services/{service-id}- Get service infoALL /{service-id}/mcp- MCP service proxy
GET /- Gateway informationGET /health- Health check
- PKCE required for all authorization code flows
 - Resource parameter binding per RFC 8707
 - Proper token audience validation
 - Secure redirect URI validation
 
- JWT tokens with service-specific audience claims
 - Short-lived access tokens (1 hour)
 - Refresh token rotation for public clients
 - Token revocation support
 
- Single OAuth provider per gateway instance
 - Provider-specific user authentication
 - Secure credential storage
 - State parameter CSRF protection
 
# 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# Format and lint code
ruff check src/ demo/ --fix
ruff format src/ demo/MCP_CONFIG_PATH- Path to config fileMCP_GATEWAY_HOST- Host overrideMCP_GATEWAY_PORT- Port overrideMCP_DEBUG- Debug mode
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)
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
Choose the appropriate storage backend for your deployment:
storage:
  type: "memory"✅ Best for: Development, testing, single-instance demos
❌ Limitations: Data lost on restart, single-instance only
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
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
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
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
 
- 📖 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
 
MIT License - see LICENSE file for details.