Files
mcphub/docs/OAUTH_GUIDE.md
airano cf62e65c55 Initial commit: MCP Hub Community Edition v3.0.0
Community edition generated from private repo via sync pipeline.
Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n,
Supabase, OpenPanel, Appwrite, Directus) with ~587 tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 08:34:44 +03:30

29 KiB

OAuth 2.1 Authentication Guide

Complete guide for using OAuth 2.1 authentication with MCP Hub.


📋 Table of Contents

  1. Overview
  2. Quick Start
  3. Configuration
  4. OAuth Flows
  5. Registering OAuth Clients
  6. OpenAI GPT Integration
  7. Claude Custom Connectors
  8. Testing
  9. Security Best Practices
  10. Troubleshooting

Overview

MCP Hub implements OAuth 2.1 with API Key-Based Authorization and MCP Specification Compliance:

  • API Key Authentication - Users authorize with their existing API Keys
  • Permission Inheritance - OAuth tokens inherit API Key's scope and project access
  • PKCE Mandatory - Proof Key for Code Exchange (S256)
  • Refresh Token Rotation - Security best practice
  • JWT Access Tokens - Stateless validation
  • Multiple Grant Types - Authorization Code, Refresh Token, Client Credentials
  • Scope-based Authorization - Fine-grained access control
  • Backward Compatible - API Keys still work
  • Open DCR for MCP Clients - Claude/ChatGPT can register automatically (RFC 7591) 🆕
  • Protected Resource Metadata - RFC 9728 compliant endpoints 🆕
  • 401 + WWW-Authenticate - Proper OAuth discovery for MCP clients 🆕

Security Model (Updated for MCP Compliance)

🔓 Open DCR for Trusted Clients: Claude and ChatGPT can automatically register OAuth clients without authentication:

  • Redirect URIs must match allowlist (claude.ai, chatgpt.com, localhost)
  • Rate limited per IP (10/min, 30/hour)
  • All registrations are audit logged

🔐 Protected DCR for Custom Apps: Custom applications still require Master API Key for registration.

🔒 API Key Authorization: Users MUST provide their API Key when authorizing OAuth clients (required mode).

🎯 Permission Inheritance: OAuth tokens automatically inherit the API Key's permissions:

  • Master API Key → OAuth token with full access
  • Per-project API Key → OAuth token limited to that project
  • Read-only API Key → OAuth token with read-only access

Claude/ChatGPT Integration: MCP clients automatically discover OAuth endpoints and register themselves.

Supported Grant Types

  1. Authorization Code (with PKCE) - For third-party apps
  2. Refresh Token (with rotation) - Renew access tokens
  3. Client Credentials - Machine-to-machine auth

Quick Start

1. Generate JWT Secret

# Generate a secure secret key
python -c "import secrets; print(secrets.token_urlsafe(64))"

# OR using openssl
openssl rand -base64 64

2. Configure Environment

Add to .env:

# Required
OAUTH_JWT_SECRET_KEY=your_generated_secret_key_here

# Optional (defaults shown)
OAUTH_JWT_ALGORITHM=HS256
OAUTH_ACCESS_TOKEN_TTL=3600      # 1 hour
OAUTH_REFRESH_TOKEN_TTL=604800   # 7 days

3. Rebuild & Deploy

# Local development
docker-compose up --build -d

# Coolify deployment
# Update environment variables in Coolify UI and redeploy

Configuration

Environment Variables

Variable Description Default Required
OAUTH_JWT_SECRET_KEY Secret key for signing JWTs - Yes
OAUTH_JWT_ALGORITHM JWT signing algorithm HS256 No
OAUTH_ACCESS_TOKEN_TTL Access token lifetime (seconds) 3600 (1h) No
OAUTH_REFRESH_TOKEN_TTL Refresh token lifetime (seconds) 604800 (7d) No
OAUTH_STORAGE_TYPE Storage backend json No
OAUTH_STORAGE_PATH Storage path for JSON files /app/data No

JWT Algorithms

Supported algorithms:

  • HS256 (default) - HMAC with SHA-256
  • HS384 - HMAC with SHA-384
  • HS512 - HMAC with SHA-512
  • RS256 - RSA with SHA-256 (requires RSA keys)
  • RS384 - RSA with SHA-384
  • RS512 - RSA with SHA-512

