@prmichaelsen/mcp-auth 0.1.0 → 0.1.1

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
@@ -6,42 +6,60 @@ Authentication and multi-tenancy framework for MCP (Model Context Protocol) serv
6
6
 
7
7
  `@prmichaelsen/mcp-auth` provides a pluggable authentication system for MCP servers, enabling:
8
8
 
9
+ - **Zero modification**: Wrap existing MCP servers without code changes
9
10
  - **Multi-tenancy**: Multiple users with separate resource tokens
10
- - **Auth-agnostic**: Support for JWT, OAuth, API Keys, or custom auth schemes
11
+ - **Auth-agnostic**: Support for JWT, environment variables, or custom auth schemes
11
12
  - **Transport-agnostic**: Works with stdio, HTTP, and SSE transports
12
13
  - **Type-safe**: Full TypeScript support
13
- - **Composable**: Middleware for rate limiting, logging, etc.
14
+ - **Composable**: Middleware for rate limiting, logging, timeouts, retries
14
15
 
15
- ## Installation
16
+ ## Two Patterns
16
17
 
17
- ```bash
18
- npm install @prmichaelsen/mcp-auth @modelcontextprotocol/sdk
18
+ ### Pattern 1: Server Wrapping (Recommended for Production)
19
+
20
+ Wrap existing MCP servers without modification:
21
+
22
+ ```typescript
23
+ import { wrapServer, JWTAuthProvider, APITokenResolver } from '@prmichaelsen/mcp-auth';
24
+ import { createServer } from '@myorg/my-mcp-server';
25
+
26
+ const wrapped = wrapServer({
27
+ serverFactory: createServer,
28
+
29
+ // Validates JWT from tenant manager
30
+ authProvider: new JWTAuthProvider({
31
+ jwtSecret: process.env.JWT_SECRET
32
+ }),
33
+
34
+ // Resolves tokens via tenant manager API (recommended)
35
+ tokenResolver: new APITokenResolver({
36
+ tenantManagerUrl: process.env.TENANT_MANAGER_URL,
37
+ serviceToken: process.env.SERVICE_TOKEN
38
+ }),
39
+
40
+ resourceType: 'myapi',
41
+ transport: { type: 'sse', port: 3000 }
42
+ });
43
+
44
+ await wrapped.start();
19
45
  ```
20
46
 
21
- ## Quick Start
47
+ ### Pattern 2: Tool-Level Auth
22
48
 
23
- ### Simple Function-Based Tool
49
+ Build new servers with integrated authentication:
24
50
 
25
51
  ```typescript
26
- import { withAuth, AuthenticatedMCPServer } from '@prmichaelsen/mcp-auth';
27
- import { EnvAuthProvider, SimpleTokenResolver } from '@prmichaelsen/mcp-auth/providers/env';
28
-
29
- // Setup auth provider
30
- const authProvider = new EnvAuthProvider();
31
- const tokenResolver = new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' });
52
+ import { AuthenticatedMCPServer, withAuth, EnvAuthProvider, SimpleTokenResolver } from '@prmichaelsen/mcp-auth';
32
53
 
33
- // Create server
34
54
  const server = new AuthenticatedMCPServer({
35
- name: 'my-mcp-server',
36
- authProvider,
37
- tokenResolver,
55
+ name: 'my-server',
56
+ authProvider: new EnvAuthProvider(),
57
+ tokenResolver: new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' }),
38
58
  resourceType: 'myapi',
39
59
  transport: { type: 'stdio' }
40
60
  });
41
61
 
42
- // Register tool with automatic auth
43
62
  server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {
44
- // accessToken and userId are automatically injected
45
63
  const client = new MyAPIClient(accessToken);
46
64
  return client.getData(args);
47
65
  }));
