@prmichaelsen/mcp-auth 4.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -226,7 +226,8 @@ transport: {
|
|
|
226
226
|
port: 3000,
|
|
227
227
|
host: '0.0.0.0',
|
|
228
228
|
basePath: '/mcp',
|
|
229
|
-
cors: true
|
|
229
|
+
cors: true,
|
|
230
|
+
corsOrigin: 'https://your-app.example.com' // REQUIRED when cors: true
|
|
230
231
|
}
|
|
231
232
|
```
|
|
232
233
|
|
|
@@ -235,6 +236,45 @@ transport: {
|
|
|
235
236
|
- `POST /mcp/message` - MCP protocol messages (requires JWT)
|
|
236
237
|
- `GET /mcp/health` - Health check endpoint
|
|
237
238
|
|
|
239
|
+
#### ⚠️ CORS Security
|
|
240
|
+
|
|
241
|
+
When enabling CORS, you **MUST** specify `corsOrigin` with explicit origins:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// ✅ SECURE - Single origin
|
|
245
|
+
transport: {
|
|
246
|
+
type: 'sse',
|
|
247
|
+
cors: true,
|
|
248
|
+
corsOrigin: 'https://app.example.com'
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ✅ SECURE - Multiple origins
|
|
252
|
+
transport: {
|
|
253
|
+
type: 'sse',
|
|
254
|
+
cors: true,
|
|
255
|
+
corsOrigin: ['https://app1.example.com', 'https://app2.example.com']
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ❌ INSECURE - Wildcard blocked in production
|
|
259
|
+
transport: {
|
|
260
|
+
type: 'sse',
|
|
261
|
+
cors: true,
|
|
262
|
+
corsOrigin: '*' // ConfigurationError in production!
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ❌ INSECURE - Missing corsOrigin
|
|
266
|
+
transport: {
|
|
267
|
+
type: 'sse',
|
|
268
|
+
cors: true // ConfigurationError: corsOrigin required!
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Security Requirements:**
|
|
273
|
+
- `corsOrigin` is **REQUIRED** when `cors: true`
|
|
274
|
+
- Wildcard (`*`) is **ONLY** allowed in development (`NODE_ENV !== 'production'`)
|
|
275
|
+
- In production, wildcard throws `ConfigurationError` to prevent CSRF attacks
|
|
276
|
+
- Use specific origins to ensure only your applications can access the MCP server
|
|
277
|
+
|
|
238
278
|
### HTTP (Remote)
|
|
239
279
|
|
|
240
280
|
```typescript
|
package/dist/types.d.ts
CHANGED
|
@@ -82,11 +82,37 @@ export interface TransportConfig {
|
|
|
82
82
|
basePath?: string;
|
|
83
83
|
/**
|
|
84
84
|
* Enable CORS for HTTP/SSE transports
|
|
85
|
+
*
|
|
86
|
+
* ⚠️ SECURITY WARNING: When enabling CORS, you MUST specify corsOrigin.
|
|
87
|
+
* Never use wildcard (*) in production environments.
|
|
88
|
+
*
|
|
85
89
|
* @default false
|
|
86
90
|
*/
|
|
87
91
|
cors?: boolean;
|
|
88
92
|
/**
|
|
89
93
|
* CORS origin configuration
|
|
94
|
+
*
|
|
95
|
+
* Specifies which origins are allowed to access the MCP server.
|
|
96
|
+
*
|
|
97
|
+
* ⚠️ SECURITY REQUIREMENTS:
|
|
98
|
+
* - REQUIRED when cors is enabled
|
|
99
|
+
* - Wildcard (*) is ONLY allowed in development (NODE_ENV !== 'production')
|
|
100
|
+
* - In production, specify explicit origins to prevent CSRF attacks
|
|
101
|
+
*
|
|
102
|
+
* @example Single origin
|
|
103
|
+
* ```typescript
|
|
104
|
+
* corsOrigin: 'https://app.example.com'
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example Multiple origins
|
|
108
|
+
* ```typescript
|
|
109
|
+
* corsOrigin: ['https://app1.example.com', 'https://app2.example.com']
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @example Development only (INSECURE)
|
|
113
|
+
* ```typescript
|
|
114
|
+
* corsOrigin: '*' // Only works when NODE_ENV !== 'production'
|
|
115
|
+
* ```
|
|
90
116
|
*/
|
|
91
117
|
corsOrigin?: string | string[];
|
|
92
118
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IAExD;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEnC;;OAEG;IACH,SAAS,EAAE,aAAa,CAAC;IAEzB;;OAEG;IACH,SAAS,EAAE,IAAI,CAAC;IAEhB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,IAAI,EAAE,aAAa,CAAC;IAEpB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-wrapper.d.ts","sourceRoot":"","sources":["../../src/wrapper/server-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,mBAAmB,EAAiC,MAAM,aAAa,CAAC;AA4BtF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,YAAY,CAAC,CAAiB;gBAE1B,MAAM,EAAE,mBAAmB;IAqBvC;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAwBvB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8C5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2C3B;;;OAGG;YACW,gBAAgB;IA2D9B;;OAEG;YACW,iBAAiB;IAW/B;;OAEG;YACW,uBAAuB;IAgDrC;;OAEG;YACW,mBAAmB;IAuBjC;;OAEG;IACH,OAAO,CAAC,eAAe;IAqCvB;;OAEG;YACW,mBAAmB;IAyBjC;;OAEG;YACW,iBAAiB;
|
|
1
|
+
{"version":3,"file":"server-wrapper.d.ts","sourceRoot":"","sources":["../../src/wrapper/server-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,mBAAmB,EAAiC,MAAM,aAAa,CAAC;AA4BtF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,YAAY,CAAC,CAAiB;gBAE1B,MAAM,EAAE,mBAAmB;IAqBvC;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAwBvB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8C5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2C3B;;;OAGG;YACW,gBAAgB;IA2D9B;;OAEG;YACW,iBAAiB;IAW/B;;OAEG;YACW,uBAAuB;IAgDrC;;OAEG;YACW,mBAAmB;IAuBjC;;OAEG;IACH,OAAO,CAAC,eAAe;IAqCvB;;OAEG;YACW,mBAAmB;IAyBjC;;OAEG;YACW,iBAAiB;IAwI/B;;OAEG;YACW,kBAAkB;IAUhC;;OAEG;IACH,YAAY,IAAI;QACd,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,KAAK,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;YACf,SAAS,EAAE,MAAM,CAAC;YAClB,QAAQ,EAAE,MAAM,CAAC;YACjB,GAAG,EAAE,MAAM,CAAC;YACZ,QAAQ,EAAE,MAAM,CAAC;SAClB,CAAC,CAAC;KACJ;IAgBD;;OAEG;IACH,eAAe,IAAI,OAAO;CAG3B"}
|
|
@@ -326,10 +326,37 @@ class AuthenticatedServerWrapper {
|
|
|
326
326
|
const app = express.default();
|
|
327
327
|
app.use(express.json());
|
|
328
328
|
if (this.config.transport.cors) {
|
|
329
|
+
if (!this.config.transport.corsOrigin) {
|
|
330
|
+
throw new ConfigurationError(
|
|
331
|
+
'CORS origin must be explicitly configured when CORS is enabled. Set transport.corsOrigin to a specific origin (e.g., "https://app.example.com") or an array of allowed origins.'
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
if (this.config.transport.corsOrigin === "*") {
|
|
335
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
336
|
+
if (isProduction) {
|
|
337
|
+
throw new ConfigurationError(
|
|
338
|
+
'CORS wildcard (*) is not allowed in production environments. Specify explicit origins to prevent CSRF attacks. Example: corsOrigin: "https://app.example.com"'
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
this.logger.warn(
|
|
342
|
+
"CORS wildcard (*) detected in development. This is insecure and should never be used in production.",
|
|
343
|
+
{ corsOrigin: this.config.transport.corsOrigin }
|
|
344
|
+
);
|
|
345
|
+
}
|
|
329
346
|
const cors = await import("cors");
|
|
330
347
|
app.use(cors.default({
|
|
331
|
-
origin: this.config.transport.corsOrigin
|
|
348
|
+
origin: this.config.transport.corsOrigin,
|
|
349
|
+
credentials: true,
|
|
350
|
+
methods: ["GET", "POST", "OPTIONS"],
|
|
351
|
+
allowedHeaders: ["Content-Type", "Authorization", "X-Request-ID"],
|
|
352
|
+
exposedHeaders: ["X-Request-ID"],
|
|
353
|
+
maxAge: 86400
|
|
354
|
+
// 24 hours
|
|
332
355
|
}));
|
|
356
|
+
this.logger.info("CORS enabled", {
|
|
357
|
+
origin: this.config.transport.corsOrigin,
|
|
358
|
+
credentials: true
|
|
359
|
+
});
|
|
333
360
|
}
|
|
334
361
|
const basePath = this.config.transport.basePath || "/mcp";
|
|
335
362
|
app.get(basePath, (req, res) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/wrapper/server-wrapper.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Authenticated server wrapper implementation\n *\n * Wraps MCP servers with authentication and multi-tenancy support.\n * Uses ephemeral instances by default for security.\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport type { ServerWrapperConfig, NormalizedServerWrapperConfig } from './config.js';\nimport type { RequestContext } from '../types.js';\nimport { \n AuthenticationError, \n TokenResolutionError,\n ConfigurationError,\n TransportError\n} from '../utils/errors.js';\nimport { createLogger, type Logger } from '../utils/logger.js';\nimport {\n validateRequiredFields,\n validateResourceType,\n validateUserId,\n validateAccessToken,\n validateTransportConfig\n} from '../utils/validation.js';\n\n/**\n * Server instance metadata (for pooled mode)\n */\ninterface ServerInstance {\n server: Server;\n accessToken: string;\n userId: string;\n createdAt: number;\n lastUsed: number;\n}\n\n/**\n * Authenticated server wrapper\n * \n * Wraps an MCP server with authentication, automatically handling:\n * - Request authentication via AuthProvider\n * - Token resolution via ResourceTokenResolver\n * - Per-user server instance creation (ephemeral or pooled)\n * - Transport management (stdio, SSE, HTTP)\n * \n * @example\n * ```typescript\n * const wrapper = new AuthenticatedServerWrapper({\n * serverFactory: (accessToken, userId) => createInstagramServer(accessToken),\n * authProvider: new JWTAuthProvider({ ... }),\n * tokenResolver: new DatabaseTokenResolver({ ... }),\n * resourceType: 'instagram',\n * transport: { type: 'sse', port: 3000 }\n * });\n * \n * await wrapper.start();\n * ```\n */\nexport class AuthenticatedServerWrapper {\n private config: NormalizedServerWrapperConfig;\n private logger: Logger;\n private serverPool: Map<string, ServerInstance>;\n private isRunning: boolean = false;\n private cleanupTimer?: NodeJS.Timeout;\n \n constructor(config: ServerWrapperConfig) {\n // Validate configuration\n this.validateConfig(config);\n \n // Normalize configuration with defaults\n this.config = this.normalizeConfig(config);\n \n // Initialize logger\n this.logger = createLogger(this.config.middleware.logging);\n \n // Initialize server pool (only used in pooled mode)\n this.serverPool = new Map();\n \n this.logger.info('AuthenticatedServerWrapper created', {\n name: this.config.name,\n resourceType: this.config.resourceType,\n transport: this.config.transport.type,\n instanceMode: this.config.instanceMode\n });\n }\n \n /**\n * Validate wrapper configuration\n */\n private validateConfig(config: ServerWrapperConfig): void {\n // Validate required fields manually for better type safety\n if (!config.serverFactory) {\n throw new ConfigurationError('serverFactory is required');\n }\n if (!config.authProvider) {\n throw new ConfigurationError('authProvider is required');\n }\n if (!config.tokenResolver) {\n throw new ConfigurationError('tokenResolver is required');\n }\n if (!config.resourceType) {\n throw new ConfigurationError('resourceType is required');\n }\n if (!config.transport) {\n throw new ConfigurationError('transport is required');\n }\n \n validateResourceType(config.resourceType);\n validateTransportConfig(config.transport);\n }\n \n /**\n * Normalize configuration with defaults\n */\n private normalizeConfig(config: ServerWrapperConfig): NormalizedServerWrapperConfig {\n return {\n serverFactory: config.serverFactory,\n authProvider: config.authProvider,\n tokenResolver: config.tokenResolver,\n resourceType: config.resourceType,\n transport: config.transport,\n name: config.name ?? 'mcp-auth-wrapped-server',\n version: config.version ?? '1.0.0',\n instanceMode: config.instanceMode ?? 'ephemeral',\n middleware: {\n rateLimit: config.middleware?.rateLimit,\n logging: config.middleware?.logging ?? { enabled: true, level: 'info' }\n },\n pooling: {\n maxServersPerUser: config.pooling?.maxServersPerUser ?? 1,\n idleTimeoutMs: config.pooling?.idleTimeoutMs ?? 300000,\n maxTotalServers: config.pooling?.maxTotalServers ?? 100\n },\n requestTimeoutMs: config.requestTimeoutMs ?? 30000,\n enableTracing: config.enableTracing ?? false\n };\n }\n \n /**\n * Start the wrapped server\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new ConfigurationError('Server is already running');\n }\n \n this.logger.info('Starting authenticated server wrapper', {\n name: this.config.name,\n transport: this.config.transport.type\n });\n \n // Initialize auth provider\n if (this.config.authProvider.initialize) {\n await this.config.authProvider.initialize();\n this.logger.debug('Auth provider initialized');\n }\n \n // Initialize token resolver\n if (this.config.tokenResolver.initialize) {\n await this.config.tokenResolver.initialize();\n this.logger.debug('Token resolver initialized');\n }\n \n // Start appropriate transport\n switch (this.config.transport.type) {\n case 'stdio':\n await this.startStdioTransport();\n break;\n case 'sse':\n await this.startSSETransport();\n break;\n case 'http':\n await this.startHTTPTransport();\n break;\n default:\n throw new TransportError(`Unsupported transport type: ${this.config.transport.type}`);\n }\n \n this.isRunning = true;\n \n this.logger.info('Server wrapper started successfully', {\n name: this.config.name,\n transport: this.config.transport.type,\n port: this.config.transport.port\n });\n }\n \n /**\n * Stop the wrapped server\n */\n async stop(): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n \n this.logger.info('Stopping server wrapper');\n \n // Clear cleanup timer\n if (this.cleanupTimer) {\n clearTimeout(this.cleanupTimer);\n this.cleanupTimer = undefined;\n }\n \n // Close all pooled servers\n if (this.config.instanceMode === 'pooled') {\n for (const [userId, instance] of this.serverPool.entries()) {\n try {\n await instance.server.close();\n this.logger.debug('Closed pooled server instance', { userId });\n } catch (error) {\n this.logger.error('Error closing server instance', error as Error, { userId });\n }\n }\n this.serverPool.clear();\n }\n \n // Cleanup auth provider\n if (this.config.authProvider.cleanup) {\n await this.config.authProvider.cleanup();\n this.logger.debug('Auth provider cleaned up');\n }\n \n // Cleanup token resolver\n if (this.config.tokenResolver.cleanup) {\n await this.config.tokenResolver.cleanup();\n this.logger.debug('Token resolver cleaned up');\n }\n \n this.isRunning = false;\n \n this.logger.info('Server wrapper stopped');\n }\n \n /**\n * Handle SSE request with direct Express req/res access\n * This allows us to use StreamableHTTPServerTransport properly\n */\n private async handleSSERequest(req: any, res: any, context: RequestContext): Promise<void> {\n const requestLogger = this.logger.child({ requestId: context.requestId });\n \n try {\n // 1. Authenticate\n requestLogger.debug('Authenticating request');\n const authResult = await this.config.authProvider.authenticate(context);\n \n if (!authResult.authenticated || !authResult.userId) {\n requestLogger.warn('Authentication failed', { error: authResult.error });\n throw new AuthenticationError(authResult.error || 'Authentication failed');\n }\n \n const userId = validateUserId(authResult.userId);\n requestLogger.debug('Authentication successful', { userId });\n \n // 2. Resolve resource token\n const accessToken = await this.config.tokenResolver.resolveToken(\n userId,\n this.config.resourceType\n );\n \n if (!accessToken) {\n requestLogger.warn('Token resolution failed', { userId, resourceType: this.config.resourceType });\n throw new TokenResolutionError(userId, this.config.resourceType);\n }\n \n validateAccessToken(accessToken);\n requestLogger.debug('Token resolved', { userId, resourceType: this.config.resourceType });\n \n // 3. Get server instance\n const server = await this.getServerInstance(userId, accessToken);\n \n // 4. Forward request to server via StreamableHTTPServerTransport\n requestLogger.debug('Forwarding request to MCP server', { userId });\n \n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined // Stateless mode\n });\n \n // Connect server to transport\n await server.connect(transport);\n \n // Forward the request through the transport\n // The transport handles JSON-RPC formatting\n // Tool names are passed through unchanged\n await transport.handleRequest(req, res, req.body);\n \n requestLogger.info('Request handled successfully', {\n userId,\n resourceType: this.config.resourceType\n });\n \n } catch (error) {\n requestLogger.error('SSE request handling failed', error as Error);\n throw error;\n }\n }\n \n /**\n * Get server instance (ephemeral or from pool)\n */\n private async getServerInstance(userId: string, accessToken: string): Promise<Server> {\n if (this.config.instanceMode === 'ephemeral') {\n // Create new server instance for each request (recommended)\n this.logger.debug('Creating ephemeral server instance', { userId });\n return await this.config.serverFactory(accessToken, userId);\n }\n \n // Pooled mode\n return await this.getPooledServerInstance(userId, accessToken);\n }\n \n /**\n * Get or create pooled server instance\n */\n private async getPooledServerInstance(userId: string, accessToken: string): Promise<Server> {\n // Check if we have a cached server instance\n if (this.serverPool.has(userId)) {\n const instance = this.serverPool.get(userId)!;\n \n // Check if token changed (user rotated token)\n if (instance.accessToken !== accessToken) {\n this.logger.info('Token changed, recreating server instance', { userId });\n await instance.server.close();\n this.serverPool.delete(userId);\n } else {\n // Reuse existing instance\n instance.lastUsed = Date.now();\n this.logger.debug('Reusing pooled server instance', { userId });\n return instance.server;\n }\n }\n \n // Check pool size limit\n if (this.serverPool.size >= this.config.pooling.maxTotalServers) {\n this.logger.warn('Server pool limit reached, evicting oldest instance', {\n poolSize: this.serverPool.size,\n maxTotal: this.config.pooling.maxTotalServers\n });\n await this.evictOldestInstance();\n }\n \n // Create new server instance\n this.logger.info('Creating new pooled server instance', { userId });\n const server = await this.config.serverFactory(accessToken, userId);\n \n // Add to pool\n this.serverPool.set(userId, {\n server,\n accessToken,\n userId,\n createdAt: Date.now(),\n lastUsed: Date.now()\n });\n \n // Schedule cleanup if not already scheduled\n if (!this.cleanupTimer) {\n this.scheduleCleanup();\n }\n \n return server;\n }\n \n /**\n * Evict oldest server instance from pool\n */\n private async evictOldestInstance(): Promise<void> {\n let oldestUserId: string | null = null;\n let oldestTime = Infinity;\n \n for (const [userId, instance] of this.serverPool.entries()) {\n if (instance.lastUsed < oldestTime) {\n oldestTime = instance.lastUsed;\n oldestUserId = userId;\n }\n }\n \n if (oldestUserId) {\n const instance = this.serverPool.get(oldestUserId)!;\n await instance.server.close();\n this.serverPool.delete(oldestUserId);\n \n this.logger.debug('Evicted oldest server instance', {\n userId: oldestUserId,\n age: Date.now() - instance.createdAt\n });\n }\n }\n \n /**\n * Schedule cleanup of idle server instances\n */\n private scheduleCleanup(): void {\n const timeout = this.config.pooling.idleTimeoutMs;\n \n this.cleanupTimer = setTimeout(async () => {\n const now = Date.now();\n const toRemove: string[] = [];\n \n for (const [userId, instance] of this.serverPool.entries()) {\n if (now - instance.lastUsed > timeout) {\n toRemove.push(userId);\n }\n }\n \n for (const userId of toRemove) {\n const instance = this.serverPool.get(userId)!;\n try {\n await instance.server.close();\n this.serverPool.delete(userId);\n \n this.logger.debug('Cleaned up idle server instance', {\n userId,\n idleTime: now - instance.lastUsed\n });\n } catch (error) {\n this.logger.error('Error cleaning up server instance', error as Error, { userId });\n }\n }\n \n // Reschedule if pool is not empty\n if (this.serverPool.size > 0) {\n this.scheduleCleanup();\n } else {\n this.cleanupTimer = undefined;\n }\n }, timeout);\n }\n \n /**\n * Start stdio transport (single-user mode)\n */\n private async startStdioTransport(): Promise<void> {\n this.logger.info('Starting stdio transport');\n \n // For stdio, we use environment variable for token\n const envVar = `${this.config.resourceType.toUpperCase()}_ACCESS_TOKEN`;\n const accessToken = process.env[envVar];\n \n if (!accessToken) {\n throw new ConfigurationError(\n `${envVar} environment variable required for stdio mode`\n );\n }\n \n const userId = 'stdio-user';\n \n // Create server instance\n const server = await this.config.serverFactory(accessToken, userId);\n \n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n \n this.logger.info('Stdio transport started', { userId });\n }\n \n /**\n * Start SSE transport (multi-user mode)\n */\n private async startSSETransport(): Promise<void> {\n this.logger.info('Starting SSE transport', {\n port: this.config.transport.port,\n basePath: this.config.transport.basePath\n });\n \n // Import express dynamically (optional dependency)\n // @ts-ignore - Dynamic import of optional dependency\n const express = await import('express');\n const app = express.default();\n \n // Enable JSON parsing\n app.use(express.json());\n \n // Enable CORS if configured\n if (this.config.transport.cors) {\n // @ts-ignore - Dynamic import of optional dependency\n const cors = await import('cors');\n app.use(cors.default({\n origin: this.config.transport.corsOrigin || '*'\n }));\n }\n \n const basePath = this.config.transport.basePath || '/mcp';\n \n // Root endpoint info\n app.get(basePath, (req: any, res: any) => {\n res.json({\n name: this.config.name,\n version: this.config.version,\n resourceType: this.config.resourceType,\n endpoints: {\n message: `POST ${basePath}/message`,\n health: `GET ${basePath}/health`\n },\n documentation: 'https://github.com/prmichaelsen/mcp-auth'\n });\n });\n \n // SSE endpoint for MCP messages\n app.post(`${basePath}/message`, async (req: any, res: any) => {\n try {\n const context: RequestContext = {\n headers: req.headers as Record<string, string>,\n transport: 'sse',\n timestamp: new Date(),\n requestId: req.headers['x-request-id'] as string | undefined\n };\n \n // Handle request and forward to MCP server via transport\n await this.handleSSERequest(req, res, context);\n \n } catch (error) {\n this.logger.error('SSE request failed', error as Error);\n \n if (error instanceof AuthenticationError || error instanceof TokenResolutionError) {\n res.status(error.statusCode).json({\n error: error.message,\n code: error.code\n });\n } else {\n res.status(500).json({\n error: 'Internal server error',\n code: 'INTERNAL_ERROR'\n });\n }\n }\n });\n \n // Health check endpoint\n app.get(`${basePath}/health`, (req: any, res: any) => {\n res.json({\n status: 'healthy',\n name: this.config.name,\n version: this.config.version,\n resourceType: this.config.resourceType,\n instanceMode: this.config.instanceMode,\n poolSize: this.serverPool.size\n });\n });\n \n // Start server\n const port = this.config.transport.port || 3000;\n const host = this.config.transport.host || '0.0.0.0';\n \n await new Promise<void>((resolve) => {\n app.listen(port, host, () => {\n this.logger.info('SSE transport listening', {\n host,\n port,\n basePath,\n url: `http://${host}:${port}${basePath}`\n });\n resolve();\n });\n });\n }\n \n /**\n * Start HTTP transport (multi-user mode)\n */\n private async startHTTPTransport(): Promise<void> {\n this.logger.info('Starting HTTP transport', {\n port: this.config.transport.port\n });\n \n // HTTP transport is similar to SSE but with different endpoint structure\n // For now, delegate to SSE implementation\n await this.startSSETransport();\n }\n \n /**\n * Get server pool statistics\n */\n getPoolStats(): {\n size: number;\n instances: Array<{\n userId: string;\n createdAt: number;\n lastUsed: number;\n age: number;\n idleTime: number;\n }>;\n } {\n const now = Date.now();\n const instances = Array.from(this.serverPool.entries()).map(([userId, instance]) => ({\n userId,\n createdAt: instance.createdAt,\n lastUsed: instance.lastUsed,\n age: now - instance.createdAt,\n idleTime: now - instance.lastUsed\n }));\n \n return {\n size: this.serverPool.size,\n instances\n };\n }\n \n /**\n * Check if server is running\n */\n isServerRunning(): boolean {\n return this.isRunning;\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * Authenticated server wrapper implementation\n *\n * Wraps MCP servers with authentication and multi-tenancy support.\n * Uses ephemeral instances by default for security.\n */\n\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport type { ServerWrapperConfig, NormalizedServerWrapperConfig } from './config.js';\nimport type { RequestContext } from '../types.js';\nimport { \n AuthenticationError, \n TokenResolutionError,\n ConfigurationError,\n TransportError\n} from '../utils/errors.js';\nimport { createLogger, type Logger } from '../utils/logger.js';\nimport {\n validateRequiredFields,\n validateResourceType,\n validateUserId,\n validateAccessToken,\n validateTransportConfig\n} from '../utils/validation.js';\n\n/**\n * Server instance metadata (for pooled mode)\n */\ninterface ServerInstance {\n server: Server;\n accessToken: string;\n userId: string;\n createdAt: number;\n lastUsed: number;\n}\n\n/**\n * Authenticated server wrapper\n * \n * Wraps an MCP server with authentication, automatically handling:\n * - Request authentication via AuthProvider\n * - Token resolution via ResourceTokenResolver\n * - Per-user server instance creation (ephemeral or pooled)\n * - Transport management (stdio, SSE, HTTP)\n * \n * @example\n * ```typescript\n * const wrapper = new AuthenticatedServerWrapper({\n * serverFactory: (accessToken, userId) => createInstagramServer(accessToken),\n * authProvider: new JWTAuthProvider({ ... }),\n * tokenResolver: new DatabaseTokenResolver({ ... }),\n * resourceType: 'instagram',\n * transport: { type: 'sse', port: 3000 }\n * });\n * \n * await wrapper.start();\n * ```\n */\nexport class AuthenticatedServerWrapper {\n private config: NormalizedServerWrapperConfig;\n private logger: Logger;\n private serverPool: Map<string, ServerInstance>;\n private isRunning: boolean = false;\n private cleanupTimer?: NodeJS.Timeout;\n \n constructor(config: ServerWrapperConfig) {\n // Validate configuration\n this.validateConfig(config);\n \n // Normalize configuration with defaults\n this.config = this.normalizeConfig(config);\n \n // Initialize logger\n this.logger = createLogger(this.config.middleware.logging);\n \n // Initialize server pool (only used in pooled mode)\n this.serverPool = new Map();\n \n this.logger.info('AuthenticatedServerWrapper created', {\n name: this.config.name,\n resourceType: this.config.resourceType,\n transport: this.config.transport.type,\n instanceMode: this.config.instanceMode\n });\n }\n \n /**\n * Validate wrapper configuration\n */\n private validateConfig(config: ServerWrapperConfig): void {\n // Validate required fields manually for better type safety\n if (!config.serverFactory) {\n throw new ConfigurationError('serverFactory is required');\n }\n if (!config.authProvider) {\n throw new ConfigurationError('authProvider is required');\n }\n if (!config.tokenResolver) {\n throw new ConfigurationError('tokenResolver is required');\n }\n if (!config.resourceType) {\n throw new ConfigurationError('resourceType is required');\n }\n if (!config.transport) {\n throw new ConfigurationError('transport is required');\n }\n \n validateResourceType(config.resourceType);\n validateTransportConfig(config.transport);\n }\n \n /**\n * Normalize configuration with defaults\n */\n private normalizeConfig(config: ServerWrapperConfig): NormalizedServerWrapperConfig {\n return {\n serverFactory: config.serverFactory,\n authProvider: config.authProvider,\n tokenResolver: config.tokenResolver,\n resourceType: config.resourceType,\n transport: config.transport,\n name: config.name ?? 'mcp-auth-wrapped-server',\n version: config.version ?? '1.0.0',\n instanceMode: config.instanceMode ?? 'ephemeral',\n middleware: {\n rateLimit: config.middleware?.rateLimit,\n logging: config.middleware?.logging ?? { enabled: true, level: 'info' }\n },\n pooling: {\n maxServersPerUser: config.pooling?.maxServersPerUser ?? 1,\n idleTimeoutMs: config.pooling?.idleTimeoutMs ?? 300000,\n maxTotalServers: config.pooling?.maxTotalServers ?? 100\n },\n requestTimeoutMs: config.requestTimeoutMs ?? 30000,\n enableTracing: config.enableTracing ?? false\n };\n }\n \n /**\n * Start the wrapped server\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n throw new ConfigurationError('Server is already running');\n }\n \n this.logger.info('Starting authenticated server wrapper', {\n name: this.config.name,\n transport: this.config.transport.type\n });\n \n // Initialize auth provider\n if (this.config.authProvider.initialize) {\n await this.config.authProvider.initialize();\n this.logger.debug('Auth provider initialized');\n }\n \n // Initialize token resolver\n if (this.config.tokenResolver.initialize) {\n await this.config.tokenResolver.initialize();\n this.logger.debug('Token resolver initialized');\n }\n \n // Start appropriate transport\n switch (this.config.transport.type) {\n case 'stdio':\n await this.startStdioTransport();\n break;\n case 'sse':\n await this.startSSETransport();\n break;\n case 'http':\n await this.startHTTPTransport();\n break;\n default:\n throw new TransportError(`Unsupported transport type: ${this.config.transport.type}`);\n }\n \n this.isRunning = true;\n \n this.logger.info('Server wrapper started successfully', {\n name: this.config.name,\n transport: this.config.transport.type,\n port: this.config.transport.port\n });\n }\n \n /**\n * Stop the wrapped server\n */\n async stop(): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n \n this.logger.info('Stopping server wrapper');\n \n // Clear cleanup timer\n if (this.cleanupTimer) {\n clearTimeout(this.cleanupTimer);\n this.cleanupTimer = undefined;\n }\n \n // Close all pooled servers\n if (this.config.instanceMode === 'pooled') {\n for (const [userId, instance] of this.serverPool.entries()) {\n try {\n await instance.server.close();\n this.logger.debug('Closed pooled server instance', { userId });\n } catch (error) {\n this.logger.error('Error closing server instance', error as Error, { userId });\n }\n }\n this.serverPool.clear();\n }\n \n // Cleanup auth provider\n if (this.config.authProvider.cleanup) {\n await this.config.authProvider.cleanup();\n this.logger.debug('Auth provider cleaned up');\n }\n \n // Cleanup token resolver\n if (this.config.tokenResolver.cleanup) {\n await this.config.tokenResolver.cleanup();\n this.logger.debug('Token resolver cleaned up');\n }\n \n this.isRunning = false;\n \n this.logger.info('Server wrapper stopped');\n }\n \n /**\n * Handle SSE request with direct Express req/res access\n * This allows us to use StreamableHTTPServerTransport properly\n */\n private async handleSSERequest(req: any, res: any, context: RequestContext): Promise<void> {\n const requestLogger = this.logger.child({ requestId: context.requestId });\n \n try {\n // 1. Authenticate\n requestLogger.debug('Authenticating request');\n const authResult = await this.config.authProvider.authenticate(context);\n \n if (!authResult.authenticated || !authResult.userId) {\n requestLogger.warn('Authentication failed', { error: authResult.error });\n throw new AuthenticationError(authResult.error || 'Authentication failed');\n }\n \n const userId = validateUserId(authResult.userId);\n requestLogger.debug('Authentication successful', { userId });\n \n // 2. Resolve resource token\n const accessToken = await this.config.tokenResolver.resolveToken(\n userId,\n this.config.resourceType\n );\n \n if (!accessToken) {\n requestLogger.warn('Token resolution failed', { userId, resourceType: this.config.resourceType });\n throw new TokenResolutionError(userId, this.config.resourceType);\n }\n \n validateAccessToken(accessToken);\n requestLogger.debug('Token resolved', { userId, resourceType: this.config.resourceType });\n \n // 3. Get server instance\n const server = await this.getServerInstance(userId, accessToken);\n \n // 4. Forward request to server via StreamableHTTPServerTransport\n requestLogger.debug('Forwarding request to MCP server', { userId });\n \n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined // Stateless mode\n });\n \n // Connect server to transport\n await server.connect(transport);\n \n // Forward the request through the transport\n // The transport handles JSON-RPC formatting\n // Tool names are passed through unchanged\n await transport.handleRequest(req, res, req.body);\n \n requestLogger.info('Request handled successfully', {\n userId,\n resourceType: this.config.resourceType\n });\n \n } catch (error) {\n requestLogger.error('SSE request handling failed', error as Error);\n throw error;\n }\n }\n \n /**\n * Get server instance (ephemeral or from pool)\n */\n private async getServerInstance(userId: string, accessToken: string): Promise<Server> {\n if (this.config.instanceMode === 'ephemeral') {\n // Create new server instance for each request (recommended)\n this.logger.debug('Creating ephemeral server instance', { userId });\n return await this.config.serverFactory(accessToken, userId);\n }\n \n // Pooled mode\n return await this.getPooledServerInstance(userId, accessToken);\n }\n \n /**\n * Get or create pooled server instance\n */\n private async getPooledServerInstance(userId: string, accessToken: string): Promise<Server> {\n // Check if we have a cached server instance\n if (this.serverPool.has(userId)) {\n const instance = this.serverPool.get(userId)!;\n \n // Check if token changed (user rotated token)\n if (instance.accessToken !== accessToken) {\n this.logger.info('Token changed, recreating server instance', { userId });\n await instance.server.close();\n this.serverPool.delete(userId);\n } else {\n // Reuse existing instance\n instance.lastUsed = Date.now();\n this.logger.debug('Reusing pooled server instance', { userId });\n return instance.server;\n }\n }\n \n // Check pool size limit\n if (this.serverPool.size >= this.config.pooling.maxTotalServers) {\n this.logger.warn('Server pool limit reached, evicting oldest instance', {\n poolSize: this.serverPool.size,\n maxTotal: this.config.pooling.maxTotalServers\n });\n await this.evictOldestInstance();\n }\n \n // Create new server instance\n this.logger.info('Creating new pooled server instance', { userId });\n const server = await this.config.serverFactory(accessToken, userId);\n \n // Add to pool\n this.serverPool.set(userId, {\n server,\n accessToken,\n userId,\n createdAt: Date.now(),\n lastUsed: Date.now()\n });\n \n // Schedule cleanup if not already scheduled\n if (!this.cleanupTimer) {\n this.scheduleCleanup();\n }\n \n return server;\n }\n \n /**\n * Evict oldest server instance from pool\n */\n private async evictOldestInstance(): Promise<void> {\n let oldestUserId: string | null = null;\n let oldestTime = Infinity;\n \n for (const [userId, instance] of this.serverPool.entries()) {\n if (instance.lastUsed < oldestTime) {\n oldestTime = instance.lastUsed;\n oldestUserId = userId;\n }\n }\n \n if (oldestUserId) {\n const instance = this.serverPool.get(oldestUserId)!;\n await instance.server.close();\n this.serverPool.delete(oldestUserId);\n \n this.logger.debug('Evicted oldest server instance', {\n userId: oldestUserId,\n age: Date.now() - instance.createdAt\n });\n }\n }\n \n /**\n * Schedule cleanup of idle server instances\n */\n private scheduleCleanup(): void {\n const timeout = this.config.pooling.idleTimeoutMs;\n \n this.cleanupTimer = setTimeout(async () => {\n const now = Date.now();\n const toRemove: string[] = [];\n \n for (const [userId, instance] of this.serverPool.entries()) {\n if (now - instance.lastUsed > timeout) {\n toRemove.push(userId);\n }\n }\n \n for (const userId of toRemove) {\n const instance = this.serverPool.get(userId)!;\n try {\n await instance.server.close();\n this.serverPool.delete(userId);\n \n this.logger.debug('Cleaned up idle server instance', {\n userId,\n idleTime: now - instance.lastUsed\n });\n } catch (error) {\n this.logger.error('Error cleaning up server instance', error as Error, { userId });\n }\n }\n \n // Reschedule if pool is not empty\n if (this.serverPool.size > 0) {\n this.scheduleCleanup();\n } else {\n this.cleanupTimer = undefined;\n }\n }, timeout);\n }\n \n /**\n * Start stdio transport (single-user mode)\n */\n private async startStdioTransport(): Promise<void> {\n this.logger.info('Starting stdio transport');\n \n // For stdio, we use environment variable for token\n const envVar = `${this.config.resourceType.toUpperCase()}_ACCESS_TOKEN`;\n const accessToken = process.env[envVar];\n \n if (!accessToken) {\n throw new ConfigurationError(\n `${envVar} environment variable required for stdio mode`\n );\n }\n \n const userId = 'stdio-user';\n \n // Create server instance\n const server = await this.config.serverFactory(accessToken, userId);\n \n // Connect to stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n \n this.logger.info('Stdio transport started', { userId });\n }\n \n /**\n * Start SSE transport (multi-user mode)\n */\n private async startSSETransport(): Promise<void> {\n this.logger.info('Starting SSE transport', {\n port: this.config.transport.port,\n basePath: this.config.transport.basePath\n });\n \n // Import express dynamically (optional dependency)\n // @ts-ignore - Dynamic import of optional dependency\n const express = await import('express');\n const app = express.default();\n \n // Enable JSON parsing\n app.use(express.json());\n \n // Enable CORS if configured\n if (this.config.transport.cors) {\n // Validate CORS configuration\n if (!this.config.transport.corsOrigin) {\n throw new ConfigurationError(\n 'CORS origin must be explicitly configured when CORS is enabled. ' +\n 'Set transport.corsOrigin to a specific origin (e.g., \"https://app.example.com\") ' +\n 'or an array of allowed origins.'\n );\n }\n \n // Check for wildcard in production\n if (this.config.transport.corsOrigin === '*') {\n const isProduction = process.env.NODE_ENV === 'production';\n \n if (isProduction) {\n throw new ConfigurationError(\n 'CORS wildcard (*) is not allowed in production environments. ' +\n 'Specify explicit origins to prevent CSRF attacks. ' +\n 'Example: corsOrigin: \"https://app.example.com\"'\n );\n }\n \n this.logger.warn(\n 'CORS wildcard (*) detected in development. ' +\n 'This is insecure and should never be used in production.',\n { corsOrigin: this.config.transport.corsOrigin }\n );\n }\n \n // @ts-ignore - Dynamic import of optional dependency\n const cors = await import('cors');\n app.use(cors.default({\n origin: this.config.transport.corsOrigin,\n credentials: true,\n methods: ['GET', 'POST', 'OPTIONS'],\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'],\n exposedHeaders: ['X-Request-ID'],\n maxAge: 86400 // 24 hours\n }));\n \n this.logger.info('CORS enabled', {\n origin: this.config.transport.corsOrigin,\n credentials: true\n });\n }\n \n const basePath = this.config.transport.basePath || '/mcp';\n \n // Root endpoint info\n app.get(basePath, (req: any, res: any) => {\n res.json({\n name: this.config.name,\n version: this.config.version,\n resourceType: this.config.resourceType,\n endpoints: {\n message: `POST ${basePath}/message`,\n health: `GET ${basePath}/health`\n },\n documentation: 'https://github.com/prmichaelsen/mcp-auth'\n });\n });\n \n // SSE endpoint for MCP messages\n app.post(`${basePath}/message`, async (req: any, res: any) => {\n try {\n const context: RequestContext = {\n headers: req.headers as Record<string, string>,\n transport: 'sse',\n timestamp: new Date(),\n requestId: req.headers['x-request-id'] as string | undefined\n };\n \n // Handle request and forward to MCP server via transport\n await this.handleSSERequest(req, res, context);\n \n } catch (error) {\n this.logger.error('SSE request failed', error as Error);\n \n if (error instanceof AuthenticationError || error instanceof TokenResolutionError) {\n res.status(error.statusCode).json({\n error: error.message,\n code: error.code\n });\n } else {\n res.status(500).json({\n error: 'Internal server error',\n code: 'INTERNAL_ERROR'\n });\n }\n }\n });\n \n // Health check endpoint\n app.get(`${basePath}/health`, (req: any, res: any) => {\n res.json({\n status: 'healthy',\n name: this.config.name,\n version: this.config.version,\n resourceType: this.config.resourceType,\n instanceMode: this.config.instanceMode,\n poolSize: this.serverPool.size\n });\n });\n \n // Start server\n const port = this.config.transport.port || 3000;\n const host = this.config.transport.host || '0.0.0.0';\n \n await new Promise<void>((resolve) => {\n app.listen(port, host, () => {\n this.logger.info('SSE transport listening', {\n host,\n port,\n basePath,\n url: `http://${host}:${port}${basePath}`\n });\n resolve();\n });\n });\n }\n \n /**\n * Start HTTP transport (multi-user mode)\n */\n private async startHTTPTransport(): Promise<void> {\n this.logger.info('Starting HTTP transport', {\n port: this.config.transport.port\n });\n \n // HTTP transport is similar to SSE but with different endpoint structure\n // For now, delegate to SSE implementation\n await this.startSSETransport();\n }\n \n /**\n * Get server pool statistics\n */\n getPoolStats(): {\n size: number;\n instances: Array<{\n userId: string;\n createdAt: number;\n lastUsed: number;\n age: number;\n idleTime: number;\n }>;\n } {\n const now = Date.now();\n const instances = Array.from(this.serverPool.entries()).map(([userId, instance]) => ({\n userId,\n createdAt: instance.createdAt,\n lastUsed: instance.lastUsed,\n age: now - instance.createdAt,\n idleTime: now - instance.lastUsed\n }));\n \n return {\n size: this.serverPool.size,\n instances\n };\n }\n \n /**\n * Check if server is running\n */\n isServerRunning(): boolean {\n return this.isRunning;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|