OAuth Flows

Authorization Code Flow (with PKCE)

Use Case: Third-party applications (like OpenAI GPTs)

┌──────────┐                                  ┌──────────────┐
│  Client  │                                  │  MCP Server  │
└────┬─────┘                                  └──────┬───────┘
     │                                                │
     │  1. Generate PKCE verifier & challenge         │
     │─────────────────────────────────────────────►  │
     │                                                │
     │  2. GET /oauth/authorize?                      │
     │     client_id=xxx&                             │
     │     redirect_uri=https://app.com/callback&     │
     │     response_type=code&                        │
     │     code_challenge=yyy&                        │
     │     code_challenge_method=S256&                │
     │     scope=read+write&                          │
     │     state=random_state                         │
     │───────────────────────────────────────────────►│
     │                                                │
     │  3. Authorization Code (5-min expiry)          │
     │◄───────────────────────────────────────────────│
     │                                                │
     │  4. POST /oauth/token                          │
     │     grant_type=authorization_code&             │
     │     client_id=xxx&                             │
     │     client_secret=zzz&                         │
     │     code=auth_abc&                             │
     │     redirect_uri=https://app.com/callback&     │
     │     code_verifier=original_verifier            │
     │───────────────────────────────────────────────►│
     │                                                │
     │  5. Access Token + Refresh Token               │
     │◄───────────────────────────────────────────────│
     │                                                │

Refresh Token Flow

Use Case: Renewing expired access tokens

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
client_id=cmp_client_xxx&
client_secret=your_secret&
refresh_token=rt_xxx

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "rt_new_token",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read write"
}

Security: Old refresh token is immediately revoked (rotation).

Client Credentials Flow

Use Case: Machine-to-machine authentication

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&
client_id=cmp_client_xxx&
client_secret=your_secret&
scope=read

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read"
}

Note: No refresh token for client credentials.


Registering OAuth Clients

Two Registration Modes

The /oauth/register endpoint supports two authentication modes:

1. Open DCR (No Auth Required) 🆕

For trusted MCP clients (Claude, ChatGPT), registration is automatic:

# Claude/ChatGPT automatically calls this - no auth needed
curl -X POST https://your-mcp-server.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Claude MCP Client",
    "redirect_uris": ["https://claude.ai/oauth/callback"],
    "grant_types": ["authorization_code", "refresh_token"]
  }'

Allowed Redirect URI Patterns:

  • https://claude.ai/*
  • https://claude.com/*
  • https://chatgpt.com/*
  • https://chat.openai.com/*
  • https://platform.openai.com/*
  • http://localhost:*/* (development)
  • http://127.0.0.1:*/* (development)

Rate Limits (per IP):

  • 10 requests per minute
  • 30 requests per hour

2. Protected DCR (Master API Key Required)

For custom applications with non-allowlisted redirect URIs:

curl -X POST https://your-mcp-server.com/oauth/register \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My Custom App",
    "redirect_uris": ["https://myapp.com/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "scope": "read write"
  }'

Security Flow (MCP Compliant)

1. MCP Client (Claude) discovers OAuth via /.well-known/oauth-authorization-server
2. MCP Client registers via Open DCR at /oauth/register (no auth for trusted URIs)
3. MCP Client receives client_id + client_secret
4. User is redirected to /oauth/authorize
5. User enters their API Key → Authorization code returned
6. MCP Client exchanges code for tokens at /oauth/token
7. OAuth token inherits user's API Key permissions

Benefits:

  • Claude/ChatGPT connect with one click (Open DCR)
  • Custom apps still require admin approval
  • Users control their own permissions via API Keys
  • Rate limiting prevents abuse

Example:

  • User has "read-only" API Key → OAuth token gets "read-only" access
  • User has project-specific API Key → OAuth token limited to that project
  • User has Master API Key → OAuth token gets full access

ChatGPT Integration (OAuth Manual Mode)

ChatGPT now supports OAuth (manual) integration where you manually configure the OAuth client credentials.

⚠️ Security Considerations

Current Limitation: Due to ChatGPT OAuth (manual) design:

  • ChatGPT cannot pass user-specific API Keys in authorization URL
  • Must use OAUTH_AUTH_MODE=optional which allows anyone with the authorization URL to connect
  • OAuth tokens issued to ChatGPT get full access (no API Key inheritance)

