Simplified the repository to have a single OAuth pattern with separate auth and MCP servers at the top level. Preserved embedded OAuth documentation as an alternative pattern.
12 KiB
OAuth Architecture Patterns
This document describes different OAuth 2.0 architecture patterns for MCP servers, as specified in the MCP Authorization specification.
Overview
Per the MCP specification, "the authorization server may be hosted with the resource server or as a separate entity." This leads to two primary patterns for implementing OAuth in MCP servers.
Pattern 1: Separate Authorization Server (Implemented)
The current implementation uses this production-ready pattern with separate authorization and resource servers.
Architecture
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ │ OAuth │ │ Token │ │
│ MCP Client │────────>│ Auth Server │<────────│ MCP Server │
│ │ │ (port 3001) │validate │ (port 3232) │
│ │<────────│ │ │ │
│ │ token │ Issues tokens │ │ Serves MCP │
│ │ │ │ │ resources │
│ │────────────────────────────────────> │ │
│ │ MCP requests with token │ │
└─────────────┘ └─────────────────┘
Components
-
Authorization Server (port 3001)
- Handles OAuth 2.0 authorization flow
- Issues and manages tokens
- Provides token introspection endpoint (RFC 7662)
- Can be replaced with Auth0, Okta, Google OAuth, etc.
-
MCP Resource Server (port 3232)
- Serves MCP protocol resources
- Validates tokens via introspection
- Contains no OAuth authorization code
- Focuses purely on MCP functionality
Benefits
- Standards Compliance: Follows OAuth 2.0 best practices
- Flexibility: Easy to swap auth providers
- Scalability: Servers scale independently
- Security: Clear security boundaries
- Maintainability: Separation of concerns
Real-World Use Cases
- SaaS Applications: Using Auth0 or Okta for authentication
- Enterprise: Integrating with corporate SSO (SAML, LDAP)
- Social Login: Google, GitHub, Facebook authentication
- Cloud Native: AWS Cognito, Azure AD integration
Implementation Details
The MCP server validates tokens by calling the auth server's /introspect endpoint:
// In MCP server
const response = await fetch(`${AUTH_SERVER_URL}/introspect`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `token=${accessToken}`
});
const introspection = await response.json();
if (introspection.active) {
// Token is valid, extract user info
const userId = introspection.sub;
}
Pattern 2: Embedded Authorization Server (Alternative)
An alternative pattern where the OAuth server is embedded within the MCP server. This demonstrates a self-hosted OAuth 2.1 authorization server running in the same process as the MCP server.
Architecture
┌─────────────┐ ┌─────────────────────────┐
│ │ OAuth │ │
│ MCP Client │────────>│ MCP Server │
│ │ │ ┌──────────────────┐ │
│ │<────────│ │ OAuth Server │ │
│ │ token │ └──────────────────┘ │
│ │ │ ┌──────────────────┐ │
│ │────────>│ │ MCP Resources │ │
│ │ MCP │ └──────────────────┘ │
└─────────────┘ └─────────────────────────┘
Upstream Delegation Pattern
Embedded OAuth often delegates user authentication to an upstream identity provider while maintaining control over token issuance:
MCP Client MCP+OAuth Server Upstream IDP
│ │ (Corporate SSO)
│──1. /authorize───────────────>│ │
│<───(show auth page)───────────│ │
│ │ │
│──2. Click "Continue"──────────>│ │
│<──redirect to upstream────────│ │
│ │ │
│──3. /upstream/authorize────────────────────────────────────────>│
│<──(authenticate user)──────────────────────────────────────────│
│──4. Provide credentials────────────────────────────────────────>│
│<──redirect with userId─────────────────────────────────────────│
│ │ │
│──5. /callback with userId───────>│ │
│ │──(validate userId) │
│ │──(issue MCP tokens) │
│<──redirect with auth code──────│ │
│ │ │
│──6. /token (exchange code)─────>│ │
│<──MCP access token─────────────│ │
Characteristics
- Single Server: OAuth and MCP in one process
- Port: Typically runs on single port (e.g., 3232)
- Token Validation: In-process, direct database access
- Deployment: Simpler, fewer moving parts
- Upstream Delegation: Can delegate authentication to corporate SSO while controlling token issuance
Benefits
- Simplicity: Single server to deploy and manage
- Performance: No network hop for token validation
- Self-Contained: All functionality in one codebase
- Control: Full control over token issuance while leveraging existing identity infrastructure
Drawbacks
- Coupling: Auth and MCP logic intertwined
- Scalability: Can't scale auth independently
- Flexibility: Harder to switch auth providers
- Updates: Auth changes require MCP server updates
Use Cases
- Enterprise Deployments: Organizations needing control over OAuth token issuance while leveraging existing corporate identity infrastructure (LDAP, Active Directory, SAML)
- Proof of Concepts: Quick prototypes and demonstrations
- Small Deployments: Internal tools with simple auth needs
- Isolated Systems: Air-gapped environments without external connectivity
- Custom Auth Requirements: Specialized authentication needs not met by standard providers
Typical Configuration
Environment variables for embedded pattern:
BASE_URI=http://localhost:3232 # Single server URL
PORT=3232 # Single port for OAuth + MCP
REDIS_URL=redis://localhost:6379 # Session storage
UPSTREAM_IDP_URL=https://corp.sso # Optional: upstream identity provider
Code Organization
Typical structure for embedded OAuth implementation:
src/
├── index.ts # Main entry point (MCP + OAuth + routing)
├── auth/
│ ├── provider.ts # OAuth server implementation
│ └── auth-core.ts # Token generation, PKCE utilities
├── services/
│ ├── mcp.ts # MCP protocol implementation
│ ├── auth.ts # Auth service integration
│ ├── redis-auth.ts # Redis auth operations
│ └── redisTransport.ts # Redis-backed transport
├── handlers/
│ ├── oauth.ts # OAuth endpoints (/authorize, /token)
│ ├── mcp-shttp.ts # Streamable HTTP handler
│ ├── mcp-sse.ts # SSE handler
│ └── upstream-idp.ts # Upstream IDP integration
└── utils/
└── logger.ts # Structured logging
Comparison
| Aspect | Separate Servers | Embedded Server |
|---|---|---|
| Architecture | 2+ servers | 1 server |
| OAuth endpoints | On auth server | On MCP server |
| Token validation | Remote (introspection) | In-process |
| Deployment | More complex | Simpler |
| Scalability | Independent scaling | Coupled scaling |
| Provider integration | Easy (standard APIs) | Difficult |
| Code organization | Clear separation | Mixed concerns |
| Network hops | Additional for validation | None for validation |
| Upstream IDP support | Via auth server | Direct integration |
| Production readiness | ✅ Recommended | ⚠️ Limited use cases |
Migration Path
From Embedded to Separate
If starting with embedded OAuth, migration to separate servers involves:
- Extract auth code: Move OAuth handlers to separate service
- Implement introspection: Add RFC 7662 endpoint
- Update MCP server: Replace in-process validation with introspection calls
- Update configuration: Point MCP server to auth server URL
- Migrate upstream IDP: Move delegation logic to auth server
- Test thoroughly: Ensure token flow works correctly
From Separate to Commercial Provider
Replacing the demo auth server with a commercial provider:
- Configure provider: Set up OAuth app in Auth0/Okta
- Update metadata URL: Point to provider's discovery endpoint
- Configure introspection: Set up token validation
- Update redirect URIs: Configure allowed callbacks
- Migrate users: Import existing users if needed
- Test integration: Verify full OAuth flow
Best Practices
- Use separate servers for production deployments
- Implement token caching to reduce introspection calls
- Use PKCE for all OAuth flows (prevents code interception)
- Validate token audience to prevent token substitution
- Implement proper session management with Redis or similar
- Use HTTPS in production for all endpoints
- Monitor token expiration and implement refresh logic
- Log authentication events for security auditing
- Consider upstream delegation for enterprise deployments
- Document auth flow for your specific implementation
Testing Considerations
When implementing either pattern, ensure comprehensive testing:
- Unit tests: Test OAuth flows, token validation, session management
- Integration tests: Test auth server and MCP server interaction
- E2E tests: Test complete user flows from authorization to resource access
- Security tests: Test PKCE validation, token expiration, session isolation
- Performance tests: Test token validation caching, concurrent sessions