Fix documentation: rename oauth file and correct test commands
- Rename oauth-patterns.md to oauth-architecture-patterns.md for clarity - Fix session-ownership.md test commands to run from mcp-server directory - Update redisTransport.ts path reference to include mcp-server prefix
This commit is contained in:
parent
5482d905a9
commit
500c263176
2 changed files with 6 additions and 3 deletions
269
docs/oauth-architecture-patterns.md
Normal file
269
docs/oauth-architecture-patterns.md
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
# OAuth Architecture Patterns
|
||||
|
||||
This document describes different OAuth 2.0 architecture patterns for MCP servers, as specified in the [MCP Authorization specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization).
|
||||
|
||||
## 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
|
||||
|
||||
1. **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.
|
||||
|
||||
2. **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:
|
||||
|
||||
```typescript
|
||||
// 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:
|
||||
```bash
|
||||
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:
|
||||
|
||||
1. **Extract auth code**: Move OAuth handlers to separate service
|
||||
2. **Implement introspection**: Add RFC 7662 endpoint
|
||||
3. **Update MCP server**: Replace in-process validation with introspection calls
|
||||
4. **Update configuration**: Point MCP server to auth server URL
|
||||
5. **Migrate upstream IDP**: Move delegation logic to auth server
|
||||
6. **Test thoroughly**: Ensure token flow works correctly
|
||||
|
||||
### From Separate to Commercial Provider
|
||||
|
||||
Replacing the demo auth server with a commercial provider:
|
||||
|
||||
1. **Configure provider**: Set up OAuth app in Auth0/Okta
|
||||
2. **Update metadata URL**: Point to provider's discovery endpoint
|
||||
3. **Configure introspection**: Set up token validation
|
||||
4. **Update redirect URIs**: Configure allowed callbacks
|
||||
5. **Migrate users**: Import existing users if needed
|
||||
6. **Test integration**: Verify full OAuth flow
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use separate servers** for production deployments
|
||||
2. **Implement token caching** to reduce introspection calls
|
||||
3. **Use PKCE** for all OAuth flows (prevents code interception)
|
||||
4. **Validate token audience** to prevent token substitution
|
||||
5. **Implement proper session management** with Redis or similar
|
||||
6. **Use HTTPS** in production for all endpoints
|
||||
7. **Monitor token expiration** and implement refresh logic
|
||||
8. **Log authentication events** for security auditing
|
||||
9. **Consider upstream delegation** for enterprise deployments
|
||||
10. **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
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
|
||||
- [RFC 6749: OAuth 2.0 Framework](https://datatracker.ietf.org/doc/html/rfc6749)
|
||||
- [RFC 7662: Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662)
|
||||
- [RFC 7636: PKCE](https://datatracker.ietf.org/doc/html/rfc7636)
|
||||
- [OAuth 2.0 Security Best Practices](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics)
|
||||
- [OAuth 2.1 Draft](https://oauth.net/2.1/) - Modern security requirements
|
||||
- [The OAuth 2.0 Authorization Framework in Practice](https://www.oauth.com/)
|
||||
|
|
@ -76,7 +76,7 @@ When an MCP server starts handling a session, it subscribes to the channel. When
|
|||
|
||||
## Implementation
|
||||
|
||||
### Core Functions (src/services/redisTransport.ts)
|
||||
### Core Functions (mcp-server/src/services/redisTransport.ts)
|
||||
|
||||
```typescript
|
||||
export async function setSessionOwner(sessionId: string, userId: string): Promise<void>
|
||||
|
|
@ -135,8 +135,11 @@ redis-cli MONITOR | grep "session:"
|
|||
## Testing
|
||||
|
||||
```bash
|
||||
npm test -- --testNamePattern="User Session Isolation"
|
||||
npm test -- --testNamePattern="session ownership"
|
||||
# Run from mcp-server directory to test session isolation
|
||||
cd mcp-server && npm test -- --testNamePattern="User Session Isolation"
|
||||
|
||||
# Run from mcp-server directory to test session ownership
|
||||
cd mcp-server && npm test -- --testNamePattern="Session Ownership"
|
||||
```
|
||||
|
||||
## References
|
||||
|
|
|
|||
Loading…
Reference in a new issue