Security Measures You Can Take:

  1. Use Minimal Scopes (Recommended):

    • Register ChatGPT client with scope: "read" only
    • Create separate clients for different access levels
  2. Private Deployment Only:

    • Only share client credentials with trusted users
    • Use this integration for personal/team use, not public GPTs
  3. Monitor Usage:

    • Check audit logs regularly: tail -f logs/audit.log
    • Review OAuth tokens: use oauth_list_tokens() tool
  4. Future Enhancement (Phase E - Planned):

    • 🔒 Custom Authorization Page (Priority 2)
      • Beautiful HTML form for API Key input
      • OAUTH_AUTH_MODE=required works with ChatGPT
      • Per-user access control
      • Multi-language support (EN/FA)
      • See docs/ROADMAP.md Phase E for details
    • IP whitelisting for OAuth endpoints
    • Per-client rate limiting

For Production/Public Use: Consider using OAUTH_AUTH_MODE=required with a custom client that supports API Key parameters, instead of ChatGPT OAuth (manual).


Step-by-Step Setup:

1. Register OAuth Client (Admin Only)

Use MCP tool or API with Master API Key:

# Using curl with Master API Key
curl -X POST https://your-mcp-server.com/oauth/register \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "ChatGPT OAuth Integration",
    "redirect_uris": ["https://chatgpt.com/connector_platform_oauth_redirect"],
    "grant_types": ["authorization_code", "refresh_token"],
    "scope": "read"
  }'

Response: Save the client_id and client_secret (shown only once!)

2. Configure ChatGPT

  1. Go to ChatGPT → Settings → Authentication
  2. Select OAuth (manual)
  3. Enter:
    • Client ID: cmp_client_xxx (from step 1)
    • Client Secret: secret_xxx (from step 1)
    • Authorization URL: https://your-mcp-server.com/oauth/authorize
    • Token URL: https://your-mcp-server.com/oauth/token
    • Scope: read write

3. Configure Environment for ChatGPT

⚠️ IMPORTANT SECURITY NOTE: ChatGPT OAuth (manual) has a limitation:

ChatGPT builds the authorization URL automatically and does not allow users to add API Key parameters. Therefore, you must use optional mode:

# In .env file
OAUTH_AUTH_MODE=optional

Security Implications:

  • ⚠️ Anyone with the URL can authorize - Client ID/Secret alone don't prevent access
  • ⚠️ OAuth tokens get full access - No API Key = no permission inheritance
  • 🔒 Mitigation: Use restricted scope when registering the client

Recommended Scopes for ChatGPT:

  • read - Read-only access (safest)
  • read write - Allow content creation (moderate risk)
  • ⚠️ Avoid admin scope for public ChatGPT integrations

4. User Authorization

When users connect ChatGPT to your MCP server:

  1. ChatGPT redirects to authorization endpoint automatically
  2. Server validates client_id and redirect_uri
  3. User approves the connection
  4. OAuth token is issued with the scope defined during client registration
  5. Redirects back to ChatGPT - Done!

Best Practice: Register separate OAuth clients for different use cases with minimal required scopes

Using MCP Tools

You can also pre-register clients using MCP tools:

# 1. Register a new OAuth client
result = await oauth_register_client(
    client_name="My OpenAI GPT",
    redirect_uris="https://chat.openai.com/aip/callback",
    grant_types="authorization_code,refresh_token",
    allowed_scopes="read,write"
)

# Save these credentials securely!
print(f"Client ID: {result['client_id']}")
print(f"Client Secret: {result['client_secret']}")  # Only shown once!

Using Direct API (Admin Required)

⚠️ Master API Key Required - Only server administrators can register OAuth clients.

curl -X POST https://mcp-dev.mcphub.dev/oauth/register \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My Application",
    "redirect_uris": ["https://example.com/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "scope": "read write"
  }'

Success Response (201 Created):

