mcp-oauth

R167/mcp-oauth

3.1

If you are the rightful owner of mcp-oauth and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.

The MCP OAuth Authorization Server is a robust, production-ready OAuth 2.1/OIDC authorization server designed to secure Model Context Protocol (MCP) resources, leveraging Cloudflare Workers for deployment.

MCP OAuth Authorization Server

A production-ready OAuth 2.1/OIDC authorization server built on Cloudflare Workers for securing Model Context Protocol (MCP) resources. The server implements 2-hop authentication via GitHub with comprehensive security features.

Features

  • OAuth 2.1 Compliant: Authorization Code flow with PKCE
  • GitHub Integration: 2-hop authentication via GitHub OAuth
  • MCP Scope Validation: Fine-grained access control for MCP servers
  • Security: JWT tokens, encrypted refresh tokens, PKCE enforcement
  • Cloudflare Workers: Serverless deployment with D1 database storage
  • Standards Compliance: OAuth 2.1, OIDC Discovery, JWKS

Architecture

See for detailed architecture documentation.

Quick Start

Development

# Install dependencies
pnpm install

# Start development server
pnpm run dev

Server runs on http://localhost:8787 with auto-generated test keys.

Staging Deployment (Ready to Use!)

The project includes a pre-configured staging environment:

# 1. Set up secrets (interactive script)
./scripts/setup-secrets.sh staging

# 2. Deploy to staging
pnpm run deploy:staging

# 3. Test deployment
curl https://mcp-oauth-authorization-server-staging.wmdurand.workers.dev/health

Staging URL: https://mcp-oauth-authorization-server-staging.wmdurand.workers.dev

Note: You must configure secrets before the server will work properly.

Production Deployment

# 1. Set up production secrets
./scripts/setup-secrets.sh prod

# 2. Deploy to production
pnpm run deploy:prod

Production URL: https://auth.mcp.r167.dev

Configuration

GitHub OAuth App Setup

Create a GitHub OAuth App with these settings:

  • Staging callback: https://mcp-oauth-authorization-server-staging.wmdurand.workers.dev/auth/github/callback
  • Production callback: https://auth.mcp.r167.dev/auth/github/callback

MCP Servers

Configure your MCP servers in src/config.json (production) or src/config.staging.json (staging):

{
  "servers": {
    "your-domain.com": {
      "your-server": {
        "name": "Your MCP Server",
        "description": "Description of what this server provides",
        "allowed_users": ["github-username1", "github-username2"]
      }
    }
  }
}

Required Configuration

Cloudflare Secrets
VariableDescription
GITHUB_CLIENT_IDGitHub OAuth app client ID
GITHUB_CLIENT_SECRETGitHub OAuth app client secret
JWT_PRIVATE_KEYRS256 private key for JWT signing
JWT_PUBLIC_KEYRS256 public key for JWT verification
REFRESH_ENCRYPTION_KEYAES-256 key for refresh token encryption
Environment Variables
VariableDescriptionSet in
WORKER_BASE_URLFull URL of the deployed workerwrangler.jsonc

Development

# Start development server
pnpm run dev

# Run tests
pnpm test

# Type checking
pnpm run type-check

# Format code
pnpm run format

Automated Secret Setup

Use the provided script to easily configure all required secrets:

# For staging environment
./scripts/setup-secrets.sh staging

# For production environment  
./scripts/setup-secrets.sh prod

The script will:

  • Prompt for GitHub OAuth credentials
  • Auto-generate JWT key pair (or accept manual input)
  • Auto-generate refresh token encryption key (or accept manual input)
  • Configure all secrets automatically

Usage

OAuth Flow

  1. Authorization Request: Client redirects user to /authorize with PKCE parameters
  2. GitHub Authentication: User authenticates with GitHub
  3. Scope Validation: Server validates MCP scope and user permissions
  4. Consent Screen: User approves access (skipped if previously approved)
  5. Authorization Code: Server returns authorization code to client
  6. Token Exchange: Client exchanges code for access/refresh tokens using PKCE verifier

Client Registration

All OAuth clients must be registered before use via the dynamic client registration endpoint:

curl -X POST https://auth.mcp.r167.dev/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My MCP Client",
    "redirect_uris": ["https://example.com/callback"],
    "scope": "mcp:example.com:github-tools email"
  }'

