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>
1026 lines
29 KiB
Markdown
1026 lines
29 KiB
Markdown
# OAuth 2.1 Authentication Guide
|
|
|
|
Complete guide for using OAuth 2.1 authentication with MCP Hub.
|
|
|
|
---
|
|
|
|
## 📋 Table of Contents
|
|
|
|
1. [Overview](#overview)
|
|
2. [Quick Start](#quick-start)
|
|
3. [Configuration](#configuration)
|
|
4. [OAuth Flows](#oauth-flows)
|
|
5. [Registering OAuth Clients](#registering-oauth-clients)
|
|
6. [OpenAI GPT Integration](#openai-gpt-integration)
|
|
7. [Claude Custom Connectors](#claude-custom-connectors-remote-mcp)
|
|
8. [Testing](#testing)
|
|
9. [Security Best Practices](#security-best-practices)
|
|
10. [Troubleshooting](#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
|
|
|
|
```bash
|
|
# 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`:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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**:
|
|
```json
|
|
{
|
|
"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**:
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```python
|
|
# 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.
|
|
|
|
```bash
|
|
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):
|
|
```json
|
|
{
|
|
"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):
|
|
```json
|
|
{
|
|
"error": "unauthorized",
|
|
"error_description": "Master API Key required for OAuth client registration. Add 'Authorization: Bearer YOUR_MASTER_API_KEY' header."
|
|
}
|
|
```
|
|
|
|
### List Registered Clients
|
|
|
|
```python
|
|
result = await oauth_list_clients()
|
|
|
|
for client in result['clients']:
|
|
print(f"{client['client_name']}: {client['client_id']}")
|
|
```
|
|
|
|
### Revoke a Client
|
|
|
|
```python
|
|
result = await oauth_revoke_client(
|
|
client_id="cmp_client_abc123"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## OpenAI GPT Integration
|
|
|
|
### Step 1: Register OAuth Client
|
|
|
|
Use the MCP tool to register:
|
|
|
|
```python
|
|
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 **Settings** → **Connectors** (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:
|
|
|
|
```bash
|
|
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
|
|
|
|
### Recommended Configuration
|
|
|
|
For **safe exploration** (read-only):
|
|
```bash
|
|
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):
|
|
```bash
|
|
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:
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
# 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**:
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# .env
|
|
LOG_LEVEL=DEBUG
|
|
```
|
|
|
|
View logs:
|
|
```bash
|
|
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**:
|
|
```json
|
|
{
|
|
"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**:
|
|
```json
|
|
{
|
|
"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
|
|
|
|
- [OAuth 2.1 Specification](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10)
|
|
- [RFC 7636 - PKCE](https://datatracker.ietf.org/doc/html/rfc7636)
|
|
- [JWT Best Practices](https://datatracker.ietf.org/doc/html/rfc8725)
|
|
- [OpenAI Actions Documentation](https://platform.openai.com/docs/actions)
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
- GitHub Issues: https://github.com/airano-ir/mcphub/issues
|
|
- Documentation: `/docs`
|
|
|
|
---
|
|
|
|
**Last Updated**: 2025-12-05
|
|
**Version**: v2.10.0 (Phase K.1 - OAuth MCP Compliance, Open DCR, RFC 9728)
|