{
  "client_id": "cmp_client_abc123",
  "client_secret": "very_long_secret_string",
  "client_id_issued_at": 1234567890,
  "client_secret_expires_at": 0,
  "client_name": "My Application",
  "redirect_uris": ["https://example.com/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "client_secret_post"
}

⚠️ Important: Save client_secret immediately - it's shown only once!

Error Response (401 Unauthorized):

{
  "error": "unauthorized",
  "error_description": "Master API Key required for OAuth client registration. Add 'Authorization: Bearer YOUR_MASTER_API_KEY' header."
}

List Registered Clients

result = await oauth_list_clients()

for client in result['clients']:
    print(f"{client['client_name']}: {client['client_id']}")

Revoke a Client

result = await oauth_revoke_client(
    client_id="cmp_client_abc123"
)

OpenAI GPT Integration

Step 1: Register OAuth Client

Use the MCP tool to register:

result = await oauth_register_client(
    client_name="OpenAI GPT Integration",
    redirect_uris="https://chat.openai.com/aip/callback,https://chatgpt.com/aip/callback",
    allowed_scopes="read,write"
)

# Save these!
# Client ID: cmp_client_abc123
# Client Secret: very_long_secret_string

Step 2: Configure GPT Action

In OpenAI GPT editor, add an Action with:

Authentication Type: OAuth

Client ID: cmp_client_abc123

Client Secret: very_long_secret_string

Authorization URL: https://your-mcp-server.com/oauth/authorize

Token URL: https://your-mcp-server.com/oauth/token

Scope: read write

Token Exchange Method: POST with Basic auth

Step 3: Test Integration

  1. In GPT conversation, trigger an action
  2. OAuth consent flow will start
  3. User approves (or auto-approved in MCP context)
  4. GPT receives access token
  5. GPT can now call MCP tools via OAuth!

Claude Custom Connectors (Remote MCP)

Claude supports Custom Connectors for Remote MCP servers, allowing direct integration without SSH tunnels or local setup.

Requirements

  • Claude Plan: Pro, Max, Team, or Enterprise
  • MCP Server: Publicly accessible with HTTPS
  • OAuth: Automatically handled via Open DCR 🆕

Step 1: Add Connector in Claude

  1. Open Claude (claude.ai or desktop app)
  2. Go to SettingsConnectors (or Integrations)
  3. Click Add custom connector
  4. Enter your MCP server URL:
    • Full access: https://your-mcp-server.com/mcp
    • Per-project: https://your-mcp-server.com/project/{alias}/mcp
  5. Click Add connector or Connect

Step 2: Authorize (Automatic Flow) 🆕

Thanks to Open DCR and MCP OAuth Compliance, the OAuth flow is now automatic:

  1. Claude discovers OAuth metadata at /.well-known/oauth-authorization-server
  2. Claude automatically registers via Open DCR at /oauth/register
  3. Claude redirects you to the authorization page
  4. Enter your API Key
  5. Done! Claude now has access to your MCP tools

No manual client registration required!

Alternative: Manual Registration (Optional)

If you prefer to pre-register the OAuth client:

curl -X POST https://your-mcp-server.com/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Claude Custom Connector",
    "redirect_uris": ["https://claude.ai/oauth/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "scope": "read write"
  }'

Note: Authorization header is optional for Claude's redirect URIs (Open DCR).

Security Considerations

⚠️ Important Security Notes:

  • Only add trusted connectors - Remote MCP servers have access to your Claude conversations
  • Use minimal scopes - Register clients with only required permissions (read for safe exploration)
  • Disable write-actions for Research feature if using with sensitive data
  • Monitor usage via audit logs: get_audit_log tool

For safe exploration (read-only):

curl -X POST https://your-mcp-server.com/oauth/register \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Claude (Read-Only)",
    "redirect_uris": ["https://claude.ai/api/mcp/auth_callback", "https://claude.com/api/mcp/auth_callback"],
    "scope": "read"
  }'

For full access (trusted environments):

curl -X POST https://your-mcp-server.com/oauth/register \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Claude (Full Access)",
    "redirect_uris": ["https://claude.ai/api/mcp/auth_callback", "https://claude.com/api/mcp/auth_callback"],
    "scope": "read write admin"
  }'

Using Per-Project Endpoints

For better security isolation, connect Claude to project-specific endpoints:

https://your-mcp-server.com/project/{alias}/mcp

This restricts Claude to tools for that specific project only.

Troubleshooting Claude Connectors

"Configure" button instead of "Connect"

This was fixed in Phase K.1. If you still see this:

  • Ensure server is updated to v2.10.0+
  • Check that OAuthRequiredMiddleware is active
  • Verify /.well-known/oauth-protected-resource returns 200

