Remove embedded OAuth implementation and flatten directory structure
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.
This commit is contained in:
parent
c637d39f8d
commit
c03a9b4e77
4 changed files with 1556 additions and 15846 deletions
|
|
@ -1,10 +1,10 @@
|
|||
# Endpoint Reference
|
||||
|
||||
Complete listing of all endpoints provided by each server configuration.
|
||||
Complete listing of all endpoints provided by each server in the architecture.
|
||||
|
||||
## Embedded OAuth Server (Port 3232)
|
||||
## Auth Server (Port 3001)
|
||||
|
||||
Single server hosting OAuth authorization, mock identity provider, and MCP resources.
|
||||
Standalone OAuth 2.0 authorization server that handles authentication and token management.
|
||||
|
||||
### OAuth Authorization Endpoints
|
||||
Provided by `mcpAuthRouter` from MCP SDK:
|
||||
|
|
@ -15,62 +15,21 @@ Provided by `mcpAuthRouter` from MCP SDK:
|
|||
- `POST /token` - Token exchange (authorization code → tokens) and token refresh
|
||||
- `POST /revoke` - Token revocation
|
||||
|
||||
### Token Introspection
|
||||
Custom implementation for resource server token validation:
|
||||
|
||||
- `POST /introspect` - Token introspection ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662))
|
||||
- Called by MCP server to validate tokens
|
||||
- Returns token status, scopes, expiry, user info
|
||||
- Protected endpoint (not public)
|
||||
|
||||
### Mock Upstream Identity Provider Endpoints
|
||||
Local simulation of upstream IDP (would be external in production):
|
||||
|
||||
- `GET /mock-upstream-idp/authorize` - Mock user authentication page
|
||||
- `GET /mock-upstream-idp/callback` - IDP callback handler (returns userId)
|
||||
|
||||
**Note**: In production, the OAuth server would redirect to external URLs like `https://accounts.google.com` or `https://login.okta.com` instead of these local endpoints.
|
||||
|
||||
### MCP Resource Endpoints
|
||||
|
||||
#### Streamable HTTP Transport (Recommended)
|
||||
- `GET /mcp` - Establish SSE stream for session
|
||||
- `POST /mcp` - Initialize session or send messages
|
||||
- `DELETE /mcp` - Terminate session
|
||||
|
||||
#### SSE Transport (Legacy)
|
||||
- `GET /sse` - Establish SSE connection
|
||||
- `POST /message` - Send messages to session
|
||||
|
||||
All MCP endpoints require `Authorization: Bearer <token>` header.
|
||||
|
||||
### Static Assets
|
||||
- `GET /` - Splash page (HTML)
|
||||
- `GET /mcp-logo.png` - MCP logo
|
||||
- `GET /styles.css` - Stylesheet
|
||||
|
||||
---
|
||||
|
||||
## External OAuth - Auth Server (Port 3001)
|
||||
|
||||
Standalone OAuth authorization server (represents Auth0, Okta, etc. in production).
|
||||
|
||||
### OAuth Authorization Endpoints
|
||||
Provided by `mcpAuthRouter` from MCP SDK:
|
||||
|
||||
- `GET /.well-known/oauth-authorization-server` - OAuth metadata discovery
|
||||
- `POST /register` - Dynamic client registration
|
||||
- `GET /authorize` - Authorization request
|
||||
- `POST /token` - Token exchange and refresh
|
||||
- `POST /revoke` - Token revocation
|
||||
|
||||
### Token Introspection
|
||||
Custom implementation for resource server token validation:
|
||||
|
||||
- `POST /introspect` - Token introspection (RFC 7662)
|
||||
- Called by MCP server to validate tokens
|
||||
- Returns token status, scopes, expiry, user info
|
||||
- Protected endpoint (not public)
|
||||
|
||||
### Mock Upstream Identity Provider Endpoints
|
||||
Local simulation of upstream IDP:
|
||||
|
||||
- `GET /mock-upstream-idp/authorize` - Mock user authentication page
|
||||
- `GET /mock-upstream-idp/callback` - IDP callback handler
|
||||
|
||||
**Note**: Commercial OAuth providers (Auth0, Okta) have their own user authentication systems. These endpoints simulate that functionality.
|
||||
**Note**: In production, this would redirect to external providers like Auth0, Okta, Google, GitHub, etc. These endpoints simulate that functionality for demonstration purposes.
|
||||
|
||||
### Utility Endpoints
|
||||
- `GET /health` - Health check (returns server status)
|
||||
|
|
@ -78,9 +37,9 @@ Local simulation of upstream IDP:
|
|||
|
||||
---
|
||||
|
||||
## External OAuth - MCP Server (Port 3232)
|
||||
## MCP Server (Port 3232)
|
||||
|
||||
Pure MCP resource server with no OAuth authorization functionality.
|
||||
MCP resource server that implements the Model Context Protocol with delegated authentication.
|
||||
|
||||
### OAuth Metadata (Read-Only)
|
||||
Provided by `mcpAuthMetadataRouter`:
|
||||
|
|
@ -103,21 +62,21 @@ Provided by `mcpAuthMetadataRouter`:
|
|||
All MCP endpoints require `Authorization: Bearer <token>` header. Tokens are validated by calling the auth server's `/introspect` endpoint.
|
||||
|
||||
### Static Assets
|
||||
- `GET /` - Splash page
|
||||
- `GET /` - Splash page (HTML)
|
||||
- `GET /mcp-logo.png` - MCP logo
|
||||
- `GET /styles.css` - Stylesheet
|
||||
|
||||
---
|
||||
|
||||
## Key Differences
|
||||
## Architecture Overview
|
||||
|
||||
| Endpoint Type | Embedded OAuth | External Auth Server | External MCP Server |
|
||||
|---------------|----------------|---------------------|---------------------|
|
||||
| OAuth authorization (`/authorize`, `/token`) | ✅ Full | ✅ Full | ❌ None |
|
||||
| Token introspection (`/introspect`) | ❌ Not needed | ✅ Yes | ❌ Not needed |
|
||||
| OAuth metadata discovery | ✅ Yes | ✅ Yes | ✅ Read-only redirect |
|
||||
| Mock IDP (`/mock-upstream-idp`) | ✅ Yes | ✅ Yes | ❌ No |
|
||||
| MCP resources (`/mcp`, `/sse`) | ✅ Yes | ❌ No | ✅ Yes |
|
||||
| Endpoint Type | Auth Server | MCP Server |
|
||||
|---------------|-------------|------------|
|
||||
| OAuth authorization (`/authorize`, `/token`) | ✅ Full implementation | ❌ None (delegates to auth server) |
|
||||
| Token introspection (`/introspect`) | ✅ Provides service | ❌ Consumes service |
|
||||
| OAuth metadata discovery | ✅ Authoritative | ✅ Read-only redirect |
|
||||
| Mock IDP (`/mock-upstream-idp`) | ✅ Yes | ❌ No |
|
||||
| MCP resources (`/mcp`, `/sse`) | ❌ No | ✅ Yes |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -146,9 +105,20 @@ All MCP endpoints require `Authorization: Bearer <token>` header. Tokens are val
|
|||
|
||||
---
|
||||
|
||||
## Authentication Flow
|
||||
|
||||
1. **Client discovers auth server**: GET `/.well-known/oauth-authorization-server` from MCP server
|
||||
2. **Client registers**: POST to auth server's `/register` endpoint
|
||||
3. **User authorizes**: Redirected to auth server's `/authorize` endpoint
|
||||
4. **Token exchange**: POST to auth server's `/token` endpoint
|
||||
5. **Access MCP resources**: Use bearer token with MCP server endpoints
|
||||
6. **Token validation**: MCP server validates tokens via auth server's `/introspect` endpoint
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [MCP Transport Specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports)
|
||||
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
|
||||
- [RFC 7662: Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662)
|
||||
- [OAuth 2.0 Endpoints](https://www.oauth.com/oauth2-servers/definitions/)
|
||||
- [OAuth 2.0 Endpoints](https://www.oauth.com/oauth2-servers/definitions/)
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
# OAuth 2.0 + PKCE Flow Analysis
|
||||
|
||||
This document details the complete OAuth 2.0 authorization code flow with PKCE as implemented in this reference server, including how it differs between integrated and separate modes.
|
||||
This document details the complete OAuth 2.0 authorization code flow with PKCE as implemented in this reference server with separate authorization and resource servers.
|
||||
|
||||
## Flow Overview
|
||||
|
||||
The server implements OAuth 2.1 with PKCE (Proof Key for Code Exchange) for secure authorization. Here's how each step maps to data storage, expiry, and mode-specific behavior:
|
||||
The server implements OAuth 2.1 with PKCE (Proof Key for Code Exchange) for secure authorization. The architecture separates the authorization server (port 3001) from the MCP resource server (port 3232).
|
||||
|
||||
## 1. Client Registration
|
||||
|
||||
|
|
@ -19,8 +19,7 @@ Auth Server → App: {"client_id": "XYZ", "client_secret": "ABC", ...}
|
|||
**Storage**:
|
||||
- Redis key: `auth:client:{clientId}`
|
||||
- Expiry: 30 days (long-lived app credentials)
|
||||
|
||||
**Mode differences**: None - identical in both modes.
|
||||
- Server: Auth Server (port 3001)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -44,10 +43,7 @@ Auth Server: Saves pending authorization, shows authorization page
|
|||
- Redis key: `auth:pending:{authCode}`
|
||||
- Expiry: 10 minutes (short-lived temporary state)
|
||||
- Data: redirect_uri, code_challenge, code_challenge_method, client_id, state
|
||||
|
||||
**Mode differences**:
|
||||
- **Embedded OAuth**: OAuth server runs on MCP server (port 3232)
|
||||
- **External OAuth**: OAuth server runs independently (port 3001)
|
||||
- Server: Auth Server (port 3001)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -55,23 +51,19 @@ Auth Server: Saves pending authorization, shows authorization page
|
|||
|
||||
**Purpose**: Authenticate the user and obtain consent.
|
||||
|
||||
**Flow** (identical in both modes):
|
||||
**Flow**:
|
||||
```
|
||||
OAuth Server → User: Shows auth page with "Continue to Authentication" button
|
||||
User → OAuth Server: Clicks button
|
||||
OAuth Server → Upstream IDP: Redirects to /mock-upstream-idp/authorize
|
||||
Auth Server → User: Shows auth page with "Continue to Authentication" button
|
||||
User → Auth Server: Clicks button
|
||||
Auth Server → Upstream IDP: Redirects to /mock-upstream-idp/authorize
|
||||
Upstream IDP → User: Shows user selection UI
|
||||
User → Upstream IDP: Selects/creates user ID
|
||||
Upstream IDP → OAuth Server: Redirects to /mock-upstream-idp/callback?userId=X
|
||||
OAuth Server: Validates user, issues authorization code
|
||||
OAuth Server → App: Redirects to app's redirect_uri with code
|
||||
Upstream IDP → Auth Server: Redirects to /mock-upstream-idp/callback?userId=X
|
||||
Auth Server: Validates user, issues authorization code
|
||||
Auth Server → App: Redirects to app's redirect_uri with code
|
||||
```
|
||||
|
||||
**Mode differences**:
|
||||
- **Embedded OAuth**: OAuth Server runs on MCP server (port 3232)
|
||||
- **External OAuth**: OAuth Server runs independently (port 3001)
|
||||
|
||||
The flow itself is identical. Both modes delegate user authentication to an upstream IDP (simulated by `/mock-upstream-idp/*` endpoints, which represent corporate SSO or social login in production).
|
||||
**Note**: In production, the upstream IDP would be an external provider (Auth0, Okta, Google, GitHub, etc.). The `/mock-upstream-idp/*` endpoints simulate this for demonstration purposes.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -109,10 +101,7 @@ Auth Server → App:
|
|||
- `auth:installation:{accessToken}` - Active MCP installation
|
||||
- `auth:refresh:{refreshToken}` - Refresh token mapping
|
||||
- Expiry: 10 minutes for exchange record, 7 days for installation
|
||||
|
||||
**Mode differences**: None in the exchange itself. The endpoint location differs:
|
||||
- **Embedded OAuth**: `http://localhost:3232/token`
|
||||
- **External OAuth**: `http://localhost:3001/token`
|
||||
- Server: Auth Server (port 3001)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -127,29 +116,28 @@ App → MCP Server: POST /mcp
|
|||
Mcp-Session-Id: <session_id>
|
||||
{MCP request}
|
||||
|
||||
MCP Server: Validates token → Serves MCP resource
|
||||
MCP Server → Auth Server: POST /introspect
|
||||
token=<access_token>
|
||||
|
||||
Auth Server → MCP Server:
|
||||
{
|
||||
"active": true,
|
||||
"userId": "...",
|
||||
"exp": ...,
|
||||
"aud": "http://localhost:3232"
|
||||
}
|
||||
|
||||
MCP Server: Validates audience, serves MCP resource
|
||||
```
|
||||
|
||||
**Token Validation - Embedded OAuth**:
|
||||
```
|
||||
MCP Server (same process as OAuth server):
|
||||
- Reads auth:installation:{accessToken} from Redis
|
||||
- Validates expiry, scopes
|
||||
- Returns user info directly
|
||||
```
|
||||
**Token Validation Process**:
|
||||
1. MCP Server receives request with bearer token
|
||||
2. Calls Auth Server's `/introspect` endpoint (RFC 7662)
|
||||
3. Auth Server validates token and returns metadata
|
||||
4. MCP Server validates audience matches `BASE_URI`
|
||||
5. Request proceeds with authenticated user context
|
||||
|
||||
**Token Validation - External OAuth**:
|
||||
```
|
||||
MCP Server:
|
||||
- Calls POST {AUTH_SERVER_URL}/introspect with token
|
||||
- Auth server validates and returns token info
|
||||
- MCP server validates audience matches BASE_URI
|
||||
- Proceeds with request
|
||||
```
|
||||
|
||||
**Mode differences**: This is the key architectural difference:
|
||||
- **Embedded OAuth**: In-process token validation (fast, direct Redis access)
|
||||
- **External OAuth**: Remote token validation via HTTP introspection (adds network hop, follows RFC 7662)
|
||||
This separation allows the Auth Server to be replaced with commercial providers (Auth0, Okta) while keeping the MCP server unchanged.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -179,8 +167,7 @@ Auth Server → App: {"access_token": "...", ...}
|
|||
- Writes: New `auth:installation:{newAccessToken}`
|
||||
- Writes: New `auth:refresh:{newRefreshToken}`
|
||||
- Expiry: 7 days
|
||||
|
||||
**Mode differences**: None - identical flow, just different endpoint locations.
|
||||
- Server: Auth Server (port 3001)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -205,7 +192,17 @@ PKCE prevents authorization code interception attacks:
|
|||
|
||||
This ensures only the client that initiated the flow can exchange the code, even if the code is intercepted.
|
||||
|
||||
**Mode differences**: None - PKCE works identically in both modes.
|
||||
---
|
||||
|
||||
## Architecture Benefits
|
||||
|
||||
The separate server architecture provides:
|
||||
|
||||
1. **Standards Compliance**: Follows OAuth 2.0 best practices for resource/authorization server separation
|
||||
2. **Flexibility**: Auth server can be replaced with Auth0, Okta, or other providers
|
||||
3. **Scalability**: Auth and MCP servers can scale independently based on load
|
||||
4. **Security**: Token validation via introspection endpoint (RFC 7662)
|
||||
5. **Maintainability**: Clear separation of authentication and business logic
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -214,4 +211,4 @@ This ensures only the client that initiated the flow can exchange the code, even
|
|||
- [RFC 6749: OAuth 2.0 Framework](https://datatracker.ietf.org/doc/html/rfc6749)
|
||||
- [RFC 7636: PKCE](https://datatracker.ietf.org/doc/html/rfc7636)
|
||||
- [RFC 7662: Token Introspection](https://datatracker.ietf.org/doc/html/rfc7662)
|
||||
- [MCP Authorization Spec](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
|
||||
- [MCP Authorization Spec](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
|
||||
269
docs/oauth-patterns.md
Normal file
269
docs/oauth-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/)
|
||||
|
|
@ -23,11 +23,11 @@ User ID sources:
|
|||
|
||||
When the MCP server validates an access token:
|
||||
|
||||
**Embedded OAuth**: Reads `McpInstallation` from Redis, extracts `installation.userId`
|
||||
1. MCP server calls the auth server's `/introspect` endpoint with the token
|
||||
2. Auth server validates the token and returns user information including the `sub` field (user ID)
|
||||
3. MCP server populates `AuthInfo.extra.userId` with this user identifier
|
||||
|
||||
**External OAuth**: Calls auth server's `/introspect` endpoint, receives `sub` field
|
||||
|
||||
Both result in `AuthInfo.extra.userId` being populated.
|
||||
This separation allows the auth server to be replaced with any OAuth provider that supports token introspection.
|
||||
|
||||
### Session Creation
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue