9.1 KiB
MCP Authentication Patterns - Comparison Matrix
This document compares all 4 authentication patterns supported by Cloudflare MCP servers.
Quick Reference Table
| Pattern | Security | Complexity | Use Case | Client Setup |
|---|---|---|---|---|
| No Auth | ⚠️ None | ⭐ Simple | Internal tools, dev | Just URL |
| Token Validation | ✅ Good | ⭐⭐ Medium | Custom auth, API keys | Bearer token |
| OAuth Proxy | ✅✅ Excellent | ⭐⭐⭐ Medium | GitHub, Google, Azure | OAuth flow |
| Full OAuth Provider | ✅✅✅ Maximum | ⭐⭐⭐⭐⭐ Complex | Custom identity | Full OAuth 2.1 |
Pattern 1: No Authentication
When to Use
- Internal tools (private network only)
- Development and testing
- Public APIs (intentionally open)
Security
⚠️ WARNING: Anyone with the URL can access your MCP server
Implementation
export class MyMCP extends McpAgent<Env> {
// No authentication required
}
export default {
fetch(request, env, ctx) {
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
}
};
Client Configuration
{
"mcpServers": {
"my-mcp": {
"url": "https://my-mcp.workers.dev/sse"
}
}
}
Pros
✅ Simplest to implement ✅ No OAuth flow complexity ✅ Fast to test
Cons
❌ No security ❌ Anyone can use your server ❌ Can't identify users
Pattern 2: Token Validation (JWT)
When to Use
- Pre-authenticated clients
- Custom authentication systems
- API key-based access
- Service-to-service communication
Security
✅ GOOD: Secure if tokens properly managed
Implementation
import { JWTVerifier } from "agents/mcp";
const verifier = new JWTVerifier({
secret: env.JWT_SECRET,
issuer: "your-auth-server",
audience: "your-mcp-server"
});
export default {
async fetch(request, env, ctx) {
// Verify token before serving MCP requests
const token = request.headers.get("Authorization")?.replace("Bearer ", "");
if (!token) {
return new Response("Unauthorized", { status: 401 });
}
try {
const payload = await verifier.verify(token);
// Token valid, serve MCP
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
} catch (error) {
return new Response("Invalid token", { status: 403 });
}
}
};
Client Configuration
{
"mcpServers": {
"my-mcp": {
"url": "https://my-mcp.workers.dev/sse",
"headers": {
"Authorization": "Bearer YOUR_JWT_TOKEN_HERE"
}
}
}
}
Pros
✅ Simple integration with existing auth ✅ No OAuth flow needed ✅ Works with any JWT issuer
Cons
❌ Token management (refresh, expiry) ❌ Manual token distribution ❌ Client must handle token lifecycle
Pattern 3: OAuth Proxy (workers-oauth-provider)
When to Use
- Integrate with GitHub, Google, Azure, etc.
- User-scoped tools (read/write GitHub repos)
- Need user identity in tools
- Production applications
Security
✅✅ EXCELLENT: Industry-standard OAuth 2.1
Implementation
import { OAuthProvider, GitHubHandler } from "@cloudflare/workers-oauth-provider";
export default new OAuthProvider({
authorizeEndpoint: "/authorize",
tokenEndpoint: "/token",
clientRegistrationEndpoint: "/register",
defaultHandler: new GitHubHandler({
clientId: (env) => env.GITHUB_CLIENT_ID,
clientSecret: (env) => env.GITHUB_CLIENT_SECRET,
scopes: ["repo", "user:email"],
context: async (accessToken) => {
const octokit = new Octokit({ auth: accessToken });
const { data: user } = await octokit.rest.users.getAuthenticated();
return {
login: user.login,
email: user.email,
accessToken
};
}
}),
kv: (env) => env.OAUTH_KV,
apiHandlers: {
"/sse": MyMCP.serveSSE("/sse"),
"/mcp": MyMCP.serve("/mcp")
},
allowConsentScreen: true,
allowDynamicClientRegistration: true
});
Client Configuration
{
"mcpServers": {
"my-mcp": {
"url": "https://my-mcp.workers.dev/sse",
"auth": {
"type": "oauth",
"authorizationUrl": "https://my-mcp.workers.dev/authorize",
"tokenUrl": "https://my-mcp.workers.dev/token"
}
}
}
}
Required Bindings
{
"kv_namespaces": [
{ "binding": "OAUTH_KV", "id": "YOUR_KV_ID" }
],
"vars": {
"GITHUB_CLIENT_ID": "optional-preconfig",
"GITHUB_CLIENT_SECRET": "optional-preconfig"
}
}
Pros
✅ Standard OAuth 2.1 flow
✅ User identity in tools (this.props.login)
✅ Automatic token management
✅ Works with multiple providers
✅ Dynamic Client Registration (no pre-config needed)
✅ Consent screen for permissions
Cons
❌ Requires KV namespace ❌ More complex than token validation ❌ OAuth flow adds latency on first connect
Pattern 4: Full OAuth Provider
When to Use
- You ARE the identity provider
- Custom consent screens
- Full control over auth flow
- Enterprise B2B applications
Security
✅✅✅ MAXIMUM: Complete control over security
Implementation
Complex - requires full OAuth 2.1 server implementation.
See Cloudflare's remote-mcp-authkit template for example.
Client Configuration
Same as Pattern 3
Pros
✅ Full control over authentication ✅ Custom user management ✅ Custom consent screens ✅ Fine-grained permissions ✅ Works with any OAuth client
Cons
❌ Very complex to implement ❌ Must handle OAuth 2.1 spec correctly ❌ Token management, refresh, expiry ❌ User database required ❌ Audit logs recommended
Supported OAuth Providers
GitHub
Handler: GitHubHandler
Scopes: repo, user:email, read:org, write:org
Example: templates/mcp-oauth-proxy.ts
Handler: GoogleHandler
Scopes: openid, email, profile, https://www.googleapis.com/auth/drive.readonly
Setup: See references/oauth-providers.md
Azure AD
Handler: AzureADHandler
Scopes: openid, email, User.Read
Setup: See references/oauth-providers.md
Custom Provider
Handler: GenericOAuthHandler
Use case: Any OAuth 2.1 compliant provider
Migration Path
From No Auth → Token Validation
- Generate JWT signing key
- Add JWTVerifier middleware
- Issue tokens to clients
- Update client config with Authorization header
From Token Validation → OAuth Proxy
- Choose OAuth provider (GitHub, Google, etc.)
- Add KV namespace binding
- Replace fetch handler with OAuthProvider
- Update client config with OAuth URLs
- Remove Authorization headers
From OAuth Proxy → Full OAuth Provider
- Implement OAuth 2.1 server logic
- Add user database (D1, KV, external)
- Implement consent screen UI
- Implement token refresh logic
- Add audit logging
Security Best Practices
All Patterns
✅ Use HTTPS in production (automatic on Cloudflare) ✅ Validate all inputs (Zod schemas) ✅ Log authentication attempts ✅ Rate limit authentication endpoints
Token Validation
✅ Use strong secrets (256-bit minimum) ✅ Short token expiry (15-60 minutes) ✅ Implement token refresh ✅ Rotate secrets regularly
OAuth Patterns
✅ Always use allowConsentScreen: true in production
✅ Request minimal scopes needed
✅ Validate redirect URIs
✅ Use PKCE for authorization code flow
✅ Store tokens securely (KV, encrypted)
Full OAuth Provider
✅ Implement OAuth 2.1 spec correctly ✅ Use authorization code flow (not implicit) ✅ Validate all OAuth parameters ✅ Implement token introspection ✅ Add audit logging for all auth events
Common Mistakes
❌ Disabling Consent Screen
allowConsentScreen: false // ❌ NEVER in production
Users won't see what permissions they're granting!
❌ Storing Secrets in Code
const secret = "my-secret-key"; // ❌ NEVER commit secrets
Use environment variables or secrets management.
❌ Overly Broad Scopes
scopes: ["repo", "delete_repo", "admin:org"] // ❌ Too powerful
Request minimal scopes needed.
❌ No Token Validation
// ❌ Trusting token without verification
const token = request.headers.get("Authorization");
// Use token without verifying...
Always validate tokens before use.
Testing Authentication
Test No Auth
curl https://my-mcp.workers.dev/sse
# Should connect immediately
Test Token Validation
# Without token (should fail)
curl https://my-mcp.workers.dev/sse
# With token (should succeed)
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://my-mcp.workers.dev/sse
Test OAuth Flow
# Use MCP Inspector
npx @modelcontextprotocol/inspector@latest
# Or use Claude Desktop (will trigger OAuth flow)
Further Reading
- OAuth 2.1 Spec: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1
- workers-oauth-provider: https://github.com/cloudflare/workers-oauth-provider
- Cloudflare Auth Docs: https://developers.cloudflare.com/agents/model-context-protocol/authorization/
- MCP Auth Spec: https://spec.modelcontextprotocol.io/specification/draft/basic/authorization/