"Connection failed" or "Error connecting to server"

  1. Check server accessibility: curl https://your-server.com/health
  2. Check OAuth metadata: curl https://your-server.com/.well-known/oauth-authorization-server
  3. Check path-specific metadata: curl https://your-server.com/.well-known/oauth-protected-resource/mcp
  4. Check logs for errors:
    docker logs your-container | grep -i oauth
    

"Unauthorized" during authorization

  • Enter a valid API Key on the authorization page
  • Check that your API Key has the required scope

DCR Rate Limited

If you see rate limit errors:

  • Wait 1 minute (per-minute limit: 10)
  • Or wait 1 hour (per-hour limit: 30)
  • Configure limits: DCR_RATE_LIMIT_PER_MINUTE and DCR_RATE_LIMIT_PER_HOUR

Tools not appearing

  • Check endpoint path (should include /mcp)
  • Verify OAuth scopes match tool requirements
  • Review server logs for errors

Testing

Manual Testing

1. Test Authorization Endpoint

# Generate PKCE challenge
python -c "
import secrets, hashlib, base64

verifier = secrets.token_urlsafe(64)[:64]
challenge = base64.urlsafe_b64encode(
    hashlib.sha256(verifier.encode()).digest()
).decode().rstrip('=')

print(f'Verifier: {verifier}')
print(f'Challenge: {challenge}')
"

# Call authorization endpoint
curl -X GET "http://localhost:8000/oauth/authorize?\
client_id=cmp_client_xxx&\
redirect_uri=http://localhost:3000/callback&\
response_type=code&\
code_challenge=YOUR_CHALLENGE&\
code_challenge_method=S256&\
scope=read+write"

2. Test Token Endpoint

# Exchange authorization code for tokens
curl -X POST http://localhost:8000/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&\
client_id=cmp_client_xxx&\
client_secret=YOUR_SECRET&\
code=auth_abc123&\
redirect_uri=http://localhost:3000/callback&\
code_verifier=YOUR_VERIFIER"

3. Test Access Token

# Use access token to call MCP tools
curl -X POST http://localhost:8000/tools/wordpress_post_list \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"site": "site1", "status": "publish"}'

Automated Testing

# Run integration tests
pytest tests/test_oauth_integration.py -v

# Run all OAuth tests
pytest tests/test_oauth*.py -v

Security Best Practices

1. JWT Secret Key Management

DO:

  • Use cryptographically secure random keys (64+ characters)
  • Store in environment variables, never in code
  • Rotate keys periodically
  • Use different keys for dev/staging/production

DON'T:

  • Use weak or predictable secrets
  • Commit secrets to version control
  • Share secrets between environments

2. Client Secrets

DO:

  • Save client secrets immediately after creation
  • Store securely (password manager, secrets vault)
  • Rotate if compromised

DON'T:

  • Log client secrets
  • Expose in error messages
  • Transmit over insecure channels

3. PKCE

DO:

  • Always use S256 method
  • Generate new verifier for each flow
  • Validate code_challenge correctly

DON'T:

  • Reuse code verifiers
  • Use plain method (OAuth 2.1 disallows it)

4. Token Handling

DO:

  • Use short-lived access tokens (1 hour)
  • Implement refresh token rotation
  • Detect and prevent token reuse
  • Validate JWT signatures

DON'T:

  • Store tokens in localStorage (XSS risk)
  • Log tokens in production
  • Ignore token expiration

5. Scope Management

DO:

  • Grant minimum necessary scopes
  • Validate scopes on every request
  • Document scope requirements

DON'T:

  • Grant "admin" scope by default
  • Allow scope escalation

Troubleshooting

ChatGPT OAuth (Manual) Issues

1. "Something went wrong with setting up the connection"

Common Causes:

  • Wrong redirect URI
  • OAUTH_AUTH_MODE is set to required (should be optional for ChatGPT)
  • Client ID/Secret mismatch

Solution:

# 1. Check redirect URI in oauth_clients.json
cat /app/data/oauth_clients.json | grep -A 3 redirect_uris
# Should contain: "https://chatgpt.com/connector_platform_oauth_redirect"

# 2. Verify OAUTH_AUTH_MODE
echo $OAUTH_AUTH_MODE
# Should be: optional