@@ -49,77 +67,123 @@ server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {
49
67
  await server.start();
50
68
  ```
51
69
 
52
- ### Class-Based Tool
70
+ ## Installation
53
71
 
54
- ```typescript
55
- import { Tool, AuthenticatedTool } from '@prmichaelsen/mcp-auth';
72
+ ```bash
73
+ # Core package
74
+ npm install @prmichaelsen/mcp-auth @modelcontextprotocol/sdk
56
75
 
57
- class GetDataTool implements Tool {
58
- name = 'get_data';
59
- description = 'Fetch data from API';
60
-
61
- async execute(args, accessToken, userId) {
62
- const client = new MyAPIClient(accessToken);
63
- return client.getData(args);
64
- }
65
- }
76
+ # For JWT support
77
+ npm install jsonwebtoken
66
78
 
67
- server.registerTool(new AuthenticatedTool(new GetDataTool()));
79
+ # For SSE/HTTP transports
80
+ npm install express cors
68
81
  ```
69
82
 
70
83
  ## Authentication Providers
71
84
 
72
- ### Environment Variable (Simple)
85
+ ### EnvAuthProvider (Single-User)
86
+
87
+ For local development and single-user scenarios:
73
88
 
74
89
  ```typescript
75
- import { EnvAuthProvider, SimpleTokenResolver } from '@prmichaelsen/mcp-auth/providers/env';
90
+ import { EnvAuthProvider, SimpleTokenResolver } from '@prmichaelsen/mcp-auth';
76
91
 
77
- const authProvider = new EnvAuthProvider();
78
- const tokenResolver = new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' });
92
+ const authProvider = new EnvAuthProvider({
93
+ userIdEnvVar: 'MCP_USER_ID',
94
+ defaultUserId: 'local-user'
95
+ });
96
+
97
+ const tokenResolver = new SimpleTokenResolver({
98
+ tokenEnvVar: 'API_TOKEN'
99
+ });
79
100
  ```
80
101
 
81
- ### JWT (Multi-tenant)
102
+ ### JWTAuthProvider + APITokenResolver (Multi-Tenant Production) ⭐ RECOMMENDED
103
+
104
+ For production multi-tenant deployments:
82
105
 
83
106
  ```typescript
84
- import { JWTAuthProvider } from '@prmichaelsen/mcp-auth/providers/jwt';
107
+ import { JWTAuthProvider, APITokenResolver } from '@prmichaelsen/mcp-auth';
85
108
 
109
+ // Validates JWT tokens from tenant manager
86
110
  const authProvider = new JWTAuthProvider({
87
111
  jwtSecret: process.env.JWT_SECRET,
88
- database: {
89
- host: 'localhost',
90
- database: 'users'
91
- }
112
+ userIdClaim: 'sub'
113
+ });
114
+
115
+ // Resolves tokens via tenant manager API
116
+ const tokenResolver = new APITokenResolver({
117
+ tenantManagerUrl: 'https://tenant-manager.example.com',
118
+ serviceToken: process.env.SERVICE_TOKEN,
119
+ cacheTokens: true, // Cache for performance
120
+ cacheTtl: 300000 // 5 minutes
92
121
  });
93
122
  ```
94
123
 
95
- ### OAuth 2.0
124
+ **Why API-Based is Better:**
125
+ - ✅ Automatic token refresh (no new JWT needed)
126
+ - ✅ Immediate token revocation
127
+ - ✅ Tokens not exposed in JWT
128
+ - ✅ Small JWT size (~200 bytes)
129
+ - ✅ Centralized token management
130
+
131
+ ### JWTAuthProvider + JWTTokenResolver (MVP/Prototyping)
132
+
133
+ For quick setup with JWT-embedded tokens:
96
134
 
97
135
  ```typescript
98
- import { OAuthProvider } from '@prmichaelsen/mcp-auth/providers/oauth';
136
+ import { JWTAuthProvider, JWTTokenResolver } from '@prmichaelsen/mcp-auth';
99
137
 
100
- const authProvider = new OAuthProvider({
101
- authorizationUrl: 'https://auth.example.com/authorize',
102
- tokenUrl: 'https://auth.example.com/token',
103
- clientId: process.env.OAUTH_CLIENT_ID,
104
- clientSecret: process.env.OAUTH_CLIENT_SECRET
138
+ const authProvider = new JWTAuthProvider({
139
+ jwtSecret: process.env.JWT_SECRET,
140
+ extractTokens: true // Extract tokens from JWT
141
+ });
142
+
143
+ const tokenResolver = new JWTTokenResolver({ authProvider });
144
+ ```
145
+
146
+ **Trade-offs:**
147
+ - ✅ Zero API calls (faster)
148
+ - ✅ Simpler to implement
149
+ - ❌ Token rotation requires new JWT
150
+ - ❌ Larger JWT size
151
+ - ❌ Tokens exposed in JWT payload
152
+
153
+ ### APITokenResolver (API-Based)
154
+
155
+ For resolving tokens via tenant manager API:
156
+
157
+ ```typescript
158
+ import { JWTAuthProvider, APITokenResolver } from '@prmichaelsen/mcp-auth';
159
+
160
+ const authProvider = new JWTAuthProvider({
161
+ jwtSecret: process.env.JWT_SECRET
162
+ });
163
+
164
+ const tokenResolver = new APITokenResolver({
165
+ tenantManagerUrl: 'https://tenant-manager.example.com',
166
+ serviceToken: process.env.SERVICE_TOKEN,
167
+ endpointPath: '/api/credentials/:userId/:resourceType'
105
168
  });
106
169
  ```
107
170
 
108
171
  ### Custom Provider
109
172
 
173
+ Implement your own authentication logic:
174
+
110
175
  ```typescript
111
176
  import { AuthProvider, AuthResult, RequestContext } from '@prmichaelsen/mcp-auth';
112
177
 
113
178
  class CustomAuthProvider implements AuthProvider {
114
179
  async authenticate(context: RequestContext): Promise<AuthResult> {
115
- // Your custom auth logic
116
180
  const apiKey = context.headers?.['x-api-key'];
117
181
 
118
182
  if (!apiKey) {
119
183
  return { authenticated: false, error: 'No API key' };
120
184
  }
121
185
 
122
- // Validate and return user ID
186
+ // Your validation logic
123
187
  return {
124
188
  authenticated: true,
125
189
  userId: 'user-123'
@@ -131,11 +195,12 @@ class CustomAuthProvider implements AuthProvider {
131
195
  ## Middleware Composition
132
196
 
133
197
  ```typescript
134
- import { compose, withAuth, withRateLimit, withLogging } from '@prmichaelsen/mcp-auth';
198
+ import { compose, withAuth, withRateLimit, withLogging, withTimeout } from '@prmichaelsen/mcp-auth';
135
199
 
136
200
  const getTool = compose(
137
- withLogging(),
201
+ withLogging({ logArgs: true }),
138
202
  withRateLimit({ maxRequests: 100, windowMs: 60000 }),
203
+ withTimeout(5000),
139
204
  withAuth(),
140
205
  async (args, accessToken, userId) => {
141
206
  // Your tool logic
@@ -150,41 +215,121 @@ server.registerTool('get_data', getTool);
150
215
  ### Stdio (Local)
151
216
 
152
217
  ```typescript
153
- const server = new AuthenticatedMCPServer({
154
- // ...
155
- transport: { type: 'stdio' }
156
- });
218
+ transport: { type: 'stdio' }
157
219
  ```
158
220
 
159
- ### HTTP/SSE (Remote)
221
+ ### SSE (Remote Multi-Tenant)
160
222
 
161
223
  ```typescript
162
- const server = new AuthenticatedMCPServer({
163
- // ...
164
- transport: {
165
- type: 'sse',
166
- port: 3000,
167
- host: '0.0.0.0',
168
- basePath: '/mcp'
169
- }
170
- });
224
+ transport: {
225
+ type: 'sse',
226
+ port: 3000,
227
+ host: '0.0.0.0',
228
+ basePath: '/mcp',
229
+ cors: true
230
+ }
231
+ ```
232
+
233
+ ### HTTP (Remote)
234
+
235
+ ```typescript
236
+ transport: {
237
+ type: 'http',
238
+ port: 3000,
239
+ host: '0.0.0.0'
240
+ }
241
+ ```
242
+
243
+ ## MCP Server Contract
244
+
245
+ To make your MCP server compatible with `wrapServer()`, export a factory function:
246
+
247
+ ```typescript
248
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
249
+
250
+ export function createServer(accessToken: string, userId?: string): Server {
251
+ const server = new Server({ name: 'my-server', version: '1.0.0' });
252
+ const client = new MyAPIClient(accessToken);
253
+
254
+ // Register your tool handlers...
255
+
256
+ return server;
257
+ }
171
258
  ```
172
259
 
260
+ That's it! No mcp-auth imports needed in your server.
261
+
262
+ ## Architecture
263
+
264
+ ### Three-Tier Deployment
265
+
266
+ 1. **Chat Platform** - Sends MCP requests with JWT
267
+ 2. **Tenant Manager** - Issues JWTs, manages user credentials
268
+ 3. **MCP Server Instance** - Uses mcp-auth to wrap MCP servers
269
+
270
+ ### Token Resolution Approaches
271
+
272
+ **Approach 1: JWT with Embedded Tokens** (Recommended)
273
+ - Tenant manager includes resource tokens in JWT
274
+ - Zero external calls
275
+ - Fastest performance
276
+
277
+ **Approach 2: API-Based Resolution**
278
+ - Tenant manager provides API for token lookup
279
+ - Better separation of concerns
280
+ - Easier token rotation
281
+
282
+ See [`agent/token-resolution-approaches.md`](./agent/token-resolution-approaches.md) for details.
283
+
173
284
  ## Documentation
174
285
 
175
- See the [`agent/`](./agent/) directory for detailed architecture documentation:
286
+ Comprehensive architecture documentation in [`agent/`](./agent/):
176
287
 
177
- - [`multi-tenant-architecture.md`](./agent/multi-tenant-architecture.md) - Overall architecture and auth schemes
178
- - [`lib-structure.md`](./agent/lib-structure.md) - Package structure and design
179
- - [`tool-patterns.md`](./agent/tool-patterns.md) - Tool registration patterns
288
+ - [`server-wrapping-pattern.md`](./agent/server-wrapping-pattern.md) - Server wrapping architecture
289
+ - [`server-contract.md`](./agent/server-contract.md) - MCP server compatibility guide
290
+ - [`dual-pattern-architecture.md`](./agent/dual-pattern-architecture.md) - Both patterns explained
291
+ - [`token-resolution-approaches.md`](./agent/token-resolution-approaches.md) - Token resolution strategies
292
+ - [`why-two-steps.md`](./agent/why-two-steps.md) - AuthProvider vs TokenResolver
293
+ - [`INTEGRATION.md`](./INTEGRATION.md) - Integration guide for MCP server authors
180
294
 
181
295
  ## Examples
182
296
 
183
- See the [`examples/`](./examples/) directory for complete examples:
297
+ Working examples coming soon in [`examples/`](./examples/):
184
298
 
185
299
  - `simple-stdio/` - Single-user stdio server
186
- - `jwt-multi-tenant/` - Multi-tenant JWT server
187
- - `oauth-server/` - OAuth authentication flow
300
+ - `wrapped-server/` - Server wrapping with JWT
301
+ - `tool-level-auth/` - Tool-level authentication
302
+ - `jwt-multi-tenant/` - Multi-tenant JWT deployment
303
+
304
+ ## API Reference
305
+
306
+ ### Core Functions
307
+
308
+ - `wrapServer(config)` - Wrap MCP server with authentication
309
+ - `withAuth(handler)` - Add auth to function-based tools
310
+ - `compose(...middlewares)` - Compose middleware functions
311
+
312
+ ### Classes
313
+
314
+ - `AuthenticatedMCPServer` - MCP server with integrated auth
315
+ - `AuthenticatedTool` - Wrapper for class-based tools
316
+ - `BaseAuthProvider` - Base class for auth providers
317
+
318
+ ### Providers
319
+
320
+ - `EnvAuthProvider` - Environment variable authentication
321
+ - `SimpleTokenResolver` - Environment variable token resolution
322
+ - `JWTAuthProvider` - JWT validation with token extraction
323
+ - `JWTTokenResolver` - JWT-embedded token resolution
324
+ - `APITokenResolver` - API-based token resolution
325
+
326
+ ### Middleware
327
+
328
+ - `withAuth()` - Authentication
329
+ - `withLogging()` - Request/response logging
330
+ - `withRateLimit()` - Rate limiting per user
331
+ - `withTimeout()` - Request timeout
332
+ - `withRetry()` - Automatic retry on failure
188
333
 
189
334
  ## License
190
335
 
@@ -192,4 +337,4 @@ MIT
192
337
 
193
338
  ## Contributing
194
339
 
195
- Contributions welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
340
+ Contributions welcome! Please open an issue or PR on [GitHub](https://github.com/prmichaelsen/mcp-auth).
@@ -1,11 +1,17 @@
1
1
  import { BaseAuthProvider } from "./base-provider.js";
2
2
  import {
3
3
  EnvAuthProvider,
4
- SimpleTokenResolver
4
+ SimpleTokenResolver,
5
+ JWTAuthProvider,
6
+ JWTTokenResolver,
7
+ APITokenResolver
5
8
  } from "./providers/index.js";
6
9
  export {
10
+ APITokenResolver,
7
11
  BaseAuthProvider,
8
12
  EnvAuthProvider,
13
+ JWTAuthProvider,
14
+ JWTTokenResolver,
9
15
  SimpleTokenResolver
10
16
  };
11
17
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/auth/index.ts"],
4
- "sourcesContent": ["/**\n * Authentication module exports\n */\n\n// Types\nexport type {\n AuthProvider,\n ResourceTokenResolver,\n AuthenticatedContext,\n AuthProviderConfig,\n TokenResolverConfig\n} from './types.js';\n\n// Base provider\nexport { BaseAuthProvider } from './base-provider.js';\n\n// Providers\nexport {\n EnvAuthProvider,\n type EnvAuthProviderConfig,\n SimpleTokenResolver,\n type SimpleTokenResolverConfig\n} from './providers/index.js';\n"],
5
- "mappings": "AAcA,SAAS,wBAAwB;AAGjC;AAAA,EACE;AAAA,EAEA;AAAA,OAEK;",
4
+ "sourcesContent": ["/**\n * Authentication module exports\n */\n\n// Types\nexport type {\n AuthProvider,\n ResourceTokenResolver,\n AuthenticatedContext,\n AuthProviderConfig,\n TokenResolverConfig\n} from './types.js';\n\n// Base provider\nexport { BaseAuthProvider } from './base-provider.js';\n\n// Providers\nexport {\n EnvAuthProvider,\n type EnvAuthProviderConfig,\n SimpleTokenResolver,\n type SimpleTokenResolverConfig,\n JWTAuthProvider,\n type JWTAuthProviderConfig,\n type JWTPayload,\n JWTTokenResolver,\n type JWTTokenResolverConfig,\n APITokenResolver,\n type APITokenResolverConfig\n} from './providers/index.js';\n"],
5
+ "mappings": "AAcA,SAAS,wBAAwB;AAGjC;AAAA,EACE;AAAA,EAEA;AAAA,EAEA;AAAA,EAGA;AAAA,EAEA;AAAA,OAEK;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,225 @@
1
+ import { TokenResolutionError } from "../../utils/errors.js";
2
+ import { createLogger } from "../../utils/logger.js";
3
+ import { validateAccessToken } from "../../utils/validation.js";
4
+ class APITokenResolver {
5
+ config;
6
+ logger;
7
+ tokenCache;
8
+ constructor(config) {
9
+ if (!config.tenantManagerUrl) {
10
+ throw new Error("tenantManagerUrl is required for APITokenResolver");
11
+ }
12
+ if (!config.serviceToken) {
13
+ throw new Error("serviceToken is required for APITokenResolver");
14
+ }
15
+ this.config = {
16
+ tenantManagerUrl: config.tenantManagerUrl.replace(/\/$/, ""),
17
+ // Remove trailing slash
18
+ serviceToken: config.serviceToken,
19
+ endpointPath: config.endpointPath ?? "/api/credentials/:userId/:resourceType",
20
+ timeoutMs: config.timeoutMs ?? 5e3,
21
+ throwOnMissing: config.throwOnMissing ?? true,
22
+ validateToken: config.validateToken ?? true,
23
+ customHeaders: config.customHeaders ?? {},
24
+ cacheTokens: config.cacheTokens ?? true,
25
+ cacheTtl: config.cacheTtl ?? 3e5,
26
+ // 5 minutes
27
+ autoRefresh: config.autoRefresh ?? false
28
+ };
29
+ this.logger = createLogger({ enabled: true, level: "info" });
30
+ this.tokenCache = /* @__PURE__ */ new Map();
31
+ }
32
+ /**
33
+ * Resolve token by calling tenant manager API
34
+ */
35
+ async resolveToken(userId, resourceType) {
36
+ if (this.config.cacheTokens) {
37
+ const cacheKey = `${userId}:${resourceType}`;
38
+ const cached = this.tokenCache.get(cacheKey);
39
+ if (cached && cached.expiresAt > Date.now()) {
40
+ this.logger.debug("Token retrieved from cache", {
41
+ userId,
42
+ resourceType,
43
+ cacheKey
44
+ });
45
+ return cached.token;
46
+ }
47
+ }
48
+ const url = this.buildUrl(userId, resourceType);
49
+ try {
50
+ this.logger.debug("Calling tenant manager API", {
51
+ userId,
52
+ resourceType,
53
+ url
54
+ });
55
+ const controller = new AbortController();
56
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
57
+ const response = await fetch(url, {
58
+ method: "GET",
59
+ headers: {
60
+ "Authorization": `Bearer ${this.config.serviceToken}`,
61
+ "Content-Type": "application/json",
62
+ ...this.config.customHeaders
63
+ },
64
+ signal: controller.signal
65
+ });
66
+ clearTimeout(timeoutId);
67
+ if (!response.ok) {
68
+ const errorText = await response.text().catch(() => "Unknown error");
69
+ this.logger.warn("API request failed", {
70
+ userId,
71
+ resourceType,
72
+ status: response.status,
73
+ error: errorText
74
+ });
75
+ if (this.config.throwOnMissing) {
76
+ throw new TokenResolutionError(userId, resourceType, {
77
+ status: response.status,
78
+ error: errorText,
79
+ url
80
+ });
81
+ }
82
+ return null;
83
+ }
84
+ const data = await response.json();
85
+ const token = data.accessToken || data.access_token || data.token;
86
+ if (!token) {
87
+ this.logger.warn("API response missing token", {
88
+ userId,
89
+ resourceType,
90
+ responseKeys: Object.keys(data || {})
91
+ });
92
+ if (this.config.throwOnMissing) {
93
+ throw new TokenResolutionError(userId, resourceType, {
94
+ reason: "API response missing accessToken field"
95
+ });
96
+ }
97
+ return null;
98
+ }
99
+ if (this.config.validateToken) {
100
+ try {
101
+ validateAccessToken(token);
102
+ } catch (error) {
103
+ this.logger.error("Token validation failed", error, {
104
+ userId,
105
+ resourceType
106
+ });
107
+ if (this.config.throwOnMissing) {
108
+ throw new TokenResolutionError(userId, resourceType, {
109
+ reason: "Invalid token format"
110
+ });
111
+ }
112
+ return null;
113
+ }
114
+ }
115
+ if (this.config.cacheTokens) {
116
+ const cacheKey = `${userId}:${resourceType}`;
117
+ this.tokenCache.set(cacheKey, {
118
+ token,
119
+ expiresAt: Date.now() + this.config.cacheTtl
120
+ });
121
+ this.logger.debug("Token cached", {
122
+ userId,
123
+ resourceType,
124
+ cacheKey,
125
+ ttl: this.config.cacheTtl
126
+ });
127
+ }
128
+ this.logger.info("Token resolved successfully", {
129
+ userId,
130
+ resourceType,
131
+ tokenLength: token.length
132
+ });
133
+ return token;
134
+ } catch (error) {
135
+ if (error instanceof TokenResolutionError) {
136
+ throw error;
137
+ }
138
+ this.logger.error("API request error", error, {
139
+ userId,
140
+ resourceType,
141
+ url
142
+ });
143
+ if (this.config.throwOnMissing) {
144
+ throw new TokenResolutionError(userId, resourceType, {
145
+ reason: error instanceof Error ? error.message : "API request failed",
146
+ url
147
+ });
148
+ }
149
+ return null;
150
+ }
151
+ }
152
+ /**
153
+ * Build API URL from template
154
+ */
155
+ buildUrl(userId, resourceType) {
156
+ const path = this.config.endpointPath.replace(":userId", encodeURIComponent(userId)).replace(":resourceType", encodeURIComponent(resourceType));
157
+ return `${this.config.tenantManagerUrl}${path}`;
158
+ }
159
+ /**
160
+ * Refresh token (calls API again)
161
+ */
162
+ async refreshToken(userId, resourceType) {
163
+ this.logger.info("Refreshing token via API", { userId, resourceType });
164
+ const cacheKey = `${userId}:${resourceType}`;
165
+ this.tokenCache.delete(cacheKey);
166
+ return this.resolveToken(userId, resourceType);
167
+ }
168
+ /**
169
+ * Validate token (basic check)
170
+ */
171
+ async validateToken(token, resourceType) {
172
+ if (!this.config.validateToken) {
173
+ return true;
174
+ }
175
+ try {
176
+ validateAccessToken(token);
177
+ return true;
178
+ } catch {
179
+ return false;
180
+ }
181
+ }
182
+ /**
183
+ * Initialize resolver
184
+ */
185
+ async initialize() {
186
+ this.logger.info("APITokenResolver initialized", {
187
+ tenantManagerUrl: this.config.tenantManagerUrl,
188
+ endpointPath: this.config.endpointPath,
189
+ cacheEnabled: this.config.cacheTokens,
190
+ timeoutMs: this.config.timeoutMs
191
+ });
192
+ try {
193
+ new URL(this.config.tenantManagerUrl);
194
+ } catch {
195
+ throw new Error(`Invalid tenantManagerUrl: ${this.config.tenantManagerUrl}`);
196
+ }
197
+ }
198
+ /**
199
+ * Cleanup resources
200
+ */
201
+ async cleanup() {
202
+ this.tokenCache.clear();
203
+ this.logger.info("APITokenResolver cleaned up");
204
+ }
205
+ /**
206
+ * Clear token cache
207
+ */
208
+ clearCache() {
209
+ this.tokenCache.clear();
210
+ this.logger.debug("Token cache cleared");
211
+ }
212
+ /**
213
+ * Get cache statistics
214
+ */
215
+ getCacheStats() {
216
+ return {
217
+ size: this.tokenCache.size,
218
+ keys: Array.from(this.tokenCache.keys())
219
+ };
220
+ }
221
+ }
222
+ export {
223
+ APITokenResolver
224
+ };
225
+ //# sourceMappingURL=api-token-resolver.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/providers/api-token-resolver.ts"],
4
+ "sourcesContent": ["/**\n * API-based token resolver\n * \n * Resolves tokens by calling the tenant manager's API.\n * Alternative to JWT-embedded tokens for better separation.\n */\n\nimport type { ResourceTokenResolver, TokenResolverConfig } from '../types.js';\nimport { TokenResolutionError } from '../../utils/errors.js';\nimport { createLogger, type Logger } from '../../utils/logger.js';\nimport { validateAccessToken } from '../../utils/validation.js';\n\n/**\n * Configuration for APITokenResolver\n */\nexport interface APITokenResolverConfig extends TokenResolverConfig {\n /**\n * Tenant manager API base URL\n * @example 'https://tenant-manager.example.com'\n */\n tenantManagerUrl: string;\n \n /**\n * Service token for authenticating MCP server \u2192 tenant manager requests\n * This is a separate token from user JWTs\n */\n serviceToken: string;\n \n /**\n * API endpoint path template\n * @default '/api/credentials/:userId/:resourceType'\n * \n * Variables:\n * - :userId - Will be replaced with actual user ID\n * - :resourceType - Will be replaced with resource type\n */\n endpointPath?: string;\n \n /**\n * Request timeout in milliseconds\n * @default 5000\n */\n timeoutMs?: number;\n \n /**\n * Whether to throw error if token is not found\n * @default true\n */\n throwOnMissing?: boolean;\n \n /**\n * Whether to validate token format\n * @default true\n */\n validateToken?: boolean;\n \n /**\n * Custom headers to include in API requests\n */\n customHeaders?: Record<string, string>;\n}\n\n/**\n * API token resolver\n * \n * Resolves tokens by calling the tenant manager's API endpoint.\n * This approach provides better separation between MCP server and tenant manager.\n * \n * @example\n * ```typescript\n * const resolver = new APITokenResolver({\n * tenantManagerUrl: 'https://tenant-manager.example.com',\n * serviceToken: process.env.SERVICE_TOKEN\n * });\n * \n * // Calls: GET https://tenant-manager.example.com/api/credentials/user-123/instagram\n * // Headers: { Authorization: Bearer <service-token> }\n * // Returns: { accessToken: \"IGQVJXabc...\" }\n * ```\n */\nexport class APITokenResolver implements ResourceTokenResolver {\n private config: Required<APITokenResolverConfig>;\n private logger: Logger;\n private tokenCache: Map<string, { token: string; expiresAt: number }>;\n \n constructor(config: APITokenResolverConfig) {\n if (!config.tenantManagerUrl) {\n throw new Error('tenantManagerUrl is required for APITokenResolver');\n }\n if (!config.serviceToken) {\n throw new Error('serviceToken is required for APITokenResolver');\n }\n \n this.config = {\n tenantManagerUrl: config.tenantManagerUrl.replace(/\\/$/, ''), // Remove trailing slash\n serviceToken: config.serviceToken,\n endpointPath: config.endpointPath ?? '/api/credentials/:userId/:resourceType',\n timeoutMs: config.timeoutMs ?? 5000,\n throwOnMissing: config.throwOnMissing ?? true,\n validateToken: config.validateToken ?? true,\n customHeaders: config.customHeaders ?? {},\n cacheTokens: config.cacheTokens ?? true,\n cacheTtl: config.cacheTtl ?? 300000, // 5 minutes\n autoRefresh: config.autoRefresh ?? false\n };\n \n this.logger = createLogger({ enabled: true, level: 'info' });\n this.tokenCache = new Map();\n }\n \n /**\n * Resolve token by calling tenant manager API\n */\n async resolveToken(userId: string, resourceType: string): Promise<string | null> {\n // Check cache if enabled\n if (this.config.cacheTokens) {\n const cacheKey = `${userId}:${resourceType}`;\n const cached = this.tokenCache.get(cacheKey);\n \n if (cached && cached.expiresAt > Date.now()) {\n this.logger.debug('Token retrieved from cache', {\n userId,\n resourceType,\n cacheKey\n });\n return cached.token;\n }\n }\n \n // Build API URL\n const url = this.buildUrl(userId, resourceType);\n \n try {\n this.logger.debug('Calling tenant manager API', {\n userId,\n resourceType,\n url\n });\n \n // Call tenant manager API with timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);\n \n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${this.config.serviceToken}`,\n 'Content-Type': 'application/json',\n ...this.config.customHeaders\n },\n signal: controller.signal\n });\n \n clearTimeout(timeoutId);\n \n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n \n this.logger.warn('API request failed', {\n userId,\n resourceType,\n status: response.status,\n error: errorText\n });\n \n if (this.config.throwOnMissing) {\n throw new TokenResolutionError(userId, resourceType, {\n status: response.status,\n error: errorText,\n url\n });\n }\n \n return null;\n }\n \n // Parse response\n const data = await response.json() as any;\n const token = data.accessToken || data.access_token || data.token;\n \n if (!token) {\n this.logger.warn('API response missing token', {\n userId,\n resourceType,\n responseKeys: Object.keys(data || {})\n });\n \n if (this.config.throwOnMissing) {\n throw new TokenResolutionError(userId, resourceType, {\n reason: 'API response missing accessToken field'\n });\n }\n \n return null;\n }\n \n // Validate token format if enabled\n if (this.config.validateToken) {\n try {\n validateAccessToken(token);\n } catch (error) {\n this.logger.error('Token validation failed', error as Error, {\n userId,\n resourceType\n });\n \n if (this.config.throwOnMissing) {\n throw new TokenResolutionError(userId, resourceType, {\n reason: 'Invalid token format'\n });\n }\n \n return null;\n }\n }\n \n // Cache token if enabled\n if (this.config.cacheTokens) {\n const cacheKey = `${userId}:${resourceType}`;\n this.tokenCache.set(cacheKey, {\n token,\n expiresAt: Date.now() + this.config.cacheTtl\n });\n \n this.logger.debug('Token cached', {\n userId,\n resourceType,\n cacheKey,\n ttl: this.config.cacheTtl\n });\n }\n \n this.logger.info('Token resolved successfully', {\n userId,\n resourceType,\n tokenLength: token.length\n });\n \n return token;\n \n } catch (error) {\n if (error instanceof TokenResolutionError) {\n throw error;\n }\n \n this.logger.error('API request error', error as Error, {\n userId,\n resourceType,\n url\n });\n \n if (this.config.throwOnMissing) {\n throw new TokenResolutionError(userId, resourceType, {\n reason: error instanceof Error ? error.message : 'API request failed',\n url\n });\n }\n \n return null;\n }\n }\n \n /**\n * Build API URL from template\n */\n private buildUrl(userId: string, resourceType: string): string {\n const path = this.config.endpointPath\n .replace(':userId', encodeURIComponent(userId))\n .replace(':resourceType', encodeURIComponent(resourceType));\n \n return `${this.config.tenantManagerUrl}${path}`;\n }\n \n /**\n * Refresh token (calls API again)\n */\n async refreshToken(userId: string, resourceType: string): Promise<string | null> {\n this.logger.info('Refreshing token via API', { userId, resourceType });\n \n // Clear cache and fetch fresh token\n const cacheKey = `${userId}:${resourceType}`;\n this.tokenCache.delete(cacheKey);\n \n return this.resolveToken(userId, resourceType);\n }\n \n /**\n * Validate token (basic check)\n */\n async validateToken(token: string, resourceType: string): Promise<boolean> {\n if (!this.config.validateToken) {\n return true;\n }\n \n try {\n validateAccessToken(token);\n return true;\n } catch {\n return false;\n }\n }\n \n /**\n * Initialize resolver\n */\n async initialize(): Promise<void> {\n this.logger.info('APITokenResolver initialized', {\n tenantManagerUrl: this.config.tenantManagerUrl,\n endpointPath: this.config.endpointPath,\n cacheEnabled: this.config.cacheTokens,\n timeoutMs: this.config.timeoutMs\n });\n \n // Validate configuration\n try {\n new URL(this.config.tenantManagerUrl);\n } catch {\n throw new Error(`Invalid tenantManagerUrl: ${this.config.tenantManagerUrl}`);\n }\n }\n \n /**\n * Cleanup resources\n */\n async cleanup(): Promise<void> {\n this.tokenCache.clear();\n this.logger.info('APITokenResolver cleaned up');\n }\n \n /**\n * Clear token cache\n */\n clearCache(): void {\n this.tokenCache.clear();\n this.logger.debug('Token cache cleared');\n }\n \n /**\n * Get cache statistics\n */\n getCacheStats(): { size: number; keys: string[] } {\n return {\n size: this.tokenCache.size,\n keys: Array.from(this.tokenCache.keys())\n };\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -4,8 +4,20 @@ import {
4
4
  import {
5
5
  SimpleTokenResolver
6
6
  } from "./simple-resolver.js";
7
+ import {
8
+ JWTAuthProvider
9
+ } from "./jwt-provider.js";
10
+ import {
11
+ JWTTokenResolver
12
+ } from "./jwt-token-resolver.js";
13
+ import {
14
+ APITokenResolver
15
+ } from "./api-token-resolver.js";
7
16
  export {
17
+ APITokenResolver,
8
18
  EnvAuthProvider,
19
+ JWTAuthProvider,
20
+ JWTTokenResolver,
9
21
  SimpleTokenResolver
10
22
  };
11
23
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/auth/providers/index.ts"],
4
- "sourcesContent": ["/**\n * Authentication providers module exports\n */\n\n// Environment-based provider\nexport { \n EnvAuthProvider,\n type EnvAuthProviderConfig \n} from './env-provider.js';\n\n// Simple token resolver\nexport { \n SimpleTokenResolver,\n type SimpleTokenResolverConfig \n} from './simple-resolver.js';\n"],
5
- "mappings": "AAKA;AAAA,EACE;AAAA,OAEK;AAGP;AAAA,EACE;AAAA,OAEK;",
4
+ "sourcesContent": ["/**\n * Authentication providers module exports\n */\n\n// Environment-based provider\nexport {\n EnvAuthProvider,\n type EnvAuthProviderConfig\n} from './env-provider.js';\n\n// Simple token resolver\nexport {\n SimpleTokenResolver,\n type SimpleTokenResolverConfig\n} from './simple-resolver.js';\n\n// JWT provider and resolver\nexport {\n JWTAuthProvider,\n type JWTAuthProviderConfig,\n type JWTPayload\n} from './jwt-provider.js';\n\nexport {\n JWTTokenResolver,\n type JWTTokenResolverConfig\n} from './jwt-token-resolver.js';\n\n// API-based token resolver\nexport {\n APITokenResolver,\n type APITokenResolverConfig\n} from './api-token-resolver.js';\n"],
5
+ "mappings": "AAKA;AAAA,EACE;AAAA,OAEK;AAGP;AAAA,EACE;AAAA,OAEK;AAGP;AAAA,EACE;AAAA,OAGK;AAEP;AAAA,EACE;AAAA,OAEK;AAGP;AAAA,EACE;AAAA,OAEK;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,136 @@
1
+ import { BaseAuthProvider } from "../base-provider.js";
2
+ class JWTAuthProvider extends BaseAuthProvider {
3
+ jwtConfig;
4
+ /**
5
+ * Token cache for extracted tokens
6
+ * Maps userId → resourceType → token
7
+ */
8
+ tokenCache = /* @__PURE__ */ new Map();
9
+ constructor(config) {
10
+ super(config);
11
+ if (!config.jwtSecret) {
12
+ throw new Error("jwtSecret is required for JWTAuthProvider");
13
+ }
14
+ this.jwtConfig = {
15
+ ...config,
16
+ jwtSecret: config.jwtSecret,
17
+ algorithm: config.algorithm ?? "HS256",
18
+ extractTokens: config.extractTokens ?? true,
19
+ userIdClaim: config.userIdClaim ?? "sub",
20
+ tokensClaim: config.tokensClaim ?? "tokens",
21
+ validateExpiration: config.validateExpiration ?? true,
22
+ clockTolerance: config.clockTolerance ?? 0,
23
+ errorMessages: config.errorMessages ?? {},
24
+ cacheResults: config.cacheResults ?? false,
25
+ cacheTtl: config.cacheTtl ?? 6e4
26
+ };
27
+ }
28
+ /**
29
+ * Authenticate request by validating JWT
30
+ */
31
+ async doAuthenticate(context) {
32
+ const token = this.extractBearerToken(context);
33
+ if (!token) {
34
+ return this.createFailureResult(
35
+ this.jwtConfig.errorMessages?.noAuth || "No JWT token provided"
36
+ );
37
+ }
38
+ try {
39
+ const jwt = await import("jsonwebtoken");
40
+ const decoded = jwt.verify(token, this.jwtConfig.jwtSecret, {
41
+ algorithms: [this.jwtConfig.algorithm],
42
+ clockTolerance: this.jwtConfig.clockTolerance
43
+ });
44
+ const userId = decoded[this.jwtConfig.userIdClaim] || decoded.userId || decoded.sub;
45
+ if (!userId) {
46
+ return this.createFailureResult("JWT does not contain user ID");
47
+ }
48
+ this.logger.debug("JWT validated successfully", {
49
+ userId,
50
+ hasTokens: !!decoded[this.jwtConfig.tokensClaim]
51
+ });
52
+ if (this.jwtConfig.extractTokens && decoded[this.jwtConfig.tokensClaim]) {
53
+ const tokens = decoded[this.jwtConfig.tokensClaim];
54
+ const userTokens = /* @__PURE__ */ new Map();
55
+ for (const [resourceType, resourceToken] of Object.entries(tokens)) {
56
+ userTokens.set(resourceType, resourceToken);
57
+ }
58
+ this.tokenCache.set(userId, userTokens);
59
+ this.logger.debug("Extracted tokens from JWT", {
60
+ userId,
61
+ resourceTypes: Object.keys(tokens)
62
+ });
63
+ }
64
+ return this.createSuccessResult(userId, {
65
+ exp: decoded.exp,
66
+ iat: decoded.iat,
67
+ hasEmbeddedTokens: !!decoded[this.jwtConfig.tokensClaim]
68
+ });
69
+ } catch (error) {
70
+ this.logger.warn("JWT validation failed", {
71
+ error: error instanceof Error ? error.message : "Unknown error"
72
+ });
73
+ if (error instanceof Error) {
74
+ if (error.message.includes("expired")) {
75
+ return this.createFailureResult(
76
+ this.jwtConfig.errorMessages?.expiredAuth || "JWT token has expired"
77
+ );
78
+ }
79
+ if (error.message.includes("invalid")) {
80
+ return this.createFailureResult(
81
+ this.jwtConfig.errorMessages?.invalidAuth || "Invalid JWT token"
82
+ );
83
+ }
84
+ }
85
+ return this.createFailureResult("JWT validation failed");
86
+ }
87
+ }
88
+ /**
89
+ * Validate provider configuration
90
+ */
91
+ async validate() {
92
+ if (!this.jwtConfig.jwtSecret || this.jwtConfig.jwtSecret.length < 32) {
93
+ this.logger.error("JWT secret must be at least 32 characters");
94
+ return false;
95
+ }
96
+ try {
97
+ await import("jsonwebtoken");
98
+ } catch {
99
+ this.logger.error("jsonwebtoken package is required. Install it with: npm install jsonwebtoken");
100
+ return false;
101
+ }
102
+ return true;
103
+ }
104
+ /**
105
+ * Get cached token for a user and resource
106
+ */
107
+ getCachedToken(userId, resourceType) {
108
+ return this.tokenCache.get(userId)?.get(resourceType) || null;
109
+ }
110
+ /**
111
+ * Clear token cache
112
+ */
113
+ clearTokenCache() {
114
+ this.tokenCache.clear();
115
+ this.logger.debug("Token cache cleared");
116
+ }
117
+ /**
118
+ * Get token cache statistics
119
+ */
120
+ getTokenCacheStats() {
121
+ const users = Array.from(this.tokenCache.entries()).map(([userId, tokens]) => ({
122
+ userId,
123
+ resourceTypes: Array.from(tokens.keys())
124
+ }));
125
+ const totalTokens = users.reduce((sum, user) => sum + user.resourceTypes.length, 0);
126
+ return {
127
+ userCount: this.tokenCache.size,
128
+ totalTokens,
129
+ users
130
+ };
131
+ }
132
+ }
133
+ export {
134
+ JWTAuthProvider
135
+ };
136
+ //# sourceMappingURL=jwt-provider.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/providers/jwt-provider.ts"],
4
+ "sourcesContent": ["/**\n * JWT authentication provider\n * \n * Reference implementation for JWT-based authentication.\n * Validates JWT tokens and optionally extracts embedded resource tokens.\n */\n\nimport { BaseAuthProvider } from '../base-provider.js';\nimport type { AuthProviderConfig } from '../types.js';\nimport type { RequestContext, AuthResult } from '../../types.js';\nimport { AuthenticationError, MissingCredentialsError, InvalidTokenError } from '../../utils/errors.js';\n\n/**\n * JWT payload structure\n */\nexport interface JWTPayload {\n /**\n * User ID (standard JWT claim: 'sub' or custom 'userId')\n */\n sub?: string;\n userId?: string;\n \n /**\n * Optional: Embedded resource tokens\n * { instagram: \"token1\", github: \"token2\" }\n */\n tokens?: Record<string, string>;\n \n /**\n * Expiration time (standard JWT claim)\n */\n exp?: number;\n \n /**\n * Issued at (standard JWT claim)\n */\n iat?: number;\n \n /**\n * Additional custom claims\n */\n [key: string]: any;\n}\n\n/**\n * Configuration for JWTAuthProvider\n */\nexport interface JWTAuthProviderConfig extends AuthProviderConfig {\n /**\n * JWT secret for verification\n */\n jwtSecret: string;\n \n /**\n * JWT algorithm\n * @default 'HS256'\n */\n algorithm?: string;\n \n /**\n * Whether to extract embedded tokens from JWT\n * If true, tokens will be cached for JWTTokenResolver\n * @default true\n */\n extractTokens?: boolean;\n \n /**\n * Custom user ID claim name\n * @default 'sub' (falls back to 'userId')\n */\n userIdClaim?: string;\n \n /**\n * Custom tokens claim name\n * @default 'tokens'\n */\n tokensClaim?: string;\n \n /**\n * Whether to validate token expiration\n * @default true\n */\n validateExpiration?: boolean;\n \n /**\n * Clock tolerance in seconds for exp/nbf claims\n * @default 0\n */\n clockTolerance?: number;\n}\n\n/**\n * JWT authentication provider\n * \n * Validates JWT tokens issued by the tenant manager.\n * Optionally extracts embedded resource tokens from the JWT payload.\n * \n * @example\n * ```typescript\n * // Basic usage\n * const provider = new JWTAuthProvider({\n * jwtSecret: process.env.JWT_SECRET\n * });\n * \n * // With token extraction\n * const provider = new JWTAuthProvider({\n * jwtSecret: process.env.JWT_SECRET,\n * extractTokens: true\n * });\n * ```\n */\nexport class JWTAuthProvider extends BaseAuthProvider {\n private jwtConfig: Required<JWTAuthProviderConfig>;\n \n /**\n * Token cache for extracted tokens\n * Maps userId \u2192 resourceType \u2192 token\n */\n public readonly tokenCache = new Map<string, Map<string, string>>();\n \n constructor(config: JWTAuthProviderConfig) {\n super(config);\n \n if (!config.jwtSecret) {\n throw new Error('jwtSecret is required for JWTAuthProvider');\n }\n \n this.jwtConfig = {\n ...config,\n jwtSecret: config.jwtSecret,\n algorithm: config.algorithm ?? 'HS256',\n extractTokens: config.extractTokens ?? true,\n userIdClaim: config.userIdClaim ?? 'sub',\n tokensClaim: config.tokensClaim ?? 'tokens',\n validateExpiration: config.validateExpiration ?? true,\n clockTolerance: config.clockTolerance ?? 0,\n errorMessages: config.errorMessages ?? {},\n cacheResults: config.cacheResults ?? false,\n cacheTtl: config.cacheTtl ?? 60000\n };\n }\n \n /**\n * Authenticate request by validating JWT\n */\n protected async doAuthenticate(context: RequestContext): Promise<AuthResult> {\n // Extract bearer token\n const token = this.extractBearerToken(context);\n \n if (!token) {\n return this.createFailureResult(\n this.jwtConfig.errorMessages?.noAuth || 'No JWT token provided'\n );\n }\n \n try {\n // Dynamically import jsonwebtoken (optional dependency)\n // @ts-ignore - Dynamic import of optional dependency\n const jwt = await import('jsonwebtoken');\n \n // Verify JWT\n const decoded = jwt.verify(token, this.jwtConfig.jwtSecret, {\n algorithms: [this.jwtConfig.algorithm as any],\n clockTolerance: this.jwtConfig.clockTolerance\n }) as JWTPayload;\n \n // Extract user ID\n const userId = decoded[this.jwtConfig.userIdClaim] || decoded.userId || decoded.sub;\n \n if (!userId) {\n return this.createFailureResult('JWT does not contain user ID');\n }\n \n this.logger.debug('JWT validated successfully', {\n userId,\n hasTokens: !!decoded[this.jwtConfig.tokensClaim]\n });\n \n // Extract embedded tokens if enabled\n if (this.jwtConfig.extractTokens && decoded[this.jwtConfig.tokensClaim]) {\n const tokens = decoded[this.jwtConfig.tokensClaim] as Record<string, string>;\n \n // Cache tokens for resolver\n const userTokens = new Map<string, string>();\n for (const [resourceType, resourceToken] of Object.entries(tokens)) {\n userTokens.set(resourceType, resourceToken);\n }\n this.tokenCache.set(userId, userTokens);\n \n this.logger.debug('Extracted tokens from JWT', {\n userId,\n resourceTypes: Object.keys(tokens)\n });\n }\n \n return this.createSuccessResult(userId, {\n exp: decoded.exp,\n iat: decoded.iat,\n hasEmbeddedTokens: !!decoded[this.jwtConfig.tokensClaim]\n });\n \n } catch (error) {\n this.logger.warn('JWT validation failed', {\n error: error instanceof Error ? error.message : 'Unknown error'\n });\n \n if (error instanceof Error) {\n if (error.message.includes('expired')) {\n return this.createFailureResult(\n this.jwtConfig.errorMessages?.expiredAuth || 'JWT token has expired'\n );\n }\n if (error.message.includes('invalid')) {\n return this.createFailureResult(\n this.jwtConfig.errorMessages?.invalidAuth || 'Invalid JWT token'\n );\n }\n }\n \n return this.createFailureResult('JWT validation failed');\n }\n }\n \n /**\n * Validate provider configuration\n */\n async validate(): Promise<boolean> {\n if (!this.jwtConfig.jwtSecret || this.jwtConfig.jwtSecret.length < 32) {\n this.logger.error('JWT secret must be at least 32 characters');\n return false;\n }\n \n // Try to import jsonwebtoken\n try {\n // @ts-ignore - Dynamic import of optional dependency\n await import('jsonwebtoken');\n } catch {\n this.logger.error('jsonwebtoken package is required. Install it with: npm install jsonwebtoken');\n return false;\n }\n \n return true;\n }\n \n /**\n * Get cached token for a user and resource\n */\n getCachedToken(userId: string, resourceType: string): string | null {\n return this.tokenCache.get(userId)?.get(resourceType) || null;\n }\n \n /**\n * Clear token cache\n */\n clearTokenCache(): void {\n this.tokenCache.clear();\n this.logger.debug('Token cache cleared');\n }\n \n /**\n * Get token cache statistics\n */\n getTokenCacheStats(): {\n userCount: number;\n totalTokens: number;\n users: Array<{ userId: string; resourceTypes: string[] }>;\n } {\n const users = Array.from(this.tokenCache.entries()).map(([userId, tokens]) => ({\n userId,\n resourceTypes: Array.from(tokens.keys())\n }));\n \n const totalTokens = users.reduce((sum, user) => sum + user.resourceTypes.length, 0);\n \n return {\n userCount: this.tokenCache.size,\n totalTokens,\n users\n };\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,94 @@
1
+ import { TokenResolutionError } from "../../utils/errors.js";
2
+ import { createLogger } from "../../utils/logger.js";
3
+ class JWTTokenResolver {
4
+ config;
5
+ logger;
6
+ constructor(config) {
7
+ if (!config.authProvider) {
8
+ throw new Error("authProvider is required for JWTTokenResolver");
9
+ }
10
+ this.config = {
11
+ authProvider: config.authProvider,
12
+ throwOnMissing: config.throwOnMissing ?? true,
13
+ cacheTokens: config.cacheTokens ?? true,
14
+ cacheTtl: config.cacheTtl ?? 3e5,
15
+ autoRefresh: config.autoRefresh ?? false
16
+ };
17
+ this.logger = createLogger({ enabled: true, level: "info" });
18
+ }
19
+ /**
20
+ * Resolve token from JWT auth provider's cache
21
+ */
22
+ async resolveToken(userId, resourceType) {
23
+ const token = this.config.authProvider.getCachedToken(userId, resourceType);
24
+ if (!token) {
25
+ const errorMessage = `No ${resourceType} token found in JWT for user ${userId}`;
26
+ this.logger.warn("Token resolution failed", {
27
+ userId,
28
+ resourceType,
29
+ reason: "Token not found in JWT cache"
30
+ });
31
+ if (this.config.throwOnMissing) {
32
+ throw new TokenResolutionError(userId, resourceType, {
33
+ reason: "Token not embedded in JWT",
34
+ hint: "Ensure tenant manager includes tokens in JWT payload"
35
+ });
36
+ }
37
+ return null;
38
+ }
39
+ this.logger.debug("Token resolved from JWT cache", {
40
+ userId,
41
+ resourceType,
42
+ tokenLength: token.length
43
+ });
44
+ return token;
45
+ }
46
+ /**
47
+ * Refresh token (not supported for JWT-embedded tokens)
48
+ */
49
+ async refreshToken(userId, resourceType) {
50
+ this.logger.warn("Token refresh not supported for JWT-embedded tokens", {
51
+ userId,
52
+ resourceType,
53
+ hint: "User must obtain new JWT from tenant manager"
54
+ });
55
+ return null;
56
+ }
57
+ /**
58
+ * Validate token (checks if token exists in cache)
59
+ */
60
+ async validateToken(token, resourceType) {
61
+ return !!(token && token.length > 0);
62
+ }
63
+ /**
64
+ * Initialize resolver
65
+ */
66
+ async initialize() {
67
+ this.logger.info("JWTTokenResolver initialized", {
68
+ throwOnMissing: this.config.throwOnMissing
69
+ });
70
+ }
71
+ /**
72
+ * Cleanup resources
73
+ */
74
+ async cleanup() {
75
+ this.logger.info("JWTTokenResolver cleaned up");
76
+ }
77
+ /**
78
+ * Get available resource types for a user
79
+ */
80
+ getAvailableResources(userId) {
81
+ const userTokens = this.config.authProvider.tokenCache.get(userId);
82
+ return userTokens ? Array.from(userTokens.keys()) : [];
83
+ }
84
+ /**
85
+ * Check if token is available for a user and resource
86
+ */
87
+ hasToken(userId, resourceType) {
88
+ return !!this.config.authProvider.getCachedToken(userId, resourceType);
89
+ }
90
+ }
91
+ export {
92
+ JWTTokenResolver
93
+ };
94
+ //# sourceMappingURL=jwt-token-resolver.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/auth/providers/jwt-token-resolver.ts"],
4
+ "sourcesContent": ["/**\n * JWT token resolver\n * \n * Resolves tokens that were extracted from JWT by JWTAuthProvider.\n * Works in conjunction with JWTAuthProvider for JWT-embedded token approach.\n */\n\nimport type { ResourceTokenResolver, TokenResolverConfig } from '../types.js';\nimport type { JWTAuthProvider } from './jwt-provider.js';\nimport { TokenResolutionError } from '../../utils/errors.js';\nimport { createLogger, type Logger } from '../../utils/logger.js';\n\n/**\n * Configuration for JWTTokenResolver\n */\nexport interface JWTTokenResolverConfig extends TokenResolverConfig {\n /**\n * JWTAuthProvider instance that extracts tokens\n */\n authProvider: JWTAuthProvider;\n \n /**\n * Whether to throw error if token is not found\n * @default true\n */\n throwOnMissing?: boolean;\n}\n\n/**\n * JWT token resolver\n * \n * Resolves tokens that were cached by JWTAuthProvider during authentication.\n * This is used for the JWT-embedded token approach where the JWT contains\n * all resource tokens.\n * \n * @example\n * ```typescript\n * const authProvider = new JWTAuthProvider({\n * jwtSecret: process.env.JWT_SECRET,\n * extractTokens: true\n * });\n * \n * const tokenResolver = new JWTTokenResolver({\n * authProvider\n * });\n * \n * // JWT structure:\n * // {\n * // \"userId\": \"user-123\",\n * // \"tokens\": {\n * // \"instagram\": \"IGQVJXabc...\",\n * // \"github\": \"ghp_abc123...\"\n * // }\n * // }\n * ```\n */\nexport class JWTTokenResolver implements ResourceTokenResolver {\n private config: Required<JWTTokenResolverConfig>;\n private logger: Logger;\n \n constructor(config: JWTTokenResolverConfig) {\n if (!config.authProvider) {\n throw new Error('authProvider is required for JWTTokenResolver');\n }\n \n this.config = {\n authProvider: config.authProvider,\n throwOnMissing: config.throwOnMissing ?? true,\n cacheTokens: config.cacheTokens ?? true,\n cacheTtl: config.cacheTtl ?? 300000,\n autoRefresh: config.autoRefresh ?? false\n };\n \n this.logger = createLogger({ enabled: true, level: 'info' });\n }\n \n /**\n * Resolve token from JWT auth provider's cache\n */\n async resolveToken(userId: string, resourceType: string): Promise<string | null> {\n // Get token from auth provider's cache\n const token = this.config.authProvider.getCachedToken(userId, resourceType);\n \n if (!token) {\n const errorMessage = `No ${resourceType} token found in JWT for user ${userId}`;\n \n this.logger.warn('Token resolution failed', {\n userId,\n resourceType,\n reason: 'Token not found in JWT cache'\n });\n \n if (this.config.throwOnMissing) {\n throw new TokenResolutionError(userId, resourceType, {\n reason: 'Token not embedded in JWT',\n hint: 'Ensure tenant manager includes tokens in JWT payload'\n });\n }\n \n return null;\n }\n \n this.logger.debug('Token resolved from JWT cache', {\n userId,\n resourceType,\n tokenLength: token.length\n });\n \n return token;\n }\n \n /**\n * Refresh token (not supported for JWT-embedded tokens)\n */\n async refreshToken(userId: string, resourceType: string): Promise<string | null> {\n this.logger.warn('Token refresh not supported for JWT-embedded tokens', {\n userId,\n resourceType,\n hint: 'User must obtain new JWT from tenant manager'\n });\n \n return null;\n }\n \n /**\n * Validate token (checks if token exists in cache)\n */\n async validateToken(token: string, resourceType: string): Promise<boolean> {\n // Basic validation - just check if token is non-empty\n return !!(token && token.length > 0);\n }\n \n /**\n * Initialize resolver\n */\n async initialize(): Promise<void> {\n this.logger.info('JWTTokenResolver initialized', {\n throwOnMissing: this.config.throwOnMissing\n });\n }\n \n /**\n * Cleanup resources\n */\n async cleanup(): Promise<void> {\n // Token cache is managed by auth provider\n this.logger.info('JWTTokenResolver cleaned up');\n }\n \n /**\n * Get available resource types for a user\n */\n getAvailableResources(userId: string): string[] {\n const userTokens = this.config.authProvider.tokenCache.get(userId);\n return userTokens ? Array.from(userTokens.keys()) : [];\n }\n \n /**\n * Check if token is available for a user and resource\n */\n hasToken(userId: string, resourceType: string): boolean {\n return !!this.config.authProvider.getCachedToken(userId, resourceType);\n }\n}\n"],
5
+ "mappings": "AASA,SAAS,4BAA4B;AACrC,SAAS,oBAAiC;AA8CnC,MAAM,iBAAkD;AAAA,EACrD;AAAA,EACA;AAAA,EAER,YAAY,QAAgC;AAC1C,QAAI,CAAC,OAAO,cAAc;AACxB,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAEA,SAAK,SAAS;AAAA,MACZ,cAAc,OAAO;AAAA,MACrB,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,aAAa,OAAO,eAAe;AAAA,MACnC,UAAU,OAAO,YAAY;AAAA,MAC7B,aAAa,OAAO,eAAe;AAAA,IACrC;AAEA,SAAK,SAAS,aAAa,EAAE,SAAS,MAAM,OAAO,OAAO,CAAC;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,QAAgB,cAA8C;AAE/E,UAAM,QAAQ,KAAK,OAAO,aAAa,eAAe,QAAQ,YAAY;AAE1E,QAAI,CAAC,OAAO;AACV,YAAM,eAAe,MAAM,YAAY,gCAAgC,MAAM;AAE7E,WAAK,OAAO,KAAK,2BAA2B;AAAA,QAC1C;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAED,UAAI,KAAK,OAAO,gBAAgB;AAC9B,cAAM,IAAI,qBAAqB,QAAQ,cAAc;AAAA,UACnD,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,IACT;AAEA,SAAK,OAAO,MAAM,iCAAiC;AAAA,MACjD;AAAA,MACA;AAAA,MACA,aAAa,MAAM;AAAA,IACrB,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,QAAgB,cAA8C;AAC/E,SAAK,OAAO,KAAK,uDAAuD;AAAA,MACtE;AAAA,MACA;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAAc,OAAe,cAAwC;AAEzE,WAAO,CAAC,EAAE,SAAS,MAAM,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,SAAK,OAAO,KAAK,gCAAgC;AAAA,MAC/C,gBAAgB,KAAK,OAAO;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAE7B,SAAK,OAAO,KAAK,6BAA6B;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,sBAAsB,QAA0B;AAC9C,UAAM,aAAa,KAAK,OAAO,aAAa,WAAW,IAAI,MAAM;AACjE,WAAO,aAAa,MAAM,KAAK,WAAW,KAAK,CAAC,IAAI,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,QAAgB,cAA+B;AACtD,WAAO,CAAC,CAAC,KAAK,OAAO,aAAa,eAAe,QAAQ,YAAY;AAAA,EACvE;AACF;",
6
+ "names": []
7
+ }
package/dist/index.js CHANGED
@@ -16,7 +16,10 @@ import {
16
16
  import { BaseAuthProvider } from "./auth/base-provider.js";
17
17
  import {
18
18
  EnvAuthProvider,
19
- SimpleTokenResolver
19
+ SimpleTokenResolver,
20
+ JWTAuthProvider,
21
+ JWTTokenResolver,
22
+ APITokenResolver
20
23
  } from "./auth/providers/index.js";
21
24
  import {
22
25
  MCPAuthError,
@@ -57,6 +60,7 @@ import {
57
60
  validateAccessToken
58
61
  } from "./utils/index.js";
59
62
  export {
63
+ APITokenResolver,
60
64
  AuthenticatedMCPServer,
61
65
  AuthenticatedServerWrapper,
62
66
  AuthenticatedTool,
@@ -65,6 +69,8 @@ export {
65
69
  ConfigurationError,
66
70
  EnvAuthProvider,
67
71
  InvalidTokenError,
72
+ JWTAuthProvider,
73
+ JWTTokenResolver,
68
74
  LogLevel,
69
75
  Logger,
70
76
  MCPAuthError,
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["/**\n * @prmichaelsen/mcp-auth\n *\n * Authentication and multi-tenancy framework for MCP (Model Context Protocol) servers.\n *\n * Supports two complementary patterns:\n * 1. **Server Wrapping** - Wrap existing MCP servers without modification (MCP-level auth)\n * 2. **Tool-Level Auth** - Build new MCP servers with integrated auth\n *\n * @packageDocumentation\n */\n\n// ============================================================================\n// PATTERN 1: SERVER WRAPPING (MCP-Level Auth)\n// ============================================================================\n// Use this to wrap existing MCP servers without modifying them\n// Ideal for multi-tenant services that host multiple MCP servers\n\nexport {\n wrapServer,\n AuthenticatedServerWrapper,\n type ServerWrapperConfig,\n type MCPServerFactory,\n type NormalizedServerWrapperConfig\n} from './wrapper/index.js';\n\n// ============================================================================\n// PATTERN 2: TOOL-LEVEL AUTH\n// ============================================================================\n// Use this to build new MCP servers with integrated authentication\n// Provides fine-grained control over auth per tool\n\nexport {\n AuthenticatedMCPServer,\n type ServerConfig,\n type NormalizedServerConfig,\n withAuth,\n compose,\n withLogging,\n withRateLimit,\n withTimeout,\n withRetry,\n type Tool,\n AuthenticatedTool,\n createAuthenticatedTool,\n type AuthenticatedToolHandler\n} from './server/index.js';\n\n// ============================================================================\n// SHARED: CORE TYPES\n// ============================================================================\n\nexport type {\n TransportType,\n RequestContext,\n AuthResult,\n TransportConfig,\n RateLimitConfig,\n LoggingConfig,\n MiddlewareConfig,\n PoolingConfig,\n Result,\n AsyncFunction,\n ToolHandler,\n Middleware\n} from './types.js';\n\n// ============================================================================\n// SHARED: AUTHENTICATION\n// ============================================================================\n\nexport type {\n AuthProvider,\n ResourceTokenResolver,\n AuthenticatedContext,\n AuthProviderConfig,\n TokenResolverConfig\n} from './auth/types.js';\n\nexport { BaseAuthProvider } from './auth/base-provider.js';\n\n// Providers\nexport {\n EnvAuthProvider,\n type EnvAuthProviderConfig,\n SimpleTokenResolver,\n type SimpleTokenResolverConfig\n} from './auth/providers/index.js';\n\n// Advanced Providers (to be implemented in Phase 8)\n// export { JWTAuthProvider } from './auth/providers/jwt-provider.js';\n// export { OAuthProvider } from './auth/providers/oauth-provider.js';\n// export { APIKeyProvider } from './auth/providers/apikey-provider.js';\n// export { DatabaseTokenResolver } from './auth/providers/database-resolver.js';\n\n// ============================================================================\n// SHARED: UTILITIES\n// ============================================================================\n\nexport {\n // Errors\n MCPAuthError,\n AuthenticationError,\n TokenResolutionError,\n InvalidTokenError,\n MissingCredentialsError,\n ConfigurationError,\n RateLimitError,\n ServerPoolError,\n TransportError,\n ValidationError,\n isMCPAuthError,\n isAuthenticationError,\n isTokenResolutionError,\n isRateLimitError,\n formatErrorForClient,\n \n // Logger\n Logger,\n LogLevel,\n defaultLogger,\n createLogger,\n sanitizeForLogging,\n \n // Validation\n validateNonEmptyString,\n validateUrl,\n validatePositiveNumber,\n validatePort,\n validateEnum,\n validateObject,\n validateFunction,\n validateRequiredFields,\n validateTransportConfig,\n validateRateLimitConfig,\n validateLoggingConfig,\n validatePoolingConfig,\n sanitizeString,\n validateUserId,\n validateResourceType,\n validateAccessToken\n} from './utils/index.js';\n\n// Re-export types for convenience\nexport type { LogEntry } from './utils/logger.js';\n\n// ============================================================================\n// USAGE EXAMPLES\n// ============================================================================\n\n/**\n * @example Server Wrapping Pattern (MCP-Level Auth)\n * ```typescript\n * import { wrapServer, JWTAuthProvider, DatabaseTokenResolver } from '@prmichaelsen/mcp-auth';\n * import { createServer as createInstagramServer } from '@prmichaelsen/instagram-mcp';\n *\n * const wrapped = wrapServer({\n * serverFactory: (accessToken, userId) => createInstagramServer(accessToken, userId),\n * authProvider: new JWTAuthProvider({ jwtSecret: process.env.JWT_SECRET }),\n * tokenResolver: new DatabaseTokenResolver({ database: {...} }),\n * resourceType: 'instagram',\n * transport: { type: 'sse', port: 3000 }\n * });\n *\n * await wrapped.start();\n * ```\n *\n * @example Tool-Level Auth Pattern\n * ```typescript\n * import { AuthenticatedMCPServer, withAuth, EnvAuthProvider } from '@prmichaelsen/mcp-auth';\n *\n * const server = new AuthenticatedMCPServer({\n * name: 'my-server',\n * authProvider: new EnvAuthProvider(),\n * tokenResolver: new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' }),\n * resourceType: 'myapi',\n * transport: { type: 'stdio' }\n * });\n *\n * server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {\n * const client = new MyAPIClient(accessToken);\n * return client.getData(args);\n * }));\n *\n * await server.start();\n * ```\n */\n"],
5
- "mappings": "AAkBA;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AAQP;AAAA,EACE;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OAEK;AAiCP,SAAS,wBAAwB;AAGjC;AAAA,EACE;AAAA,EAEA;AAAA,OAEK;AAYP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;",
4
+ "sourcesContent": ["/**\n * @prmichaelsen/mcp-auth\n *\n * Authentication and multi-tenancy framework for MCP (Model Context Protocol) servers.\n *\n * Supports two complementary patterns:\n * 1. **Server Wrapping** - Wrap existing MCP servers without modification (MCP-level auth)\n * 2. **Tool-Level Auth** - Build new MCP servers with integrated auth\n *\n * @packageDocumentation\n */\n\n// ============================================================================\n// PATTERN 1: SERVER WRAPPING (MCP-Level Auth)\n// ============================================================================\n// Use this to wrap existing MCP servers without modifying them\n// Ideal for multi-tenant services that host multiple MCP servers\n\nexport {\n wrapServer,\n AuthenticatedServerWrapper,\n type ServerWrapperConfig,\n type MCPServerFactory,\n type NormalizedServerWrapperConfig\n} from './wrapper/index.js';\n\n// ============================================================================\n// PATTERN 2: TOOL-LEVEL AUTH\n// ============================================================================\n// Use this to build new MCP servers with integrated authentication\n// Provides fine-grained control over auth per tool\n\nexport {\n AuthenticatedMCPServer,\n type ServerConfig,\n type NormalizedServerConfig,\n withAuth,\n compose,\n withLogging,\n withRateLimit,\n withTimeout,\n withRetry,\n type Tool,\n AuthenticatedTool,\n createAuthenticatedTool,\n type AuthenticatedToolHandler\n} from './server/index.js';\n\n// ============================================================================\n// SHARED: CORE TYPES\n// ============================================================================\n\nexport type {\n TransportType,\n RequestContext,\n AuthResult,\n TransportConfig,\n RateLimitConfig,\n LoggingConfig,\n MiddlewareConfig,\n PoolingConfig,\n Result,\n AsyncFunction,\n ToolHandler,\n Middleware\n} from './types.js';\n\n// ============================================================================\n// SHARED: AUTHENTICATION\n// ============================================================================\n\nexport type {\n AuthProvider,\n ResourceTokenResolver,\n AuthenticatedContext,\n AuthProviderConfig,\n TokenResolverConfig\n} from './auth/types.js';\n\nexport { BaseAuthProvider } from './auth/base-provider.js';\n\n// Providers\nexport {\n EnvAuthProvider,\n type EnvAuthProviderConfig,\n SimpleTokenResolver,\n type SimpleTokenResolverConfig,\n JWTAuthProvider,\n type JWTAuthProviderConfig,\n type JWTPayload,\n JWTTokenResolver,\n type JWTTokenResolverConfig,\n APITokenResolver,\n type APITokenResolverConfig\n} from './auth/providers/index.js';\n\n// Note: OAuth and API Key providers can be added in the future if needed\n// For now, JWT-based auth with embedded or API-resolved tokens covers most use cases\n\n// ============================================================================\n// SHARED: UTILITIES\n// ============================================================================\n\nexport {\n // Errors\n MCPAuthError,\n AuthenticationError,\n TokenResolutionError,\n InvalidTokenError,\n MissingCredentialsError,\n ConfigurationError,\n RateLimitError,\n ServerPoolError,\n TransportError,\n ValidationError,\n isMCPAuthError,\n isAuthenticationError,\n isTokenResolutionError,\n isRateLimitError,\n formatErrorForClient,\n \n // Logger\n Logger,\n LogLevel,\n defaultLogger,\n createLogger,\n sanitizeForLogging,\n \n // Validation\n validateNonEmptyString,\n validateUrl,\n validatePositiveNumber,\n validatePort,\n validateEnum,\n validateObject,\n validateFunction,\n validateRequiredFields,\n validateTransportConfig,\n validateRateLimitConfig,\n validateLoggingConfig,\n validatePoolingConfig,\n sanitizeString,\n validateUserId,\n validateResourceType,\n validateAccessToken\n} from './utils/index.js';\n\n// Re-export types for convenience\nexport type { LogEntry } from './utils/logger.js';\n\n// ============================================================================\n// USAGE EXAMPLES\n// ============================================================================\n\n/**\n * @example Server Wrapping Pattern (MCP-Level Auth)\n * ```typescript\n * import { wrapServer, JWTAuthProvider, DatabaseTokenResolver } from '@prmichaelsen/mcp-auth';\n * import { createServer as createInstagramServer } from '@prmichaelsen/instagram-mcp';\n *\n * const wrapped = wrapServer({\n * serverFactory: (accessToken, userId) => createInstagramServer(accessToken, userId),\n * authProvider: new JWTAuthProvider({ jwtSecret: process.env.JWT_SECRET }),\n * tokenResolver: new DatabaseTokenResolver({ database: {...} }),\n * resourceType: 'instagram',\n * transport: { type: 'sse', port: 3000 }\n * });\n *\n * await wrapped.start();\n * ```\n *\n * @example Tool-Level Auth Pattern\n * ```typescript\n * import { AuthenticatedMCPServer, withAuth, EnvAuthProvider } from '@prmichaelsen/mcp-auth';\n *\n * const server = new AuthenticatedMCPServer({\n * name: 'my-server',\n * authProvider: new EnvAuthProvider(),\n * tokenResolver: new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' }),\n * resourceType: 'myapi',\n * transport: { type: 'stdio' }\n * });\n *\n * server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {\n * const client = new MyAPIClient(accessToken);\n * return client.getData(args);\n * }));\n *\n * await server.start();\n * ```\n */\n"],
5
+ "mappings": "AAkBA;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AAQP;AAAA,EACE;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OAEK;AAiCP,SAAS,wBAAwB;AAGjC;AAAA,EACE;AAAA,EAEA;AAAA,EAEA;AAAA,EAGA;AAAA,EAEA;AAAA,OAEK;AASP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;",
6
6
  "names": []
7
7
  }
@@ -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 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 incoming MCP request with authentication\n */\n private async handleRequest(request: any, context: RequestContext): Promise<any> {\n const requestId = context.requestId ?? `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const requestLogger = this.logger.child({ requestId });\n \n try {\n requestLogger.debug('Handling request', {\n transport: context.transport,\n hasHeaders: !!context.headers\n });\n \n // 1. Authenticate request\n const authResult = await this.config.authProvider.authenticate(context);\n \n if (!authResult.authenticated || !authResult.userId) {\n requestLogger.warn('Authentication failed', {\n error: authResult.error\n });\n throw new AuthenticationError(authResult.error || 'Authentication failed');\n }\n \n const userId = validateUserId(authResult.userId);\n \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', {\n userId,\n resourceType: this.config.resourceType\n });\n throw new TokenResolutionError(userId, this.config.resourceType);\n }\n \n validateAccessToken(accessToken);\n \n requestLogger.debug('Token resolved', {\n userId,\n resourceType: this.config.resourceType,\n tokenLength: accessToken.length\n });\n \n // 3. Get server instance (ephemeral or pooled)\n const server = await this.getServerInstance(userId, accessToken);\n \n // 4. Forward request to server\n // Note: This is a simplified version. Actual implementation would need\n // to properly handle MCP protocol messages\n requestLogger.debug('Forwarding request to server instance', { userId });\n \n // TODO: Implement actual MCP request forwarding\n // For now, this is a placeholder\n const response = { success: true, userId, resourceType: this.config.resourceType };\n \n requestLogger.info('Request handled successfully', {\n userId,\n resourceType: this.config.resourceType\n });\n \n return response;\n \n } catch (error) {\n requestLogger.error('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\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 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 // 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 const result = await this.handleRequest(req.body, context);\n res.json(result);\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 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 incoming MCP request with authentication\n */\n private async handleRequest(request: any, context: RequestContext): Promise<any> {\n const requestId = context.requestId ?? `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const requestLogger = this.logger.child({ requestId });\n \n try {\n requestLogger.debug('Handling request', {\n transport: context.transport,\n hasHeaders: !!context.headers\n });\n \n // 1. Authenticate request\n const authResult = await this.config.authProvider.authenticate(context);\n \n if (!authResult.authenticated || !authResult.userId) {\n requestLogger.warn('Authentication failed', {\n error: authResult.error\n });\n throw new AuthenticationError(authResult.error || 'Authentication failed');\n }\n \n const userId = validateUserId(authResult.userId);\n \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', {\n userId,\n resourceType: this.config.resourceType\n });\n throw new TokenResolutionError(userId, this.config.resourceType);\n }\n \n validateAccessToken(accessToken);\n \n requestLogger.debug('Token resolved', {\n userId,\n resourceType: this.config.resourceType,\n tokenLength: accessToken.length\n });\n \n // 3. Get server instance (ephemeral or pooled)\n const server = await this.getServerInstance(userId, accessToken);\n \n // 4. Forward request to server\n // Note: This is a simplified version. Actual implementation would need\n // to properly handle MCP protocol messages\n requestLogger.debug('Forwarding request to server instance', { userId });\n \n // TODO: Implement actual MCP request forwarding\n // For now, this is a placeholder\n const response = { success: true, userId, resourceType: this.config.resourceType };\n \n requestLogger.info('Request handled successfully', {\n userId,\n resourceType: this.config.resourceType\n });\n \n return response;\n \n } catch (error) {\n requestLogger.error('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 // 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 const result = await this.handleRequest(req.body, context);\n res.json(result);\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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prmichaelsen/mcp-auth",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Authentication and multi-tenancy framework for MCP servers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -85,7 +85,9 @@
85
85
  },
86
86
  "optionalDependencies": {
87
87
  "@types/jsonwebtoken": "^9.0.0",
88
- "jsonwebtoken": "^9.0.0"
88
+ "jsonwebtoken": "^9.0.0",
89
+ "express": "^4.21.2",
90
+ "cors": "^2.8.5"
89
91
  },
90
92
  "publishConfig": {
91
93
  "access": "public"