Response:

{
  "client_id": "mcp_example.com_12345...",
  "client_name": "My MCP Client", 
  "redirect_uris": ["https://example.com/callback"],
  "scope": "mcp:example.com:github-tools email",
  "expires_at": 1698765432,
  "registration_client_uri": "https://auth.mcp.r167.dev/client/mcp_example.com_12345..."
}

Client Expiration: Clients expire after 60 days of inactivity. Expiration is refreshed whenever refresh tokens are used.

MCP Scope Format

Scopes must follow the pattern: mcp:<domain>:<server> where:

  • domain: Must match the redirect URI domain and client registration domain
  • server: Must exist in src/config.json configuration
  • User must be in the server's allowed_users list

Example: mcp:example.com:github-tools email

API Endpoints

  • GET /authorize - OAuth authorization endpoint
  • POST /token - Token exchange endpoint
  • GET /.well-known/jwks.json - Public keys for token validation
  • GET /.well-known/oauth-authorization-server - OAuth metadata
  • GET /auth/github/callback - GitHub OAuth callback
  • POST /validate - Validate JWT access tokens
  • POST /admin/revoke - Revoke access or refresh tokens
  • POST /register - Register new OAuth client (dynamic registration)
  • GET /client/{client_id} - Get client information
  • GET /health - Health check

Token Validation

Option 1: Server-side Validation (Recommended)

Resource servers validate access tokens by:

  1. Fetching public keys from /.well-known/jwks.json
  2. Verifying JWT signature with RS256
  3. Validating claims (issuer, audience, expiration)
  4. Checking audience matches expected MCP scope

Example validation (pseudo-code):

const token = request.headers.authorization.replace('Bearer ', '');
const publicKey = await fetchPublicKey();
const payload = await verifyJWT(token, publicKey);

if (payload.aud === 'mcp:example.com:github-tools') {
  // Grant access to user payload.sub (GitHub user ID)
}
Option 2: Using the Validation Endpoint

For debugging or simple validation, use the /validate endpoint:

# With Authorization header
curl -X POST https://auth.mcp.r167.dev/validate \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# With request body
curl -X POST https://auth.mcp.r167.dev/validate \
  -H "Content-Type: application/json" \
  -d '{"token": "YOUR_ACCESS_TOKEN"}'

Response:

{
  "valid": true,
  "payload": {
    "token_type": "access",
    "sub": "12345678",
    "aud": "mcp:example.com:github-tools",
    "iss": "https://auth.mcp.r167.dev",
    "exp": 1698765432,
    "iat": 1698761832,
    "email": "user@example.com"
  }
}

Note: The system uses D1 database for state storage, not KV storage.

Security Features

  • PKCE: All authorization flows require PKCE for security
  • Encrypted Refresh Tokens: AES-GCM encryption with key rotation
  • Scope Binding: Tokens bound to specific MCP scopes
  • Domain Validation: MCP scope domain must match redirect URI
  • User Authorization: ACL-based access control per MCP server
  • Token Rotation: Refresh tokens are rotated on use
  • Secure Headers: Proper CORS and security headers

Configuration

D1 Database

The server automatically creates required tables on first startup:

  • authorization_codes - Temporary authorization codes (10 min TTL)
  • refresh_tokens - Refresh token metadata (30 day TTL)
  • user_sessions - User authentication sessions (30 min TTL)
  • client_approvals - Stored consent decisions (30 day TTL)
  • revoked_tokens - Revoked refresh tokens (expires with token)
  • registered_clients - Dynamic client registrations (60 day inactivity expiration)

Key Rotation

Refresh token encryption supports key rotation:

# Generate new encryption key
NEW_KEY=$(openssl rand -base64 32)
wrangler secret put REFRESH_ENCRYPTION_KEY --text "$NEW_KEY"

Old tokens remain valid during transition period.

Monitoring

  • Health check endpoint: GET /health
  • Cloudflare Workers analytics and logs
  • Error logging for debugging

Advanced Deployment

For detailed deployment procedures, advanced configuration, and troubleshooting, see .

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Run tests: pnpm test
  4. Submit a pull request

License

MIT License - see LICENSE file for details.