# 3. Check logs for specific error
tail -f logs/audit.log | grep -i oauth

2. "API Key is required" error during authorization

Cause: OAUTH_AUTH_MODE=required but ChatGPT cannot pass API Key

Solution:

# In .env file, change to:
OAUTH_AUTH_MODE=optional

# Then restart/redeploy

3. "Invalid redirect_uri" error

Cause: Redirect URI mismatch between client registration and ChatGPT request

Solution:

# Register new client with correct URI
curl -X POST https://your-server.com/oauth/register \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "ChatGPT OAuth",
    "redirect_uris": ["https://chatgpt.com/connector_platform_oauth_redirect"],
    "grant_types": ["authorization_code", "refresh_token"],
    "scope": "read"
  }'

# Update ChatGPT config with new client_id and client_secret

4. ChatGPT connection works but has too much access

Cause: OAuth client registered with broad scopes (e.g., "read write admin")

Solution:

# Revoke old client
curl -X POST https://your-server.com/tools/oauth_revoke_client \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -d '{"client_id": "cmp_client_old"}'

# Register new client with minimal scope
curl -X POST https://your-server.com/oauth/register \
  -H "Authorization: Bearer YOUR_MASTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "ChatGPT (Read-Only)",
    "redirect_uris": ["https://chatgpt.com/connector_platform_oauth_redirect"],
    "scope": "read"
  }'

Common Issues

1. "Invalid client credentials"

Cause: Wrong client_id or client_secret

Solution:

# List registered clients
curl -H "Authorization: Bearer $MASTER_API_KEY" \
  http://localhost:8000/tools/oauth_list_clients

# Verify credentials match

2. "PKCE validation failed"

Cause: Mismatch between code_verifier and code_challenge

Solution:

  • Ensure code_verifier used in token request matches the original
  • Verify code_challenge was generated correctly with S256

3. "Token expired"

Cause: Access token expired (default 1 hour)

Solution:

# Use refresh token to get new access token
curl -X POST http://localhost:8000/oauth/token \
  -d "grant_type=refresh_token&\
client_id=xxx&\
client_secret=yyy&\
refresh_token=rt_zzz"

4. "Insufficient scope"

Cause: Access token doesn't have required scope

Solution:

  • Request correct scopes during authorization
  • Check allowed_scopes for the client
  • Ensure scope is included in JWT payload

5. "Authorization code already used"

Cause: Attempting to reuse authorization code

Solution:

  • Authorization codes are single-use only
  • Start a new authorization flow
  • Check for client-side caching issues

Debug Logging

Enable debug logging:

# .env
LOG_LEVEL=DEBUG

View logs:

docker-compose logs -f mcp-server | grep -i oauth

API Reference

Endpoints

GET /oauth/authorize

Authorization endpoint for OAuth flow.

Query Parameters:

  • client_id (required) - OAuth client ID
  • redirect_uri (required) - Callback URI
  • response_type (required) - Must be "code"
  • code_challenge (required) - PKCE challenge
  • code_challenge_method (required) - Must be "S256"
  • scope (optional) - Requested scopes (space-separated)
  • state (optional) - CSRF protection token

Response:

{
  "redirect_uri": "http://localhost:3000/callback?code=auth_xxx&state=yyy",
  "code": "auth_xxx",
  "expires_in": 300
}

POST /oauth/token

Token endpoint for all OAuth grants.

Request Body (application/x-www-form-urlencoded or JSON):

  • grant_type (required) - "authorization_code" | "refresh_token" | "client_credentials"
  • client_id (required) - OAuth client ID
  • client_secret (required) - Client secret

For authorization_code:

  • code (required) - Authorization code
  • redirect_uri (required) - Same as /authorize
  • code_verifier (required) - PKCE verifier

For refresh_token:

  • refresh_token (required) - Current refresh token

For client_credentials:

  • scope (optional) - Requested scopes

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "rt_xxx",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read write"
}

MCP Tools

oauth_register_client

Register a new OAuth client.

oauth_list_clients

List all registered OAuth clients.

oauth_revoke_client

Revoke (delete) an OAuth client.


Resources


Support

For issues or questions:


Last Updated: 2025-12-05 Version: v2.10.0 (Phase K.1 - OAuth MCP Compliance, Open DCR, RFC 9728)