Skip to content

atrawog/mcp-oauth-gateway

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MCP OAuth Gateway

An OAuth 2.1 Authorization Server that adds authentication to any MCP (Model Context Protocol) server without code modification. The gateway acts as an OAuth Authorization Server while using GitHub as the Identity Provider (IdP) for user authentication.

πŸ“– View Documentation | πŸ”§ Installation Guide | πŸ—οΈ Architecture Overview

⚠️ Important Notice

This is a reference implementation and test platform for the MCP protocol.

  • Primary Purpose: Reference implementation for MCP protocol development and testing
  • Experimental Nature: Used as a test platform for future MCP protocol iterations
  • Security Disclaimer: While this implementation strives for security best practices, this implementation likely contains bugs and security vulnerabilities
  • Production Warning: NOT recommended for production use without thorough security review
  • Use at Your Own Risk: This is experimental software intended for development and testing

πŸ—οΈ Architecture

Overview

The MCP OAuth Gateway is a zero-modification authentication layer for MCP servers. It implements OAuth 2.1 with dynamic client registration (RFC 7591/7592) and leverages GitHub as the identity provider for user authentication. The architecture follows these core principles:

  • Complete Separation of Concerns: Authentication, routing, and MCP protocol handling are strictly isolated
  • No MCP Server Modifications: Official MCP servers run unmodified, wrapped only for HTTP transport
  • Standards Compliance: Full OAuth 2.1, RFC 7591/7592, and MCP protocol compliance
  • Production-Ready Security: HTTPS everywhere, PKCE mandatory, JWT tokens, secure session management
  • Dynamic Service Discovery: Services can be enabled/disabled via configuration

System Components

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                                   EXTERNAL CLIENTS                                  β”‚
β”‚         (Claude.ai, MCP CLI tools, IDE extensions, Custom integrations)             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                           β”‚
                                     HTTPS β”‚ :443
                                           ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                               TRAEFIK REVERSE PROXY                                 β”‚
β”‚                          (Layer 1: Routing & TLS Termination)                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ Let's Encrypt automatic HTTPS certificates for all subdomains                     β”‚
β”‚ β€’ Priority-based routing rules (OAuth > Verify > MCP > Catch-all)                   β”‚
β”‚ β€’ ForwardAuth middleware for MCP endpoints β†’ Auth Service /verify                   β”‚
β”‚ β€’ Request routing based on subdomain and path:                                      β”‚
β”‚   - auth.domain.com/* β†’ Auth Service (no auth required)                             β”‚
β”‚   - *.domain.com/.well-known/* β†’ Auth Service (OAuth discovery)                     β”‚
β”‚   - *.domain.com/mcp β†’ MCP Services (auth required via ForwardAuth)                 β”‚
β”‚ β€’ Docker service discovery via labels                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚                                              β”‚
                    β”‚ OAuth/Auth Requests                          β”‚ MCP Requests
                    β”‚ (unauthenticated)                            β”‚ (authenticated)
                    ↓                                              ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           AUTH SERVICE                    β”‚    β”‚         MCP SERVICES                β”‚
β”‚   (Layer 2: OAuth Authorization Server)   β”‚    β”‚    (Layer 3: Protocol Handlers)     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Container: auth:8000                      β”‚    β”‚ Containers:                         β”‚
β”‚ Package: mcp-oauth-dynamicclient          β”‚    β”‚ β€’ mcp-echo-stateful:3000            β”‚
β”‚                                           β”‚    β”‚ β€’ mcp-echo-stateless:3000           β”‚
β”‚                                           β”‚    β”‚ β€’ mcp-fetch:3000                    β”‚
β”‚ OAuth Endpoints:                          β”‚    β”‚ β€’ mcp-memory:3000                   β”‚
β”‚ β€’ POST /register (RFC 7591)               β”‚    β”‚ β€’ mcp-time:3000                     β”‚
β”‚ β€’ GET /authorize + /callback              β”‚    β”‚ β€’ ... (dynamically enabled)         β”‚
β”‚ β€’ POST /token                             β”‚    β”‚                                     β”‚
β”‚ β€’ GET /.well-known/* (RFC 8414)           β”‚    β”‚ Architecture:                       β”‚
β”‚ β€’ POST /revoke, /introspect               β”‚    β”‚ β€’ mcp-streamablehttp-proxy wrapper  β”‚
β”‚                                           β”‚    β”‚ β€’ Spawns official MCP stdio servers β”‚
β”‚ Management Endpoints (RFC 7592):          β”‚    β”‚ β€’ Bridges stdio ↔ HTTP/SSE          β”‚
β”‚ β€’ GET/PUT/DELETE /register/{client_id}    β”‚    β”‚ β€’ No OAuth knowledge                β”‚
β”‚                                           β”‚    β”‚ β€’ Receives user identity in headers β”‚
β”‚ Internal Endpoints:                       β”‚    β”‚                                     β”‚
β”‚ β€’ GET/POST /verify (ForwardAuth)          β”‚    β”‚ Protocol Endpoints:                 β”‚
β”‚                                           β”‚    β”‚ β€’ POST /mcp (JSON-RPC over HTTP)    β”‚
β”‚ External Integration:                     │←---β”‚ β€’ GET /mcp (SSE for async messages) β”‚
β”‚ β€’ GitHub OAuth (user authentication)      β”‚    β”‚ β€’ Health checks on /health          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚                                              ↑
                    β”‚                                              β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                   ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                                REDIS STORAGE LAYER                                  β”‚
β”‚                            (Persistent State Management)                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Container: redis:6379                                                               β”‚
β”‚ Persistence: AOF + RDB snapshots                                                    β”‚
β”‚                                                                                     β”‚
β”‚ Data Structures:                                                                    β”‚
β”‚ β€’ oauth:client:{client_id} β†’ OAuth client registrations (90 days / eternal)         β”‚
β”‚ β€’ oauth:state:{state} β†’ Authorization flow state (5 minutes)                        β”‚
β”‚ β€’ oauth:code:{code} β†’ Authorization codes + user info (1 year)                      β”‚
β”‚ β€’ oauth:token:{jti} β†’ JWT token tracking for revocation (30 days)                   β”‚
β”‚ β€’ oauth:refresh:{token} β†’ Refresh token data (1 year)                               β”‚
β”‚ β€’ oauth:user_tokens:{username} β†’ User's active tokens index                         β”‚
β”‚ β€’ redis:session:{id}:state β†’ MCP session state (managed by proxy)                   β”‚
β”‚ β€’ redis:session:{id}:messages β†’ MCP message queues                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

NETWORK TOPOLOGY: β€’ All services connected via 'public' Docker network β€’ Internal service communication only (except Traefik ingress) β€’ Redis exposed on localhost:6379 for debugging only β€’ Each MCP service runs in isolated container with no shared state

Security Architecture

Authentication Layers

  1. TLS/HTTPS: Enforced by Traefik for all external communication
  2. OAuth Client Authentication: client_id + client_secret at token endpoint
  3. User Authentication: GitHub OAuth with ALLOWED_GITHUB_USERS whitelist
  4. Token Authentication: JWT Bearer tokens for API access
  5. PKCE Protection: Mandatory S256 code challenges

Security Boundaries

  • Public Access: Only /register and /.well-known/* endpoints
  • Client-Authenticated: /token endpoint requires client credentials
  • User-Authenticated: /authorize requires GitHub login
  • Bearer-Authenticated: All /mcp endpoints require valid JWT
  • Registration-Token-Authenticated: Client management endpoints (RFC 7592)

Token Types and Scopes

  • registration_access_token: Bearer token for client management only (RFC 7592)
  • access_token: JWT containing user identity + client_id for MCP access
  • refresh_token: Opaque token for obtaining new access tokens
  • authorization_code: One-time code binding user to client

Architectural Decisions

Why Three Layers?

  1. Traefik (Routing): Centralizes TLS, routing, and auth enforcement
  2. Auth Service (OAuth): Isolated OAuth implementation, no MCP knowledge
  3. MCP Services (Protocol): Pure MCP protocol handlers, no auth knowledge

Why mcp-streamablehttp-proxy?

  • Wraps official stdio-based MCP servers without modification
  • Provides HTTP transport required for web clients
  • Manages subprocess lifecycle and session state
  • Enables horizontal scaling possibilities

Why Redis?

  • Fast, reliable state storage for OAuth flows
  • Supports atomic operations for security
  • Built-in TTL for automatic cleanup
  • Enables distributed deployment if needed

Why GitHub OAuth?

  • Trusted identity provider for developers
  • No password management needed
  • Strong security with 2FA support
  • Rich user profile information

OAuth Architecture: Client Credentials + User Authentication

The gateway implements a multi-layered OAuth 2.1 authorization system that integrates client credential authentication with GitHub OAuth identity federation:

The gateway implements a sophisticated OAuth 2.1 system with three distinct authentication flows:

  1. GitHub Device Flow (RFC 8628) - For command-line/browserless scenarios
  2. GitHub OAuth Web Flow - For browser-based end-user authentication
  3. Dynamic Client Registration (RFC 7591) - For MCP client registration

Authentication Flow Decision Tree

Need Authentication?
β”œβ”€> For Gateway's Own GitHub Access?
β”‚   └─> Use Device Flow: `just generate-github-token`
β”‚       - Shows code: "Visit github.com/login/device"
β”‚       - No browser redirect needed
β”‚       - Stores GITHUB_PAT in .env
β”‚
β”œβ”€> For MCP Client Token?
β”‚   └─> Use Device Flow: `just mcp-client-token`
β”‚       - Client uses device flow for browserless auth
β”‚       - Stores MCP_CLIENT_ACCESS_TOKEN in .env
β”‚
└─> For End User Access (Browser)?
    └─> Use Standard OAuth Flow
        - User visits protected resource
        - Redirected to GitHub for login
        - Redirected back to gateway
        - JWT issued with user+client identity

The gateway implementation combines client credential authentication with GitHub user authentication:

╔═══════════════════════════════════════════════════════════════════════════════════╗
β•‘                        OAUTH CLIENT REGISTRATION (RFC 7591/7592)                  β•‘
╠═══════════════════════════════════════════════════════════════════════════════════╣
β•‘                                                                                   β•‘
β•‘  πŸ“ STEP 1: CLIENT REGISTRATION (No Authentication Required)                      β•‘
β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β•‘
β•‘  β”‚ POST /register                                                              β”‚  β•‘
β•‘  β”‚ β€’ Public endpoint - any MCP client can register                             β”‚  β•‘
β•‘  β”‚ β€’ Creates OAuth client application credentials                              β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ Request Body:                                                               β”‚  β•‘
β•‘  β”‚ {                                                                           β”‚  β•‘
β•‘  β”‚   "redirect_uris": ["https://example.com/callback"],                        β”‚  β•‘
β•‘  β”‚   "client_name": "My MCP Client"                                            β”‚  β•‘
β•‘  β”‚ }                                                                           β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ Response:                                                                   β”‚  β•‘
β•‘  β”‚ β€’ client_id: "client_abc123..."          ← OAuth client credentials         β”‚  β•‘
β•‘  β”‚ β€’ client_secret: "secret_xyz789..."      ← Used at /token endpoint          β”‚  β•‘
β•‘  β”‚ β€’ registration_access_token: "reg_tok..."← ONLY for client management       β”‚  β•‘
β•‘  β”‚ β€’ registration_client_uri: "https://auth.../register/client_abc123"         β”‚  β•‘
β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
β•‘                                                                                   β•‘
β•‘  πŸ”§ OPTIONAL: CLIENT MANAGEMENT (Requires registration_access_token)              β•‘
β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β•‘
β•‘  β”‚ Authorization: Bearer <registration_access_token>                           β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ β€’ GET /register/{client_id}    - View client configuration                  β”‚  β•‘
β•‘  β”‚ β€’ PUT /register/{client_id}    - Update redirect URIs, etc.                 β”‚  β•‘
β•‘  β”‚ β€’ DELETE /register/{client_id} - Delete client registration                 β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ Note: This token is ONLY for managing the client registration,              β”‚  β•‘
β•‘  β”‚       NOT for accessing MCP resources!                                      β”‚  β•‘
β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
β•‘                                                                                   β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

                                         ↓
                    Client has credentials, now needs user authorization
                                         ↓

╔═══════════════════════════════════════════════════════════════════════════════════╗
β•‘                           USER AUTHENTICATION FLOW (GitHub OAuth)                 β•‘
╠═══════════════════════════════════════════════════════════════════════════════════╣
β•‘                                                                                   β•‘
β•‘  πŸ‘€ STEP 2: USER AUTHORIZATION (Human authenticates via GitHub)                   β•‘
β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β•‘
β•‘  β”‚ GET /authorize?client_id=client_abc123&redirect_uri=...&code_challenge=...  β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ 1. Gateway validates client_id exists                                       β”‚  β•‘
β•‘  β”‚ 2. Redirects user to GitHub OAuth:                                          β”‚  β•‘
β•‘  β”‚    β†’ User logs into GitHub                                                  β”‚  β•‘
β•‘  β”‚    β†’ GitHub authenticates the human user                                    β”‚  β•‘
β•‘  β”‚    β†’ Returns to gateway /callback with GitHub user info                     β”‚  β•‘
β•‘  β”‚ 3. Gateway checks ALLOWED_GITHUB_USERS whitelist                            β”‚  β•‘
β•‘  β”‚ 4. Creates authorization code tied to:                                      β”‚  β•‘
β•‘  β”‚    β€’ The OAuth client (client_id)                                           β”‚  β•‘
β•‘  β”‚    β€’ The GitHub user (username, email, etc.)                                β”‚  β•‘
β•‘  β”‚ 5. Redirects back to client with code                                       β”‚  β•‘
β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
β•‘                                                                                   β•‘
β•‘  🎫 STEP 3: TOKEN EXCHANGE (Client credentials + Authorization code)              β•‘
β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β•‘
β•‘  β”‚ POST /token                                                                 β”‚  β•‘
β•‘  β”‚ Content-Type: application/x-www-form-urlencoded                             β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ Request:                                                                    β”‚  β•‘
β•‘  β”‚ β€’ client_id=client_abc123          ← Authenticates the OAuth client         β”‚  β•‘
β•‘  β”‚ β€’ client_secret=secret_xyz789      ← Proves client identity                 β”‚  β•‘
β•‘  β”‚ β€’ code=auth_code_from_step_2       ← Contains GitHub user info              β”‚  β•‘
β•‘  β”‚ β€’ code_verifier=pkce_verifier      ← PKCE verification                      β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ Response:                                                                   β”‚  β•‘
β•‘  β”‚ β€’ access_token: JWT containing:                                             β”‚  β•‘
β•‘  β”‚   - sub: GitHub user ID                                                     β”‚  β•‘
β•‘  β”‚   - username: GitHub username                                               β”‚  β•‘
β•‘  β”‚   - email: GitHub email                                                     β”‚  β•‘
β•‘  β”‚   - client_id: client_abc123                                                β”‚  β•‘
β•‘  β”‚ β€’ refresh_token: For renewing access                                        β”‚  β•‘
β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
β•‘                                                                                   β•‘
β•‘  πŸ›‘οΈ STEP 4: RESOURCE ACCESS (Using the access token)                              β•‘
β•‘  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β•‘
β•‘  β”‚ Authorization: Bearer <access_token>                                        β”‚  β•‘
β•‘  β”‚                                                                             β”‚  β•‘
β•‘  β”‚ β€’ Token contains BOTH client_id AND user identity                           β”‚  β•‘
β•‘  β”‚ β€’ Traefik ForwardAuth validates token via /verify                           β”‚  β•‘
β•‘  β”‚ β€’ User identity passed to MCP services as headers:                          β”‚  β•‘
β•‘  β”‚   - X-User-Id: GitHub user ID                                               β”‚  β•‘
β•‘  β”‚   - X-User-Name: GitHub username                                            β”‚  β•‘
β•‘  β”‚ β€’ Access granted to /mcp endpoints on all enabled services                  β”‚  β•‘
β•‘  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β•‘
β•‘                                                                                   β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

πŸ”‘ ARCHITECTURAL PRINCIPLES:

  • client_id + client_secret establish OAuth client identity (e.g., Claude.ai application)
  • GitHub OAuth provides user identity federation and authentication
  • The resultant access_token JWT encapsulates both client and user identity claims
  • registration_access_token is scoped exclusively to client lifecycle management operations

OAuth Roles

  1. MCP OAuth Gateway - OAuth 2.1 Authorization Server

    • Client Registration: Implements RFC 7591/7592 for dynamic client registration
    • User Authentication: Integrates with GitHub OAuth as the Identity Provider
    • Token Issuance: Issues JWT access tokens containing both client and user identity
    • Token Types:
      • registration_access_token: Only for managing client registrations (RFC 7592)
      • access_token: JWT with user claims + client_id for accessing MCP resources
      • refresh_token: For renewing access tokens
  2. GitHub OAuth - Identity Provider (IdP)

    • Authenticates human users through GitHub's OAuth flow
    • Provides user identity (ID, username, email) to the gateway
    • Gateway validates users against ALLOWED_GITHUB_USERS whitelist
    • User info is embedded in the final JWT access token
  3. MCP Servers - Protected Resources

    • Run unmodified official MCP servers wrapped with mcp-streamablehttp-proxy
    • Protected by OAuth without any code changes
    • Receive pre-authenticated requests with user identity in headers
    • Support various protocol versions based on implementation

πŸš€ Try It Out - Public Test Servers

Before setting up your own gateway, you can test the OAuth flow and MCP integration using my publicly available test servers:

Available Test Servers

How to Use

These servers are configured to accept any GitHub user for authentication:

  1. Make a request to either test server endpoint
  2. Receive 401 Unauthorized with OAuth discovery information
  3. Complete OAuth flow using your GitHub account
  4. Access granted - Use the bearer token for subsequent MCP requests

The test servers implement the full OAuth 2.1 + MCP 2025-06-18 integration:

  • Dynamic client registration (RFC 7591)
  • GitHub OAuth for user authentication
  • PKCE protection for security
  • StreamableHTTP transport for MCP protocol

These servers are ideal for:

  • Testing MCP client implementations
  • Understanding the OAuth flow
  • Validating integration before deployment
  • Educational purposes and demos

πŸ“‹ Requirements

System Requirements

  • Docker and Docker Compose (required for all services)
  • pixi - Modern Python package manager
  • just - Command runner (all commands go through just)
  • Python 3.11+ (managed automatically by pixi)

Infrastructure Requirements

  • Public IP address and properly configured DNS (MANDATORY)
    • All subdomains must resolve to your server:
      • auth.your-domain.com - OAuth authorization server
      • service.your-domain.com - Each MCP service subdomain
    • Ports 80 and 443 must be accessible from the internet

GitHub OAuth App

You'll need to create a GitHub OAuth App that serves two distinct purposes:

  1. End-User Authentication (Browser-based OAuth flow)

    • When users access protected MCP services via browser/Claude.ai
    • Users are redirected to GitHub for authentication
    • Requires callback URL: https://auth.yourdomain.com/callback
  2. Gateway Self-Authentication (Device flow)

    • When the gateway itself needs GitHub access
    • Uses device flow (no browser redirect)
    • Initiated by just generate-github-token

πŸ“ Repository Structure

This repository uses Git submodules for better modularity. All Python packages are maintained as submodules:

Submodule Packages

Main Repository Components

  • auth/ - OAuth authorization server implementation
  • traefik/ - Reverse proxy configuration and routing
  • mcp- service directories* - Docker configurations for MCP services
  • tests/ - Comprehensive test suite
  • docs/ - Jupyter Book documentation
  • scripts/ - Utility and automation scripts

πŸš€ Installation

1. Clone and Setup

# Clone the repository with submodules
git clone --recurse-submodules https://github.com/atrawog/mcp-oauth-gateway.git
cd mcp-oauth-gateway

# If you already cloned without submodules, initialize them
git submodule update --init --recursive

# Install dependencies with pixi
pixi install

# Run initial setup
just setup

2. Configure Environment

# 1. Copy the example configuration
cp .env.example .env

# 2. Generate required secrets
just generate-all-secrets    # Generates JWT secret, RSA keys, and Redis password

# 3. Edit .env with your configuration
nano .env

Edit your .env file with the required configuration:

  1. Domain Setup (REQUIRED)

    BASE_DOMAIN=your-domain.com      # Your actual domain
    [email protected] # Email for Let's Encrypt
  2. GitHub OAuth App (REQUIRED)

    GITHUB_CLIENT_ID=your_client_id
    GITHUB_CLIENT_SECRET=your_client_secret
  3. Access Control (REQUIRED)

    # Option 1: Whitelist specific users
    ALLOWED_GITHUB_USERS=user1,user2,user3
    # Option 2: Allow any authenticated GitHub user
    ALLOWED_GITHUB_USERS=*

3. Create GitHub OAuth App

  1. Go to GitHub OAuth Apps
  2. Click "New OAuth App"
  3. Fill in:
    • Application name: MCP OAuth Gateway
    • Homepage URL: https://your-domain.com
    • Authorization callback URL: https://auth.your-domain.com/callback
  4. Save the Client ID and Client Secret to your .env file

4. Start the Gateway

# Start all services
just up

# Check service health
just check-health

πŸ”§ Configuration

All configuration is managed through the .env file. The gateway uses dynamic service selection - you can enable or disable individual MCP services based on your needs.

πŸ”‘ Token Requirements Summary

Minimum tokens required to run the gateway:

Token Purpose How to Get Required?
GITHUB_CLIENT_ID GitHub OAuth App ID Create at github.com/settings/developers βœ… YES
GITHUB_CLIENT_SECRET GitHub OAuth App Secret Create at github.com/settings/developers βœ… YES
GATEWAY_JWT_SECRET JWT signing secret Run: just generate-jwt-secret βœ… YES
JWT_PRIVATE_KEY_B64 RSA key for JWT Run: just generate-rsa-keys βœ… YES
REDIS_PASSWORD Redis security Run: just generate-redis-password βœ… YES

Tokens only needed for testing:

Token Purpose How to Get Required?
GITHUB_PAT GitHub API access in tests Run: just generate-github-token ❌ NO
GATEWAY_OAUTH_* tokens Test suite authentication Generated during test setup ❌ NO
MCP_CLIENT_* tokens Testing MCP clients Run: just mcp-client-token ❌ NO

⚑ Key Point: The gateway itself doesn't use OAuth tokens - it only needs GitHub OAuth App credentials to authenticate users!

Configuration Categories

1. Core Settings (REQUIRED)

# Domain Configuration - MUST be publicly accessible domains!
BASE_DOMAIN=your-domain.com
[email protected]

# GitHub OAuth App Credentials
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret

# Security Keys (generated by just commands)
GATEWAY_JWT_SECRET=<generated-by-just-generate-jwt-secret>

# Redis Security
REDIS_PASSWORD=your_secure_redis_password

2. Access Control

# User Whitelist Options:
ALLOWED_GITHUB_USERS=user1,user2,user3  # Specific users only
# OR
ALLOWED_GITHUB_USERS=*                  # Any GitHub user

3. Token Configuration

# Token Lifetimes (in seconds)
CLIENT_LIFETIME=7776000        # Client registration: 90 days (0 = eternal)
# Note: Access and refresh token lifetimes are configured in .env

4. MCP Service Management

The gateway allows you to enable/disable individual MCP services. All services default to true (enabled) if not specified.

# Core MCP Services
MCP_FETCH_ENABLED=true              # Web content fetching (stdio proxy)
MCP_FETCHS_ENABLED=true             # Native Python fetch implementation
MCP_FILESYSTEM_ENABLED=true         # File system access (sandboxed)
MCP_MEMORY_ENABLED=true             # Persistent memory/knowledge graph
MCP_TIME_ENABLED=true               # Time and timezone operations

# Advanced MCP Services
MCP_SEQUENTIALTHINKING_ENABLED=true # Structured problem solving
MCP_TMUX_ENABLED=true               # Terminal multiplexer integration
MCP_PLAYWRIGHT_ENABLED=false        # Browser automation (resource intensive)
MCP_EVERYTHING_ENABLED=true         # Test server with all features

# Echo Diagnostic Services (Choose one or both)
MCP_ECHO_STATEFUL_ENABLED=true      # Echo with session state & replayLastEcho
MCP_ECHO_STATELESS_ENABLED=true     # Echo without state management

5. Protocol Configuration

# MCP Protocol Version (used by services that support it)
MCP_PROTOCOL_VERSION=2025-06-18

# Note: Some services only support specific versions:
# - mcp-memory: 2024-11-05
# - mcp-sequentialthinking: 2024-11-05
# - mcp-fetch, mcp-filesystem, mcp-time: 2025-03-26
# - Others: 2025-06-18
Available MCP Services
Service Description Protocol Version Container Port
mcp-echo-stateful Diagnostic tools with session state & memory 2025-06-18 3000
mcp-echo-stateless Diagnostic tools without state management 2025-06-18 3000
mcp-fetch Web content fetching (stdio wrapper) 2025-03-26 3000
mcp-fetchs Native Python fetch implementation 2025-06-18 3000
mcp-filesystem File system access (sandboxed) 2025-03-26 3000
mcp-memory Persistent memory/knowledge graph 2024-11-05 3000
mcp-sequentialthinking Structured problem solving 2024-11-05 3000
mcp-time Time and timezone operations 2025-03-26 3000
mcp-tmux Terminal multiplexer integration 2025-06-18 3000
mcp-playwright Browser automation 2025-06-18 3000
mcp-everything Test server with all features 2025-06-18 3000

All services use mcp-streamablehttp-proxy to wrap official MCP stdio servers, exposing them via HTTP on port 3000.

Gateway Extensions Beyond MCP Protocol

The gateway adds several features not specified in the MCP protocol:

  • RFC 7592 Client Management: Complete client lifecycle management (GET/PUT/DELETE operations)
  • GitHub OAuth Integration: User authentication through GitHub as Identity Provider

6. Test Configuration

# Enable/disable tests for specific services
MCP_FETCH_TESTS_ENABLED=false
MCP_FETCHS_TESTS_ENABLED=false
MCP_FILESYSTEM_TESTS_ENABLED=false
MCP_MEMORY_TESTS_ENABLED=false
MCP_PLAYWRIGHT_TESTS_ENABLED=false
MCP_SEQUENTIALTHINKING_TESTS_ENABLED=false
MCP_TIME_TESTS_ENABLED=false
MCP_TMUX_TESTS_ENABLED=false
MCP_EVERYTHING_TESTS_ENABLED=false
MCP_ECHO_STATEFUL_TESTS_ENABLED=false
MCP_ECHO_STATELESS_TESTS_ENABLED=true

# Test parameters
TEST_HTTP_TIMEOUT=30.0
TEST_MAX_RETRIES=3
TEST_RETRY_DELAY=1.0

About

An OAuth 2.1 Authorization Server that adds authentication to any MCP (Model Context Protocol) server without code modification.

Topics

Resources

License

Stars

Watchers

Forks

Languages