@mcp-z/client 1.0.1 โ†’ 1.0.3

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.
Files changed (73) hide show
  1. package/dist/cjs/auth/capability-discovery.d.cts +0 -19
  2. package/dist/cjs/auth/capability-discovery.d.ts +0 -19
  3. package/dist/cjs/auth/capability-discovery.js +123 -52
  4. package/dist/cjs/auth/capability-discovery.js.map +1 -1
  5. package/dist/cjs/auth/index.js.map +1 -1
  6. package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
  7. package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
  8. package/dist/cjs/auth/pkce.js.map +1 -1
  9. package/dist/cjs/auth/rfc9728-discovery.d.cts +7 -0
  10. package/dist/cjs/auth/rfc9728-discovery.d.ts +7 -0
  11. package/dist/cjs/auth/rfc9728-discovery.js +208 -46
  12. package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
  13. package/dist/cjs/auth/types.js.map +1 -1
  14. package/dist/cjs/client-helpers.js.map +1 -1
  15. package/dist/cjs/config/server-loader.js.map +1 -1
  16. package/dist/cjs/config/validate-config.js +3 -7
  17. package/dist/cjs/config/validate-config.js.map +1 -1
  18. package/dist/cjs/connection/connect-client.js.map +1 -1
  19. package/dist/cjs/connection/existing-process-transport.js.map +1 -1
  20. package/dist/cjs/connection/types.js.map +1 -1
  21. package/dist/cjs/connection/wait-for-http-ready.js.map +1 -1
  22. package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
  23. package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
  24. package/dist/cjs/dcr/index.js.map +1 -1
  25. package/dist/cjs/index.js.map +1 -1
  26. package/dist/cjs/monkey-patches.js.map +1 -1
  27. package/dist/cjs/response-wrappers.js.map +1 -1
  28. package/dist/cjs/search/index.js.map +1 -1
  29. package/dist/cjs/search/search.js.map +1 -1
  30. package/dist/cjs/search/types.js.map +1 -1
  31. package/dist/cjs/spawn/spawn-server.js +4 -0
  32. package/dist/cjs/spawn/spawn-server.js.map +1 -1
  33. package/dist/cjs/spawn/spawn-servers.js.map +1 -1
  34. package/dist/cjs/types.js.map +1 -1
  35. package/dist/cjs/utils/logger.js.map +1 -1
  36. package/dist/cjs/utils/path-utils.js.map +1 -1
  37. package/dist/cjs/utils/sanitizer.js.map +1 -1
  38. package/dist/esm/auth/capability-discovery.d.ts +0 -19
  39. package/dist/esm/auth/capability-discovery.js +43 -41
  40. package/dist/esm/auth/capability-discovery.js.map +1 -1
  41. package/dist/esm/auth/index.js.map +1 -1
  42. package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
  43. package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
  44. package/dist/esm/auth/pkce.js.map +1 -1
  45. package/dist/esm/auth/rfc9728-discovery.d.ts +7 -0
  46. package/dist/esm/auth/rfc9728-discovery.js +67 -0
  47. package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
  48. package/dist/esm/auth/types.js.map +1 -1
  49. package/dist/esm/client-helpers.js.map +1 -1
  50. package/dist/esm/config/server-loader.js.map +1 -1
  51. package/dist/esm/config/validate-config.js +3 -7
  52. package/dist/esm/config/validate-config.js.map +1 -1
  53. package/dist/esm/connection/connect-client.js.map +1 -1
  54. package/dist/esm/connection/existing-process-transport.js.map +1 -1
  55. package/dist/esm/connection/types.js.map +1 -1
  56. package/dist/esm/connection/wait-for-http-ready.js.map +1 -1
  57. package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
  58. package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
  59. package/dist/esm/dcr/index.js.map +1 -1
  60. package/dist/esm/index.js.map +1 -1
  61. package/dist/esm/monkey-patches.js.map +1 -1
  62. package/dist/esm/response-wrappers.js.map +1 -1
  63. package/dist/esm/search/index.js.map +1 -1
  64. package/dist/esm/search/search.js.map +1 -1
  65. package/dist/esm/search/types.js.map +1 -1
  66. package/dist/esm/spawn/spawn-server.js +4 -0
  67. package/dist/esm/spawn/spawn-server.js.map +1 -1
  68. package/dist/esm/spawn/spawn-servers.js.map +1 -1
  69. package/dist/esm/types.js.map +1 -1
  70. package/dist/esm/utils/logger.js.map +1 -1
  71. package/dist/esm/utils/path-utils.js.map +1 -1
  72. package/dist/esm/utils/sanitizer.js.map +1 -1
  73. package/package.json +1 -1
@@ -3,23 +3,4 @@
3
3
  * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
4
4
  */
5
5
  import type { AuthCapabilities } from './types.js';
6
- /**
7
- * Probe OAuth server capabilities using RFC 9728 โ†’ RFC 8414 discovery chain
8
- * Returns capabilities including DCR support detection
9
- *
10
- * Discovery Strategy:
11
- * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)
12
- * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata
13
- * 3. Fall back to direct RFC 8414 discovery at resource origin
14
- *
15
- * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)
16
- * @returns AuthCapabilities object with discovered endpoints and features
17
- *
18
- * @example
19
- * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com
20
- * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');
21
- * if (caps.supportsDcr) {
22
- * console.log('Registration endpoint:', caps.registrationEndpoint);
23
- * }
24
- */
25
6
  export declare function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities>;
@@ -3,23 +3,4 @@
3
3
  * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata
4
4
  */
5
5
  import type { AuthCapabilities } from './types.js';
6
- /**
7
- * Probe OAuth server capabilities using RFC 9728 โ†’ RFC 8414 discovery chain
8
- * Returns capabilities including DCR support detection
9
- *
10
- * Discovery Strategy:
11
- * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)
12
- * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata
13
- * 3. Fall back to direct RFC 8414 discovery at resource origin
14
- *
15
- * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)
16
- * @returns AuthCapabilities object with discovered endpoints and features
17
- *
18
- * @example
19
- * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com
20
- * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');
21
- * if (caps.supportsDcr) {
22
- * console.log('Registration endpoint:', caps.registrationEndpoint);
23
- * }
24
- */
25
6
  export declare function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities>;
@@ -156,17 +156,81 @@ function _ts_generator(thisArg, body) {
156
156
  return url;
157
157
  }
158
158
  }
159
+ /**
160
+ * Probe OAuth server capabilities using RFC 9728 โ†’ RFC 8414 discovery chain
161
+ * Returns capabilities including DCR support detection
162
+ *
163
+ * Discovery Strategy:
164
+ * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)
165
+ * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata
166
+ * 3. Fall back to direct RFC 8414 discovery at resource origin
167
+ *
168
+ * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)
169
+ * @returns AuthCapabilities object with discovered endpoints and features
170
+ *
171
+ * @example
172
+ * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com
173
+ * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');
174
+ * if (caps.supportsDcr) {
175
+ * console.log('Registration endpoint:', caps.registrationEndpoint);
176
+ * }
177
+ */ function buildCapabilities(metadata, scopes) {
178
+ var supportsDcr = !!metadata.registration_endpoint;
179
+ var capabilities = {
180
+ supportsDcr: supportsDcr
181
+ };
182
+ if (metadata.registration_endpoint) {
183
+ capabilities.registrationEndpoint = metadata.registration_endpoint;
184
+ }
185
+ if (metadata.authorization_endpoint) {
186
+ capabilities.authorizationEndpoint = metadata.authorization_endpoint;
187
+ }
188
+ if (metadata.token_endpoint) capabilities.tokenEndpoint = metadata.token_endpoint;
189
+ if (metadata.introspection_endpoint) {
190
+ capabilities.introspectionEndpoint = metadata.introspection_endpoint;
191
+ }
192
+ if (scopes && scopes.length > 0) {
193
+ capabilities.scopes = scopes;
194
+ } else if (metadata.scopes_supported) {
195
+ capabilities.scopes = metadata.scopes_supported;
196
+ }
197
+ return capabilities;
198
+ }
199
+ function resolveCapabilitiesFromAuthorizationServer(authServerUrl, scopes) {
200
+ return _async_to_generator(function() {
201
+ var metadata;
202
+ return _ts_generator(this, function(_state) {
203
+ switch(_state.label){
204
+ case 0:
205
+ return [
206
+ 4,
207
+ (0, _rfc9728discoveryts.discoverAuthorizationServerMetadata)(authServerUrl)
208
+ ];
209
+ case 1:
210
+ metadata = _state.sent();
211
+ if (!metadata) return [
212
+ 2,
213
+ null
214
+ ];
215
+ return [
216
+ 2,
217
+ buildCapabilities(metadata, scopes)
218
+ ];
219
+ }
220
+ });
221
+ })();
222
+ }
159
223
  function probeAuthCapabilities(baseUrl) {
160
224
  return _async_to_generator(function() {
161
- var resourceMetadata, authServerUrl, authServerMetadata, supportsDcr, capabilities, scopes, origin, authServerMetadata1, supportsDcr1, capabilities1, _error;
225
+ var resourceMetadata, authServerUrl, capabilities, issuer, issuerCapabilities, issuer1, issuerCapabilities1, origin, originCapabilities, _error;
162
226
  return _ts_generator(this, function(_state) {
163
227
  switch(_state.label){
164
228
  case 0:
165
229
  _state.trys.push([
166
230
  0,
167
- 5,
231
+ 10,
168
232
  ,
169
- 6
233
+ 11
170
234
  ]);
171
235
  return [
172
236
  4,
@@ -176,7 +240,7 @@ function probeAuthCapabilities(baseUrl) {
176
240
  resourceMetadata = _state.sent();
177
241
  if (!(resourceMetadata && resourceMetadata.authorization_servers.length > 0)) return [
178
242
  3,
179
- 3
243
+ 5
180
244
  ];
181
245
  // Found protected resource metadata with authorization servers
182
246
  // Discover the authorization server's metadata (RFC 8414)
@@ -192,66 +256,73 @@ function probeAuthCapabilities(baseUrl) {
192
256
  }
193
257
  return [
194
258
  4,
195
- (0, _rfc9728discoveryts.discoverAuthorizationServerMetadata)(authServerUrl)
259
+ resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported)
196
260
  ];
197
261
  case 2:
198
- authServerMetadata = _state.sent();
199
- if (authServerMetadata) {
200
- // Successfully discovered full OAuth metadata via RFC 9728 โ†’ RFC 8414 chain
201
- supportsDcr = !!authServerMetadata.registration_endpoint;
202
- capabilities = {
203
- supportsDcr: supportsDcr
204
- };
205
- if (authServerMetadata.registration_endpoint) {
206
- capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;
207
- }
208
- if (authServerMetadata.authorization_endpoint) {
209
- capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;
210
- }
211
- if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;
212
- if (authServerMetadata.introspection_endpoint) {
213
- capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;
214
- }
215
- // Prefer resource scopes over auth server scopes
216
- scopes = resourceMetadata.scopes_supported || authServerMetadata.scopes_supported;
217
- if (scopes) capabilities.scopes = scopes;
262
+ capabilities = _state.sent();
263
+ if (capabilities) {
218
264
  return [
219
265
  2,
220
266
  capabilities
221
267
  ];
222
268
  }
223
- _state.label = 3;
269
+ return [
270
+ 4,
271
+ (0, _rfc9728discoveryts.discoverAuthorizationServerIssuer)(baseUrl)
272
+ ];
224
273
  case 3:
274
+ issuer = _state.sent();
275
+ if (!issuer) return [
276
+ 3,
277
+ 5
278
+ ];
279
+ return [
280
+ 4,
281
+ resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported)
282
+ ];
283
+ case 4:
284
+ issuerCapabilities = _state.sent();
285
+ if (issuerCapabilities) return [
286
+ 2,
287
+ issuerCapabilities
288
+ ];
289
+ _state.label = 5;
290
+ case 5:
291
+ return [
292
+ 4,
293
+ (0, _rfc9728discoveryts.discoverAuthorizationServerIssuer)(baseUrl)
294
+ ];
295
+ case 6:
296
+ issuer1 = _state.sent();
297
+ if (!issuer1) return [
298
+ 3,
299
+ 8
300
+ ];
301
+ return [
302
+ 4,
303
+ resolveCapabilitiesFromAuthorizationServer(issuer1)
304
+ ];
305
+ case 7:
306
+ issuerCapabilities1 = _state.sent();
307
+ if (issuerCapabilities1) return [
308
+ 2,
309
+ issuerCapabilities1
310
+ ];
311
+ _state.label = 8;
312
+ case 8:
225
313
  // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin
226
314
  // This handles same-domain OAuth (traditional setup)
227
315
  origin = getOrigin(baseUrl);
228
316
  return [
229
317
  4,
230
- (0, _rfc9728discoveryts.discoverAuthorizationServerMetadata)(origin)
318
+ resolveCapabilitiesFromAuthorizationServer(origin)
319
+ ];
320
+ case 9:
321
+ originCapabilities = _state.sent();
322
+ if (originCapabilities) return [
323
+ 2,
324
+ originCapabilities
231
325
  ];
232
- case 4:
233
- authServerMetadata1 = _state.sent();
234
- if (authServerMetadata1) {
235
- supportsDcr1 = !!authServerMetadata1.registration_endpoint;
236
- capabilities1 = {
237
- supportsDcr: supportsDcr1
238
- };
239
- if (authServerMetadata1.registration_endpoint) {
240
- capabilities1.registrationEndpoint = authServerMetadata1.registration_endpoint;
241
- }
242
- if (authServerMetadata1.authorization_endpoint) {
243
- capabilities1.authorizationEndpoint = authServerMetadata1.authorization_endpoint;
244
- }
245
- if (authServerMetadata1.token_endpoint) capabilities1.tokenEndpoint = authServerMetadata1.token_endpoint;
246
- if (authServerMetadata1.introspection_endpoint) {
247
- capabilities1.introspectionEndpoint = authServerMetadata1.introspection_endpoint;
248
- }
249
- if (authServerMetadata1.scopes_supported) capabilities1.scopes = authServerMetadata1.scopes_supported;
250
- return [
251
- 2,
252
- capabilities1
253
- ];
254
- }
255
326
  // No OAuth metadata found
256
327
  return [
257
328
  2,
@@ -259,7 +330,7 @@ function probeAuthCapabilities(baseUrl) {
259
330
  supportsDcr: false
260
331
  }
261
332
  ];
262
- case 5:
333
+ case 10:
263
334
  _error = _state.sent();
264
335
  // Network error, invalid JSON, or other fetch failure
265
336
  // Gracefully degrade - assume no DCR support
@@ -269,7 +340,7 @@ function probeAuthCapabilities(baseUrl) {
269
340
  supportsDcr: false
270
341
  }
271
342
  ];
272
- case 6:
343
+ case 11:
273
344
  return [
274
345
  2
275
346
  ];
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/capability-discovery.ts"],"sourcesContent":["/**\n * OAuth Server Capability Discovery\n * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata\n */\n\nimport { discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.ts';\nimport type { AuthCapabilities } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // โ†’ 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // โ†’ 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Probe OAuth server capabilities using RFC 9728 โ†’ RFC 8414 discovery chain\n * Returns capabilities including DCR support detection\n *\n * Discovery Strategy:\n * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)\n * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata\n * 3. Fall back to direct RFC 8414 discovery at resource origin\n *\n * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns AuthCapabilities object with discovered endpoints and features\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');\n * if (caps.supportsDcr) {\n * console.log('Registration endpoint:', caps.registrationEndpoint);\n * }\n */\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery\n // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp โ†’ todoist.com)\n const resourceMetadata = await discoverProtectedResourceMetadata(baseUrl);\n\n if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {\n // Found protected resource metadata with authorization servers\n // Discover the authorization server's metadata (RFC 8414)\n const authServerUrl = resourceMetadata.authorization_servers[0];\n if (!authServerUrl) {\n // Array has length > 0 but first element is undefined/null - skip this path\n return { supportsDcr: false };\n }\n const authServerMetadata = await discoverAuthorizationServerMetadata(authServerUrl);\n\n if (authServerMetadata) {\n // Successfully discovered full OAuth metadata via RFC 9728 โ†’ RFC 8414 chain\n const supportsDcr = !!authServerMetadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (authServerMetadata.registration_endpoint) {\n capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;\n }\n if (authServerMetadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;\n }\n if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;\n if (authServerMetadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;\n }\n\n // Prefer resource scopes over auth server scopes\n const scopes = resourceMetadata.scopes_supported || authServerMetadata.scopes_supported;\n if (scopes) capabilities.scopes = scopes;\n\n return capabilities;\n }\n }\n\n // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin\n // This handles same-domain OAuth (traditional setup)\n const origin = getOrigin(baseUrl);\n const authServerMetadata = await discoverAuthorizationServerMetadata(origin);\n\n if (authServerMetadata) {\n const supportsDcr = !!authServerMetadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (authServerMetadata.registration_endpoint) {\n capabilities.registrationEndpoint = authServerMetadata.registration_endpoint;\n }\n if (authServerMetadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = authServerMetadata.authorization_endpoint;\n }\n if (authServerMetadata.token_endpoint) capabilities.tokenEndpoint = authServerMetadata.token_endpoint;\n if (authServerMetadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = authServerMetadata.introspection_endpoint;\n }\n if (authServerMetadata.scopes_supported) capabilities.scopes = authServerMetadata.scopes_supported;\n\n return capabilities;\n }\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["probeAuthCapabilities","getOrigin","url","URL","origin","baseUrl","resourceMetadata","authServerUrl","authServerMetadata","supportsDcr","capabilities","scopes","_error","discoverProtectedResourceMetadata","authorization_servers","length","discoverAuthorizationServerMetadata","registration_endpoint","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","scopes_supported"],"mappings":"AAAA;;;CAGC;;;;+BA0CqBA;;;eAAAA;;;kCAxCiE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGvF;;;;;;;;CAQC,GACD,SAASC,UAAUC,GAAW;IAC5B,IAAI;QACF,OAAO,IAAIC,IAAID,KAAKE,MAAM;IAC5B,EAAE,eAAM;QACN,sDAAsD;QACtD,OAAOF;IACT;AACF;AAqBO,SAAeF,sBAAsBK,OAAe;;YAIjDC,kBAKEC,eAKAC,oBAIEC,aACAC,cAcAC,QASJP,QACAI,qBAGEC,cACAC,eAmBDE;;;;;;;;;;oBA9DkB;;wBAAMC,IAAAA,qDAAiC,EAACR;;;oBAA3DC,mBAAmB;yBAErBA,CAAAA,oBAAoBA,iBAAiBQ,qBAAqB,CAACC,MAAM,GAAG,CAAA,GAApET;;;;oBACF,+DAA+D;oBAC/D,0DAA0D;oBACpDC,gBAAgBD,iBAAiBQ,qBAAqB,CAAC,EAAE;oBAC/D,IAAI,CAACP,eAAe;wBAClB,4EAA4E;wBAC5E;;4BAAO;gCAAEE,aAAa;4BAAM;;oBAC9B;oBAC2B;;wBAAMO,IAAAA,uDAAmC,EAACT;;;oBAA/DC,qBAAqB;oBAE3B,IAAIA,oBAAoB;wBACtB,4EAA4E;wBACtEC,cAAc,CAAC,CAACD,mBAAmBS,qBAAqB;wBACxDP,eAAiC;4BAAED,aAAAA;wBAAY;wBAErD,IAAID,mBAAmBS,qBAAqB,EAAE;4BAC5CP,aAAaQ,oBAAoB,GAAGV,mBAAmBS,qBAAqB;wBAC9E;wBACA,IAAIT,mBAAmBW,sBAAsB,EAAE;4BAC7CT,aAAaU,qBAAqB,GAAGZ,mBAAmBW,sBAAsB;wBAChF;wBACA,IAAIX,mBAAmBa,cAAc,EAAEX,aAAaY,aAAa,GAAGd,mBAAmBa,cAAc;wBACrG,IAAIb,mBAAmBe,sBAAsB,EAAE;4BAC7Cb,aAAac,qBAAqB,GAAGhB,mBAAmBe,sBAAsB;wBAChF;wBAEA,iDAAiD;wBAC3CZ,SAASL,iBAAiBmB,gBAAgB,IAAIjB,mBAAmBiB,gBAAgB;wBACvF,IAAId,QAAQD,aAAaC,MAAM,GAAGA;wBAElC;;4BAAOD;;oBACT;;;oBAGF,wEAAwE;oBACxE,qDAAqD;oBAC/CN,SAASH,UAAUI;oBACE;;wBAAMW,IAAAA,uDAAmC,EAACZ;;;oBAA/DI,sBAAqB;oBAE3B,IAAIA,qBAAoB;wBAChBC,eAAc,CAAC,CAACD,oBAAmBS,qBAAqB;wBACxDP,gBAAiC;4BAAED,aAAAA;wBAAY;wBAErD,IAAID,oBAAmBS,qBAAqB,EAAE;4BAC5CP,cAAaQ,oBAAoB,GAAGV,oBAAmBS,qBAAqB;wBAC9E;wBACA,IAAIT,oBAAmBW,sBAAsB,EAAE;4BAC7CT,cAAaU,qBAAqB,GAAGZ,oBAAmBW,sBAAsB;wBAChF;wBACA,IAAIX,oBAAmBa,cAAc,EAAEX,cAAaY,aAAa,GAAGd,oBAAmBa,cAAc;wBACrG,IAAIb,oBAAmBe,sBAAsB,EAAE;4BAC7Cb,cAAac,qBAAqB,GAAGhB,oBAAmBe,sBAAsB;wBAChF;wBACA,IAAIf,oBAAmBiB,gBAAgB,EAAEf,cAAaC,MAAM,GAAGH,oBAAmBiB,gBAAgB;wBAElG;;4BAAOf;;oBACT;oBAEA,0BAA0B;oBAC1B;;wBAAO;4BAAED,aAAa;wBAAM;;;oBACrBG;oBACP,sDAAsD;oBACtD,6CAA6C;oBAC7C;;wBAAO;4BAAEH,aAAa;wBAAM;;;;;;;;IAEhC"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/capability-discovery.ts"],"sourcesContent":["/**\n * OAuth Server Capability Discovery\n * Probes RFC 9728 (Protected Resource) and RFC 8414 (Authorization Server) metadata\n */\n\nimport { discoverAuthorizationServerIssuer, discoverAuthorizationServerMetadata, discoverProtectedResourceMetadata } from './rfc9728-discovery.ts';\nimport type { AuthCapabilities, AuthorizationServerMetadata } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // โ†’ 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // โ†’ 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Probe OAuth server capabilities using RFC 9728 โ†’ RFC 8414 discovery chain\n * Returns capabilities including DCR support detection\n *\n * Discovery Strategy:\n * 1. Try RFC 9728 Protected Resource Metadata (supports cross-domain OAuth)\n * 2. If found, use first authorization_server to discover RFC 8414 Authorization Server Metadata\n * 3. Fall back to direct RFC 8414 discovery at resource origin\n *\n * @param baseUrl - Base URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns AuthCapabilities object with discovered endpoints and features\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const caps = await probeAuthCapabilities('https://ai.todoist.net/mcp');\n * if (caps.supportsDcr) {\n * console.log('Registration endpoint:', caps.registrationEndpoint);\n * }\n */\nfunction buildCapabilities(metadata: AuthorizationServerMetadata, scopes?: string[]): AuthCapabilities {\n const supportsDcr = !!metadata.registration_endpoint;\n const capabilities: AuthCapabilities = { supportsDcr };\n\n if (metadata.registration_endpoint) {\n capabilities.registrationEndpoint = metadata.registration_endpoint;\n }\n if (metadata.authorization_endpoint) {\n capabilities.authorizationEndpoint = metadata.authorization_endpoint;\n }\n if (metadata.token_endpoint) capabilities.tokenEndpoint = metadata.token_endpoint;\n if (metadata.introspection_endpoint) {\n capabilities.introspectionEndpoint = metadata.introspection_endpoint;\n }\n\n if (scopes && scopes.length > 0) {\n capabilities.scopes = scopes;\n } else if (metadata.scopes_supported) {\n capabilities.scopes = metadata.scopes_supported;\n }\n\n return capabilities;\n}\n\nasync function resolveCapabilitiesFromAuthorizationServer(authServerUrl: string, scopes?: string[]): Promise<AuthCapabilities | null> {\n const metadata = await discoverAuthorizationServerMetadata(authServerUrl);\n if (!metadata) return null;\n return buildCapabilities(metadata, scopes);\n}\n\nexport async function probeAuthCapabilities(baseUrl: string): Promise<AuthCapabilities> {\n try {\n // Strategy 1: Try RFC 9728 Protected Resource Metadata discovery\n // This handles cross-domain OAuth (e.g., Todoist: ai.todoist.net/mcp โ†’ todoist.com)\n const resourceMetadata = await discoverProtectedResourceMetadata(baseUrl);\n\n if (resourceMetadata && resourceMetadata.authorization_servers.length > 0) {\n // Found protected resource metadata with authorization servers\n // Discover the authorization server's metadata (RFC 8414)\n const authServerUrl = resourceMetadata.authorization_servers[0];\n if (!authServerUrl) {\n // Array has length > 0 but first element is undefined/null - skip this path\n return { supportsDcr: false };\n }\n const capabilities = await resolveCapabilitiesFromAuthorizationServer(authServerUrl, resourceMetadata.scopes_supported);\n if (capabilities) {\n return capabilities;\n }\n\n const issuer = await discoverAuthorizationServerIssuer(baseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer, resourceMetadata.scopes_supported);\n if (issuerCapabilities) return issuerCapabilities;\n }\n }\n\n const issuer = await discoverAuthorizationServerIssuer(baseUrl);\n if (issuer) {\n const issuerCapabilities = await resolveCapabilitiesFromAuthorizationServer(issuer);\n if (issuerCapabilities) return issuerCapabilities;\n }\n\n // Strategy 2: Fall back to direct RFC 8414 discovery at resource origin\n // This handles same-domain OAuth (traditional setup)\n const origin = getOrigin(baseUrl);\n const originCapabilities = await resolveCapabilitiesFromAuthorizationServer(origin);\n if (originCapabilities) return originCapabilities;\n\n // No OAuth metadata found\n return { supportsDcr: false };\n } catch (_error) {\n // Network error, invalid JSON, or other fetch failure\n // Gracefully degrade - assume no DCR support\n return { supportsDcr: false };\n }\n}\n"],"names":["probeAuthCapabilities","getOrigin","url","URL","origin","buildCapabilities","metadata","scopes","supportsDcr","registration_endpoint","capabilities","registrationEndpoint","authorization_endpoint","authorizationEndpoint","token_endpoint","tokenEndpoint","introspection_endpoint","introspectionEndpoint","length","scopes_supported","resolveCapabilitiesFromAuthorizationServer","authServerUrl","discoverAuthorizationServerMetadata","baseUrl","resourceMetadata","issuer","issuerCapabilities","originCapabilities","_error","discoverProtectedResourceMetadata","authorization_servers","discoverAuthorizationServerIssuer"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/index.ts"],"sourcesContent":["/**\n * Authentication Module\n * Exports public API for OAuth authentication only (DCR moved to ../dcr/)\n */\n\nexport { probeAuthCapabilities } from './capability-discovery.ts';\nexport { InteractiveOAuthFlow } from './interactive-oauth-flow.ts';\nexport type { OAuthCallbackListenerOptions } from './oauth-callback-listener.ts';\nexport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nexport type { AuthCapabilities, CallbackResult, OAuthFlowOptions, TokenSet } from './types.ts';\n"],"names":["InteractiveOAuthFlow","OAuthCallbackListener","probeAuthCapabilities"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QAGQA;eAAAA,4CAAoB;;QAEpBC;eAAAA,8CAAqB;;QAHrBC;eAAAA,4CAAqB;;;qCAAQ;sCACD;uCAEC"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/index.ts"],"sourcesContent":["/**\n * Authentication Module\n * Exports public API for OAuth authentication only (DCR moved to ../dcr/)\n */\n\nexport { probeAuthCapabilities } from './capability-discovery.ts';\nexport { InteractiveOAuthFlow } from './interactive-oauth-flow.ts';\nexport type { OAuthCallbackListenerOptions } from './oauth-callback-listener.ts';\nexport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nexport type { AuthCapabilities, CallbackResult, OAuthFlowOptions, TokenSet } from './types.ts';\n"],"names":["InteractiveOAuthFlow","OAuthCallbackListener","probeAuthCapabilities"],"mappings":"AAAA;;;CAGC;;;;;;;;;;;QAGQA;eAAAA,4CAAoB;;QAEpBC;eAAAA,8CAAqB;;QAHrBC;eAAAA,4CAAqB;;;qCAAQ;sCACD;uCAEC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/interactive-oauth-flow.ts"],"sourcesContent":["/**\n * OAuth Authorization Flow Handler\n * Manages browser-based OAuth flows and token exchange with PKCE support\n */\n\nimport * as child_process from 'node:child_process';\nimport { logger as defaultLogger } from '../utils/logger.ts';\nimport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nimport { generatePkce } from './pkce.ts';\nimport type { OAuthFlowOptions, PkceParams, TokenSet } from './types.ts';\n\n/**\n * OAuth token response from token endpoint\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * InteractiveOAuthFlow manages the complete OAuth authorization code flow\n */\nexport class InteractiveOAuthFlow {\n /**\n * Perform OAuth authorization code flow\n *\n * @param authorizationEndpoint - OAuth authorization endpoint URL\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @param options - Flow options (port is required - use get-port to find available port)\n * @returns Token set with access and refresh tokens\n *\n * @throws Error if flow fails or times out\n *\n * @example\n * import getPort from 'get-port';\n *\n * const flow = new InteractiveOAuthFlow();\n * const port = await getPort();\n * const tokens = await flow.performAuthFlow(\n * 'https://example.com/oauth/authorize',\n * 'https://example.com/oauth/token',\n * 'client-id',\n * 'client-secret',\n * { port, scopes: ['read', 'write'] }\n * );\n */\n async performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet> {\n const logger = options.logger ?? defaultLogger;\n const callbackListener = new OAuthCallbackListener({ port: options.port, logger });\n\n // Generate PKCE parameters if requested (RFC 7636)\n let pkce: PkceParams | undefined;\n if (options.pkce) {\n logger.debug('๐Ÿ” Generating PKCE parameters...');\n pkce = await generatePkce();\n }\n\n try {\n // Start callback server\n await callbackListener.start();\n\n // Build redirect URI\n const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;\n\n // Build authorization URL\n const authUrl = new URL(authorizationEndpoint);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n\n if (options.scopes && options.scopes.length > 0) {\n authUrl.searchParams.set('scope', options.scopes.join(' '));\n }\n\n // Add resource parameter if specified (RFC 8707)\n if (options.resource) {\n authUrl.searchParams.set('resource', options.resource);\n }\n\n // Add PKCE parameters if generated (RFC 7636)\n if (pkce) {\n authUrl.searchParams.set('code_challenge', pkce.codeChallenge);\n authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);\n }\n\n // Open browser or print URL for headless mode\n if (options.headless) {\n logger.info('๐Ÿ”— Please visit this URL to authorize:');\n logger.info(authUrl.toString());\n logger.info('Waiting for callback...');\n } else {\n logger.debug('๐ŸŒ Opening browser for OAuth authorization...');\n // Try to open browser (requires 'open' package or native command)\n await this.openBrowser(authUrl.toString());\n }\n\n // Wait for callback with timeout\n const timeout = options.timeout || (options.headless ? 60000 : 300000);\n const result = await callbackListener.waitForCallback(timeout);\n\n // Exchange authorization code for tokens (with PKCE verifier if used)\n const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, pkce?.codeVerifier);\n\n return tokens;\n } catch (error) {\n logger.error('โŒ OAuth flow failed:', error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n // Always close callback server\n await callbackListener.stop();\n }\n }\n\n /**\n * Exchange authorization code for access and refresh tokens\n * @param codeVerifier - Optional PKCE code verifier (RFC 7636)\n */\n private async exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string): Promise<TokenSet> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n // Add PKCE code verifier if provided (RFC 7636)\n if (codeVerifier) {\n params.set('code_verifier', codeVerifier);\n }\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: params,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Refresh access token using refresh token\n *\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param refreshToken - Refresh token from previous token set\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @returns New token set with refreshed access token\n *\n * @throws Error if refresh fails\n */\n async refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string): Promise<TokenSet> {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token refresh response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || refreshToken, // Reuse old refresh token if not provided\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Open browser to authorization URL\n * Uses platform-specific command to open default browser\n */\n private async openBrowser(url: string): Promise<void> {\n // Determine platform-specific command\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === 'darwin') {\n command = 'open';\n args = [url];\n } else if (platform === 'win32') {\n command = 'cmd';\n args = ['/c', 'start', url];\n } else {\n // Linux and others\n command = 'xdg-open';\n args = [url];\n }\n\n // Spawn browser process\n const child = child_process.spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.unref();\n }\n}\n"],"names":["InteractiveOAuthFlow","performAuthFlow","authorizationEndpoint","tokenEndpoint","clientId","clientSecret","options","logger","callbackListener","pkce","redirectUri","authUrl","timeout","result","tokens","error","defaultLogger","OAuthCallbackListener","port","debug","generatePkce","start","URL","searchParams","set","scopes","length","join","resource","codeChallenge","codeChallengeMethod","headless","info","toString","openBrowser","waitForCallback","exchangeCodeForTokens","code","codeVerifier","Error","message","String","stop","params","response","errorText","data","tokenSet","URLSearchParams","grant_type","redirect_uri","client_id","client_secret","fetch","method","headers","Accept","Connection","body","ok","text","status","json","access_token","accessToken","refreshToken","refresh_token","expiresAt","Date","now","expires_in","scope","split","refreshTokens","url","platform","command","args","child","process","child_process","spawn","detached","stdio","unref"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/interactive-oauth-flow.ts"],"sourcesContent":["/**\n * OAuth Authorization Flow Handler\n * Manages browser-based OAuth flows and token exchange with PKCE support\n */\n\nimport * as child_process from 'node:child_process';\nimport { logger as defaultLogger } from '../utils/logger.ts';\nimport { OAuthCallbackListener } from './oauth-callback-listener.ts';\nimport { generatePkce } from './pkce.ts';\nimport type { OAuthFlowOptions, PkceParams, TokenSet } from './types.ts';\n\n/**\n * OAuth token response from token endpoint\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * InteractiveOAuthFlow manages the complete OAuth authorization code flow\n */\nexport class InteractiveOAuthFlow {\n /**\n * Perform OAuth authorization code flow\n *\n * @param authorizationEndpoint - OAuth authorization endpoint URL\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @param options - Flow options (port is required - use get-port to find available port)\n * @returns Token set with access and refresh tokens\n *\n * @throws Error if flow fails or times out\n *\n * @example\n * import getPort from 'get-port';\n *\n * const flow = new InteractiveOAuthFlow();\n * const port = await getPort();\n * const tokens = await flow.performAuthFlow(\n * 'https://example.com/oauth/authorize',\n * 'https://example.com/oauth/token',\n * 'client-id',\n * 'client-secret',\n * { port, scopes: ['read', 'write'] }\n * );\n */\n async performAuthFlow(authorizationEndpoint: string, tokenEndpoint: string, clientId: string, clientSecret: string, options: OAuthFlowOptions): Promise<TokenSet> {\n const logger = options.logger ?? defaultLogger;\n const callbackListener = new OAuthCallbackListener({ port: options.port, logger });\n\n // Generate PKCE parameters if requested (RFC 7636)\n let pkce: PkceParams | undefined;\n if (options.pkce) {\n logger.debug('๐Ÿ” Generating PKCE parameters...');\n pkce = await generatePkce();\n }\n\n try {\n // Start callback server\n await callbackListener.start();\n\n // Build redirect URI\n const redirectUri = options.redirectUri || `http://localhost:${options.port}/callback`;\n\n // Build authorization URL\n const authUrl = new URL(authorizationEndpoint);\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n\n if (options.scopes && options.scopes.length > 0) {\n authUrl.searchParams.set('scope', options.scopes.join(' '));\n }\n\n // Add resource parameter if specified (RFC 8707)\n if (options.resource) {\n authUrl.searchParams.set('resource', options.resource);\n }\n\n // Add PKCE parameters if generated (RFC 7636)\n if (pkce) {\n authUrl.searchParams.set('code_challenge', pkce.codeChallenge);\n authUrl.searchParams.set('code_challenge_method', pkce.codeChallengeMethod);\n }\n\n // Open browser or print URL for headless mode\n if (options.headless) {\n logger.info('๐Ÿ”— Please visit this URL to authorize:');\n logger.info(authUrl.toString());\n logger.info('Waiting for callback...');\n } else {\n logger.debug('๐ŸŒ Opening browser for OAuth authorization...');\n // Try to open browser (requires 'open' package or native command)\n await this.openBrowser(authUrl.toString());\n }\n\n // Wait for callback with timeout\n const timeout = options.timeout || (options.headless ? 60000 : 300000);\n const result = await callbackListener.waitForCallback(timeout);\n\n // Exchange authorization code for tokens (with PKCE verifier if used)\n const tokens = await this.exchangeCodeForTokens(tokenEndpoint, result.code, clientId, clientSecret, redirectUri, pkce?.codeVerifier);\n\n return tokens;\n } catch (error) {\n logger.error('โŒ OAuth flow failed:', error instanceof Error ? error.message : String(error));\n throw error;\n } finally {\n // Always close callback server\n await callbackListener.stop();\n }\n }\n\n /**\n * Exchange authorization code for access and refresh tokens\n * @param codeVerifier - Optional PKCE code verifier (RFC 7636)\n */\n private async exchangeCodeForTokens(tokenEndpoint: string, code: string, clientId: string, clientSecret: string, redirectUri: string, codeVerifier?: string): Promise<TokenSet> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: redirectUri,\n client_id: clientId,\n client_secret: clientSecret,\n });\n\n // Add PKCE code verifier if provided (RFC 7636)\n if (codeVerifier) {\n params.set('code_verifier', codeVerifier);\n }\n\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: params,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || '',\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Refresh access token using refresh token\n *\n * @param tokenEndpoint - OAuth token endpoint URL\n * @param refreshToken - Refresh token from previous token set\n * @param clientId - OAuth client ID\n * @param clientSecret - OAuth client secret\n * @returns New token set with refreshed access token\n *\n * @throws Error if refresh fails\n */\n async refreshTokens(tokenEndpoint: string, refreshToken: string, clientId: string, clientSecret: string): Promise<TokenSet> {\n const response = await fetch(tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Accept: 'application/json',\n Connection: 'close',\n },\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorText}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n\n if (!data.access_token) {\n throw new Error('Token refresh response missing access_token');\n }\n\n const tokenSet: TokenSet = {\n accessToken: data.access_token,\n refreshToken: data.refresh_token || refreshToken, // Reuse old refresh token if not provided\n expiresAt: Date.now() + data.expires_in * 1000,\n clientId,\n clientSecret,\n };\n\n if (data.scope) {\n tokenSet.scopes = data.scope.split(' ');\n }\n\n return tokenSet;\n }\n\n /**\n * Open browser to authorization URL\n * Uses platform-specific command to open default browser\n */\n private async openBrowser(url: string): Promise<void> {\n // Determine platform-specific command\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === 'darwin') {\n command = 'open';\n args = [url];\n } else if (platform === 'win32') {\n command = 'cmd';\n args = ['/c', 'start', url];\n } else {\n // Linux and others\n command = 'xdg-open';\n args = [url];\n }\n\n // Spawn browser process\n const child = child_process.spawn(command, args, {\n detached: true,\n stdio: 'ignore',\n });\n\n child.unref();\n }\n}\n"],"names":["InteractiveOAuthFlow","performAuthFlow","authorizationEndpoint","tokenEndpoint","clientId","clientSecret","options","logger","callbackListener","pkce","redirectUri","authUrl","timeout","result","tokens","error","defaultLogger","OAuthCallbackListener","port","debug","generatePkce","start","URL","searchParams","set","scopes","length","join","resource","codeChallenge","codeChallengeMethod","headless","info","toString","openBrowser","waitForCallback","exchangeCodeForTokens","code","codeVerifier","Error","message","String","stop","params","response","errorText","data","tokenSet","URLSearchParams","grant_type","redirect_uri","client_id","client_secret","fetch","method","headers","Accept","Connection","body","ok","text","status","json","access_token","accessToken","refreshToken","refresh_token","expiresAt","Date","now","expires_in","scope","split","refreshTokens","url","platform","command","args","child","process","child_process","spawn","detached","stdio","unref"],"mappings":"AAAA;;;CAGC;;;;+BAsBYA;;;eAAAA;;;yEApBkB;wBACS;uCACF;sBACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBtB,IAAA,AAAMA,qCAAN;;aAAMA;gCAAAA;;iBAAAA;IACX;;;;;;;;;;;;;;;;;;;;;;;;GAwBC,GACD,OAAMC,eAiEL,GAjED,SAAMA,gBAAgBC,qBAA6B,EAAEC,aAAqB,EAAEC,QAAgB,EAAEC,YAAoB,EAAEC,OAAyB;;gBAC5HA,iBAATC,QACAC,kBAGFC,MAWIC,aAGAC,SAgCAC,SACAC,QAGAC,QAGCC;;;;wBAzDHR,UAASD,kBAAAA,QAAQC,MAAM,cAAdD,6BAAAA,kBAAkBU,gBAAa;wBACxCR,mBAAmB,IAAIS,8CAAqB,CAAC;4BAAEC,MAAMZ,QAAQY,IAAI;4BAAEX,QAAAA;wBAAO;6BAI5ED,QAAQG,IAAI,EAAZH;;;;wBACFC,OAAOY,KAAK,CAAC;wBACN;;4BAAMC,IAAAA,oBAAY;;;wBAAzBX,OAAO;;;;;;;;;wBAIP,wBAAwB;wBACxB;;4BAAMD,iBAAiBa,KAAK;;;wBAA5B;wBAEA,qBAAqB;wBACfX,cAAcJ,QAAQI,WAAW,IAAI,AAAC,oBAAgC,OAAbJ,QAAQY,IAAI,EAAC;wBAE5E,0BAA0B;wBACpBP,UAAU,IAAIW,IAAIpB;wBACxBS,QAAQY,YAAY,CAACC,GAAG,CAAC,aAAapB;wBACtCO,QAAQY,YAAY,CAACC,GAAG,CAAC,gBAAgBd;wBACzCC,QAAQY,YAAY,CAACC,GAAG,CAAC,iBAAiB;wBAE1C,IAAIlB,QAAQmB,MAAM,IAAInB,QAAQmB,MAAM,CAACC,MAAM,GAAG,GAAG;4BAC/Cf,QAAQY,YAAY,CAACC,GAAG,CAAC,SAASlB,QAAQmB,MAAM,CAACE,IAAI,CAAC;wBACxD;wBAEA,iDAAiD;wBACjD,IAAIrB,QAAQsB,QAAQ,EAAE;4BACpBjB,QAAQY,YAAY,CAACC,GAAG,CAAC,YAAYlB,QAAQsB,QAAQ;wBACvD;wBAEA,8CAA8C;wBAC9C,IAAInB,MAAM;4BACRE,QAAQY,YAAY,CAACC,GAAG,CAAC,kBAAkBf,KAAKoB,aAAa;4BAC7DlB,QAAQY,YAAY,CAACC,GAAG,CAAC,yBAAyBf,KAAKqB,mBAAmB;wBAC5E;6BAGIxB,QAAQyB,QAAQ,EAAhBzB;;;;wBACFC,OAAOyB,IAAI,CAAC;wBACZzB,OAAOyB,IAAI,CAACrB,QAAQsB,QAAQ;wBAC5B1B,OAAOyB,IAAI,CAAC;;;;;;wBAEZzB,OAAOY,KAAK,CAAC;wBACb,kEAAkE;wBAClE;;4BAAM,IAAI,CAACe,WAAW,CAACvB,QAAQsB,QAAQ;;;wBAAvC;;;wBAGF,iCAAiC;wBAC3BrB,UAAUN,QAAQM,OAAO,IAAKN,CAAAA,QAAQyB,QAAQ,GAAG,QAAQ,MAAK;wBACrD;;4BAAMvB,iBAAiB2B,eAAe,CAACvB;;;wBAAhDC,SAAS;wBAGA;;4BAAM,IAAI,CAACuB,qBAAqB,CAACjC,eAAeU,OAAOwB,IAAI,EAAEjC,UAAUC,cAAcK,aAAaD,iBAAAA,2BAAAA,KAAM6B,YAAY;;;wBAA7HxB,SAAS;wBAEf;;4BAAOA;;;wBACAC;wBACPR,OAAOQ,KAAK,CAAC,wBAAwBA,AAAK,YAALA,OAAiBwB,SAAQxB,MAAMyB,OAAO,GAAGC,OAAO1B;wBACrF,MAAMA;;wBAEN,+BAA+B;wBAC/B;;4BAAMP,iBAAiBkC,IAAI;;;wBAA3B;;;;;;;;;;QAEJ;;IAEA;;;GAGC,GACD,OAAcN,qBAgDb,GAhDD,SAAcA,sBAAsBjC,aAAqB,EAAEkC,IAAY,EAAEjC,QAAgB,EAAEC,YAAoB,EAAEK,WAAmB,EAAE4B,YAAqB;;gBACnJK,QAaAC,UAWEC,WAIFC,MAMAC;;;;wBAlCAJ,SAAS,IAAIK,gBAAgB;4BACjCC,YAAY;4BACZZ,MAAAA;4BACAa,cAAcxC;4BACdyC,WAAW/C;4BACXgD,eAAe/C;wBACjB;wBAEA,gDAAgD;wBAChD,IAAIiC,cAAc;4BAChBK,OAAOnB,GAAG,CAAC,iBAAiBc;wBAC9B;wBAEiB;;4BAAMe,MAAMlD,eAAe;gCAC1CmD,QAAQ;gCACRC,SAAS;oCACP,gBAAgB;oCAChBC,QAAQ;oCACRC,YAAY;gCACd;gCACAC,MAAMf;4BACR;;;wBARMC,WAAW;6BAUb,CAACA,SAASe,EAAE,EAAZ;;;;wBACgB;;4BAAMf,SAASgB,IAAI;;;wBAA/Bf,YAAY;wBAClB,MAAM,IAAIN,MAAM,AAAC,0BAA8CM,OAArBD,SAASiB,MAAM,EAAC,OAAe,OAAVhB;;wBAGnD;;4BAAMD,SAASkB,IAAI;;;wBAA3BhB,OAAQ;wBAEd,IAAI,CAACA,KAAKiB,YAAY,EAAE;4BACtB,MAAM,IAAIxB,MAAM;wBAClB;wBAEMQ,WAAqB;4BACzBiB,aAAalB,KAAKiB,YAAY;4BAC9BE,cAAcnB,KAAKoB,aAAa,IAAI;4BACpCC,WAAWC,KAAKC,GAAG,KAAKvB,KAAKwB,UAAU,GAAG;4BAC1ClE,UAAAA;4BACAC,cAAAA;wBACF;wBAEA,IAAIyC,KAAKyB,KAAK,EAAE;4BACdxB,SAAStB,MAAM,GAAGqB,KAAKyB,KAAK,CAACC,KAAK,CAAC;wBACrC;wBAEA;;4BAAOzB;;;;QACT;;IAEA;;;;;;;;;;GAUC,GACD,OAAM0B,aAwCL,GAxCD,SAAMA,cAActE,aAAqB,EAAE8D,YAAoB,EAAE7D,QAAgB,EAAEC,YAAoB;;gBAC/FuC,UAgBEC,WAIFC,MAMAC;;;;wBA1BW;;4BAAMM,MAAMlD,eAAe;gCAC1CmD,QAAQ;gCACRC,SAAS;oCACP,gBAAgB;oCAChBC,QAAQ;oCACRC,YAAY;gCACd;gCACAC,MAAM,IAAIV,gBAAgB;oCACxBC,YAAY;oCACZiB,eAAeD;oCACfd,WAAW/C;oCACXgD,eAAe/C;gCACjB;4BACF;;;wBAbMuC,WAAW;6BAeb,CAACA,SAASe,EAAE,EAAZ;;;;wBACgB;;4BAAMf,SAASgB,IAAI;;;wBAA/Bf,YAAY;wBAClB,MAAM,IAAIN,MAAM,AAAC,yBAA6CM,OAArBD,SAASiB,MAAM,EAAC,OAAe,OAAVhB;;wBAGlD;;4BAAMD,SAASkB,IAAI;;;wBAA3BhB,OAAQ;wBAEd,IAAI,CAACA,KAAKiB,YAAY,EAAE;4BACtB,MAAM,IAAIxB,MAAM;wBAClB;wBAEMQ,WAAqB;4BACzBiB,aAAalB,KAAKiB,YAAY;4BAC9BE,cAAcnB,KAAKoB,aAAa,IAAID;4BACpCE,WAAWC,KAAKC,GAAG,KAAKvB,KAAKwB,UAAU,GAAG;4BAC1ClE,UAAAA;4BACAC,cAAAA;wBACF;wBAEA,IAAIyC,KAAKyB,KAAK,EAAE;4BACdxB,SAAStB,MAAM,GAAGqB,KAAKyB,KAAK,CAACC,KAAK,CAAC;wBACrC;wBAEA;;4BAAOzB;;;;QACT;;IAEA;;;GAGC,GACD,OAAcb,WAyBb,GAzBD,SAAcA,YAAYwC,GAAW;;gBAE7BC,UACFC,SACAC,MAeEC;;gBAlBN,sCAAsC;gBAChCH,WAAWI,QAAQJ,QAAQ;gBAIjC,IAAIA,aAAa,UAAU;oBACzBC,UAAU;oBACVC;wBAAQH;;gBACV,OAAO,IAAIC,aAAa,SAAS;oBAC/BC,UAAU;oBACVC;wBAAQ;wBAAM;wBAASH;;gBACzB,OAAO;oBACL,mBAAmB;oBACnBE,UAAU;oBACVC;wBAAQH;;gBACV;gBAEA,wBAAwB;gBAClBI,QAAQE,mBAAcC,KAAK,CAACL,SAASC,MAAM;oBAC/CK,UAAU;oBACVC,OAAO;gBACT;gBAEAL,MAAMM,KAAK;;;;;QACb;;WArOWpF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/oauth-callback-listener.ts"],"sourcesContent":["/**\n * OAuth Callback Server for CLI Authentication\n * Listens for OAuth authorization callbacks and captures authorization code\n */\n\nimport http from 'node:http';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport type { CallbackResult } from './types.ts';\n\nexport interface OAuthCallbackListenerOptions {\n /** Port to listen on (required - use get-port package to find available port) */\n port: number;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * OAuthCallbackListener handles OAuth redirect callbacks\n * Starts a temporary HTTP server to receive authorization code\n *\n * Note: Caller is responsible for finding an available port using get-port package\n */\nexport class OAuthCallbackListener {\n private server: http.Server | undefined;\n private resolveCallback?: (result: CallbackResult) => void;\n private rejectCallback?: (error: Error) => void;\n private timeout: NodeJS.Timeout | undefined;\n private port: number;\n private logger: Logger;\n\n constructor(options: OAuthCallbackListenerOptions) {\n this.port = options.port;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Start the callback server\n * Fails fast if port is already in use - caller should use get-port to find available port\n */\n async start(): Promise<void> {\n await this.listen(this.port);\n this.logger.debug(`โœ… Callback server listening on http://localhost:${this.port}/callback`);\n }\n\n /**\n * Listen on a specific port\n */\n private listen(port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = http.createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n this.server.on('error', (error) => {\n reject(error);\n });\n\n this.server.listen(port, () => {\n resolve();\n });\n });\n }\n\n /**\n * Handle incoming HTTP requests\n */\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || '', `http://localhost:${this.port}`);\n\n if (url.pathname === '/callback') {\n this.handleCallback(url, res);\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n }\n\n /**\n * Handle OAuth callback\n */\n private handleCallback(url: URL, res: http.ServerResponse): void {\n const code = url.searchParams.get('code');\n const state = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n // Handle OAuth errors\n if (error) {\n const errorMessage = errorDescription ? `${error}: ${errorDescription}` : error;\n\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Authorization Failed</h1>\n <p>${errorMessage}</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error(errorMessage));\n }\n return;\n }\n\n // Validate code parameter\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Invalid Callback</h1>\n <p>Missing authorization code</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error('Missing authorization code'));\n }\n return;\n }\n\n // Success - send confirmation page\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n </head>\n <body>\n <h1>Authorization Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n <script>setTimeout(() => window.close(), 2000);</script>\n </body>\n </html>\n `);\n\n // Resolve the promise with authorization code\n if (this.resolveCallback) {\n const result: CallbackResult = { code };\n if (state) {\n result.state = state;\n }\n this.resolveCallback(result);\n }\n }\n\n /**\n * Wait for OAuth callback with timeout\n */\n async waitForCallback(timeoutMs = 300000): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n this.resolveCallback = resolve;\n this.rejectCallback = reject;\n\n // Set timeout to prevent hanging forever\n this.timeout = setTimeout(() => {\n reject(new Error(`Authorization timeout - no callback received within ${timeoutMs / 1000} seconds`));\n this.stop();\n }, timeoutMs);\n });\n }\n\n /**\n * Stop the callback server and close\n */\n async stop(): Promise<void> {\n // Clear the timeout\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n\n // Close the server\n if (this.server) {\n await new Promise<void>((resolve) => {\n this.server?.close(() => {\n this.logger.debug('๐Ÿ”’ Callback server closed');\n resolve();\n });\n });\n this.server = undefined;\n }\n }\n\n /**\n * Get the callback URL for this server\n */\n getCallbackUrl(): string {\n if (!this.port) {\n throw new Error('Server not started - call start() first');\n }\n return `http://localhost:${this.port}/callback`;\n }\n}\n"],"names":["OAuthCallbackListener","options","port","logger","defaultLogger","start","listen","debug","Promise","resolve","reject","server","http","createServer","req","res","handleRequest","on","error","url","URL","pathname","handleCallback","writeHead","end","code","searchParams","get","state","errorDescription","errorMessage","rejectCallback","Error","resolveCallback","result","waitForCallback","timeoutMs","timeout","setTimeout","stop","clearTimeout","undefined","close","getCallbackUrl"],"mappings":"AAAA;;;CAGC;;;;+BAmBYA;;;eAAAA;;;+DAjBI;wBACoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgB9C,IAAA,AAAMA,sCAAN;;aAAMA,sBAQCC,OAAqC;gCARtCD;YAUKC;QADd,IAAI,CAACC,IAAI,GAAGD,QAAQC,IAAI;QACxB,IAAI,CAACC,MAAM,IAAGF,kBAAAA,QAAQE,MAAM,cAAdF,6BAAAA,kBAAkBG,gBAAa;;iBAVpCJ;IAaX;;;GAGC,GACD,OAAMK,KAGL,GAHD,SAAMA;;;;;wBACJ;;4BAAM,IAAI,CAACC,MAAM,CAAC,IAAI,CAACJ,IAAI;;;wBAA3B;wBACA,IAAI,CAACC,MAAM,CAACI,KAAK,CAAC,AAAC,mDAA4D,OAAV,IAAI,CAACL,IAAI,EAAC;;;;;;QACjF;;IAEA;;GAEC,GACD,OAAQI,MAcP,GAdD,SAAQA,OAAOJ,IAAY;;QACzB,OAAO,IAAIM,QAAQ,SAACC,SAASC;YAC3B,MAAKC,MAAM,GAAGC,iBAAI,CAACC,YAAY,CAAC,SAACC,KAAKC;gBACpC,MAAKC,aAAa,CAACF,KAAKC;YAC1B;YAEA,MAAKJ,MAAM,CAACM,EAAE,CAAC,SAAS,SAACC;gBACvBR,OAAOQ;YACT;YAEA,MAAKP,MAAM,CAACL,MAAM,CAACJ,MAAM;gBACvBO;YACF;QACF;IACF;IAEA;;GAEC,GACD,OAAQO,aASP,GATD,SAAQA,cAAcF,GAAyB,EAAEC,GAAwB;QACvE,IAAMI,MAAM,IAAIC,IAAIN,IAAIK,GAAG,IAAI,IAAI,AAAC,oBAA6B,OAAV,IAAI,CAACjB,IAAI;QAEhE,IAAIiB,IAAIE,QAAQ,KAAK,aAAa;YAChC,IAAI,CAACC,cAAc,CAACH,KAAKJ;QAC3B,OAAO;YACLA,IAAIQ,SAAS,CAAC,KAAK;gBAAE,gBAAgB;YAAa;YAClDR,IAAIS,GAAG,CAAC;QACV;IACF;IAEA;;GAEC,GACD,OAAQF,cAqEP,GArED,SAAQA,eAAeH,GAAQ,EAAEJ,GAAwB;QACvD,IAAMU,OAAON,IAAIO,YAAY,CAACC,GAAG,CAAC;QAClC,IAAMC,QAAQT,IAAIO,YAAY,CAACC,GAAG,CAAC;QACnC,IAAMT,QAAQC,IAAIO,YAAY,CAACC,GAAG,CAAC;QACnC,IAAME,mBAAmBV,IAAIO,YAAY,CAACC,GAAG,CAAC;QAE9C,sBAAsB;QACtB,IAAIT,OAAO;YACT,IAAMY,eAAeD,mBAAmB,AAAC,GAAYA,OAAVX,OAAM,MAAqB,OAAjBW,oBAAqBX;YAE1EH,IAAIQ,SAAS,CAAC,KAAK;gBAAE,gBAAgB;YAAY;YACjDR,IAAIS,GAAG,CAAC,AAAC,iGAIe,OAAbM,cAAa;YAMxB,IAAI,IAAI,CAACC,cAAc,EAAE;gBACvB,IAAI,CAACA,cAAc,CAAC,IAAIC,MAAMF;YAChC;YACA;QACF;QAEA,0BAA0B;QAC1B,IAAI,CAACL,MAAM;YACTV,IAAIQ,SAAS,CAAC,KAAK;gBAAE,gBAAgB;YAAY;YACjDR,IAAIS,GAAG,CAAC;YAUR,IAAI,IAAI,CAACO,cAAc,EAAE;gBACvB,IAAI,CAACA,cAAc,CAAC,IAAIC,MAAM;YAChC;YACA;QACF;QAEA,mCAAmC;QACnCjB,IAAIQ,SAAS,CAAC,KAAK;YAAE,gBAAgB;QAA2B;QAChER,IAAIS,GAAG,CAAC;QAaR,8CAA8C;QAC9C,IAAI,IAAI,CAACS,eAAe,EAAE;YACxB,IAAMC,SAAyB;gBAAET,MAAAA;YAAK;YACtC,IAAIG,OAAO;gBACTM,OAAON,KAAK,GAAGA;YACjB;YACA,IAAI,CAACK,eAAe,CAACC;QACvB;IACF;IAEA;;GAEC,GACD,OAAMC,eAWL,GAXD,SAAMA;YAAgBC,YAAAA,iEAAY;;;;;gBAChC;;oBAAO,IAAI5B,QAAQ,SAACC,SAASC;wBAC3B,MAAKuB,eAAe,GAAGxB;wBACvB,MAAKsB,cAAc,GAAGrB;wBAEtB,yCAAyC;wBACzC,MAAK2B,OAAO,GAAGC,WAAW;4BACxB5B,OAAO,IAAIsB,MAAM,AAAC,uDAAuE,OAAjBI,YAAY,MAAK;4BACzF,MAAKG,IAAI;wBACX,GAAGH;oBACL;;;QACF;;IAEA;;GAEC,GACD,OAAMG,IAiBL,GAjBD,SAAMA;;;;;;;wBACJ,oBAAoB;wBACpB,IAAI,IAAI,CAACF,OAAO,EAAE;4BAChBG,aAAa,IAAI,CAACH,OAAO;4BACzB,IAAI,CAACA,OAAO,GAAGI;wBACjB;6BAGI,IAAI,CAAC9B,MAAM,EAAX;;;;wBACF;;4BAAM,IAAIH,QAAc,SAACC;oCACvB;iCAAA,eAAA,MAAKE,MAAM,cAAX,mCAAA,aAAa+B,KAAK,CAAC;oCACjB,MAAKvC,MAAM,CAACI,KAAK,CAAC;oCAClBE;gCACF;4BACF;;;wBALA;wBAMA,IAAI,CAACE,MAAM,GAAG8B;;;;;;;;QAElB;;IAEA;;GAEC,GACDE,OAAAA,cAKC,GALDA,SAAAA;QACE,IAAI,CAAC,IAAI,CAACzC,IAAI,EAAE;YACd,MAAM,IAAI8B,MAAM;QAClB;QACA,OAAO,AAAC,oBAA6B,OAAV,IAAI,CAAC9B,IAAI,EAAC;IACvC;WA/KWF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/oauth-callback-listener.ts"],"sourcesContent":["/**\n * OAuth Callback Server for CLI Authentication\n * Listens for OAuth authorization callbacks and captures authorization code\n */\n\nimport http from 'node:http';\nimport { logger as defaultLogger, type Logger } from '../utils/logger.ts';\nimport type { CallbackResult } from './types.ts';\n\nexport interface OAuthCallbackListenerOptions {\n /** Port to listen on (required - use get-port package to find available port) */\n port: number;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: Logger;\n}\n\n/**\n * OAuthCallbackListener handles OAuth redirect callbacks\n * Starts a temporary HTTP server to receive authorization code\n *\n * Note: Caller is responsible for finding an available port using get-port package\n */\nexport class OAuthCallbackListener {\n private server: http.Server | undefined;\n private resolveCallback?: (result: CallbackResult) => void;\n private rejectCallback?: (error: Error) => void;\n private timeout: NodeJS.Timeout | undefined;\n private port: number;\n private logger: Logger;\n\n constructor(options: OAuthCallbackListenerOptions) {\n this.port = options.port;\n this.logger = options.logger ?? defaultLogger;\n }\n\n /**\n * Start the callback server\n * Fails fast if port is already in use - caller should use get-port to find available port\n */\n async start(): Promise<void> {\n await this.listen(this.port);\n this.logger.debug(`โœ… Callback server listening on http://localhost:${this.port}/callback`);\n }\n\n /**\n * Listen on a specific port\n */\n private listen(port: number): Promise<void> {\n return new Promise((resolve, reject) => {\n this.server = http.createServer((req, res) => {\n this.handleRequest(req, res);\n });\n\n this.server.on('error', (error) => {\n reject(error);\n });\n\n this.server.listen(port, () => {\n resolve();\n });\n });\n }\n\n /**\n * Handle incoming HTTP requests\n */\n private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {\n const url = new URL(req.url || '', `http://localhost:${this.port}`);\n\n if (url.pathname === '/callback') {\n this.handleCallback(url, res);\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n }\n\n /**\n * Handle OAuth callback\n */\n private handleCallback(url: URL, res: http.ServerResponse): void {\n const code = url.searchParams.get('code');\n const state = url.searchParams.get('state');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n // Handle OAuth errors\n if (error) {\n const errorMessage = errorDescription ? `${error}: ${errorDescription}` : error;\n\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Authorization Failed</h1>\n <p>${errorMessage}</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error(errorMessage));\n }\n return;\n }\n\n // Validate code parameter\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(`\n <html>\n <body>\n <h1>Invalid Callback</h1>\n <p>Missing authorization code</p>\n <script>setTimeout(() => window.close(), 3000);</script>\n </body>\n </html>\n `);\n\n if (this.rejectCallback) {\n this.rejectCallback(new Error('Missing authorization code'));\n }\n return;\n }\n\n // Success - send confirmation page\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`\n <html>\n <head>\n <meta charset=\"UTF-8\">\n </head>\n <body>\n <h1>Authorization Successful</h1>\n <p>You can close this window and return to the terminal.</p>\n <script>setTimeout(() => window.close(), 2000);</script>\n </body>\n </html>\n `);\n\n // Resolve the promise with authorization code\n if (this.resolveCallback) {\n const result: CallbackResult = { code };\n if (state) {\n result.state = state;\n }\n this.resolveCallback(result);\n }\n }\n\n /**\n * Wait for OAuth callback with timeout\n */\n async waitForCallback(timeoutMs = 300000): Promise<CallbackResult> {\n return new Promise((resolve, reject) => {\n this.resolveCallback = resolve;\n this.rejectCallback = reject;\n\n // Set timeout to prevent hanging forever\n this.timeout = setTimeout(() => {\n reject(new Error(`Authorization timeout - no callback received within ${timeoutMs / 1000} seconds`));\n this.stop();\n }, timeoutMs);\n });\n }\n\n /**\n * Stop the callback server and close\n */\n async stop(): Promise<void> {\n // Clear the timeout\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = undefined;\n }\n\n // Close the server\n if (this.server) {\n await new Promise<void>((resolve) => {\n this.server?.close(() => {\n this.logger.debug('๐Ÿ”’ Callback server closed');\n resolve();\n });\n });\n this.server = undefined;\n }\n }\n\n /**\n * Get the callback URL for this server\n */\n getCallbackUrl(): string {\n if (!this.port) {\n throw new Error('Server not started - call start() first');\n }\n return `http://localhost:${this.port}/callback`;\n }\n}\n"],"names":["OAuthCallbackListener","options","port","logger","defaultLogger","start","listen","debug","Promise","resolve","reject","server","http","createServer","req","res","handleRequest","on","error","url","URL","pathname","handleCallback","writeHead","end","code","searchParams","get","state","errorDescription","errorMessage","rejectCallback","Error","resolveCallback","result","waitForCallback","timeoutMs","timeout","setTimeout","stop","clearTimeout","undefined","close","getCallbackUrl"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/pkce.ts"],"sourcesContent":["/**\n * PKCE (Proof Key for Code Exchange) utilities\n * Implements RFC 7636 for OAuth 2.0 public client security\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { PkceParams } from './types.ts';\n\n/**\n * Generate random code verifier for PKCE (RFC 7636 Section 4.1)\n * Returns cryptographically random string of 43-128 characters using base64url encoding\n */\nfunction generateRandomCodeVerifier(): string {\n // RFC 7636 recommends 43-128 characters\n // Using 32 random bytes -> 43 base64url characters\n return randomBytes(32).toString('base64url');\n}\n\n/**\n * Calculate PKCE code challenge from code verifier (RFC 7636 Section 4.2)\n * Uses S256 method: BASE64URL(SHA256(ASCII(code_verifier)))\n */\nasync function calculatePKCECodeChallenge(codeVerifier: string): Promise<string> {\n const hash = createHash('sha256').update(codeVerifier, 'ascii').digest();\n return Buffer.from(hash).toString('base64url');\n}\n\n/**\n * Generate PKCE parameters for OAuth 2.0 authorization code flow\n * Uses S256 method (SHA-256 hash) as recommended by RFC 7636\n *\n * @returns PkceParams with code verifier, challenge, and method\n *\n * @example\n * const pkce = await generatePkce();\n * // Use pkce.codeChallenge and pkce.codeChallengeMethod in authorization URL\n * // Store pkce.codeVerifier for token exchange\n */\nexport async function generatePkce(): Promise<PkceParams> {\n // Generate cryptographically random code verifier (RFC 7636 ยง 4.1)\n const codeVerifier = generateRandomCodeVerifier();\n\n // Generate code challenge using S256 method (RFC 7636 ยง 4.2)\n // S256: BASE64URL(SHA256(ASCII(code_verifier)))\n const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);\n\n return {\n codeVerifier,\n codeChallenge,\n codeChallengeMethod: 'S256',\n };\n}\n"],"names":["generatePkce","generateRandomCodeVerifier","randomBytes","toString","calculatePKCECodeChallenge","codeVerifier","hash","createHash","update","digest","Buffer","from","codeChallenge","codeChallengeMethod"],"mappings":"AAAA;;;CAGC;;;;+BAmCqBA;;;eAAAA;;;0BAjCkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGxC;;;CAGC,GACD,SAASC;IACP,wCAAwC;IACxC,mDAAmD;IACnD,OAAOC,IAAAA,uBAAW,EAAC,IAAIC,QAAQ,CAAC;AAClC;AAEA;;;CAGC,GACD,SAAeC,2BAA2BC,YAAoB;;YACtDC;;YAAAA,OAAOC,IAAAA,sBAAU,EAAC,UAAUC,MAAM,CAACH,cAAc,SAASI,MAAM;YACtE;;gBAAOC,OAAOC,IAAI,CAACL,MAAMH,QAAQ,CAAC;;;IACpC;;AAaO,SAAeH;;YAEdK,cAIAO;;;;oBALN,mEAAmE;oBAC7DP,eAAeJ;oBAIC;;wBAAMG,2BAA2BC;;;oBAAjDO,gBAAgB;oBAEtB;;wBAAO;4BACLP,cAAAA;4BACAO,eAAAA;4BACAC,qBAAqB;wBACvB;;;;IACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/pkce.ts"],"sourcesContent":["/**\n * PKCE (Proof Key for Code Exchange) utilities\n * Implements RFC 7636 for OAuth 2.0 public client security\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { PkceParams } from './types.ts';\n\n/**\n * Generate random code verifier for PKCE (RFC 7636 Section 4.1)\n * Returns cryptographically random string of 43-128 characters using base64url encoding\n */\nfunction generateRandomCodeVerifier(): string {\n // RFC 7636 recommends 43-128 characters\n // Using 32 random bytes -> 43 base64url characters\n return randomBytes(32).toString('base64url');\n}\n\n/**\n * Calculate PKCE code challenge from code verifier (RFC 7636 Section 4.2)\n * Uses S256 method: BASE64URL(SHA256(ASCII(code_verifier)))\n */\nasync function calculatePKCECodeChallenge(codeVerifier: string): Promise<string> {\n const hash = createHash('sha256').update(codeVerifier, 'ascii').digest();\n return Buffer.from(hash).toString('base64url');\n}\n\n/**\n * Generate PKCE parameters for OAuth 2.0 authorization code flow\n * Uses S256 method (SHA-256 hash) as recommended by RFC 7636\n *\n * @returns PkceParams with code verifier, challenge, and method\n *\n * @example\n * const pkce = await generatePkce();\n * // Use pkce.codeChallenge and pkce.codeChallengeMethod in authorization URL\n * // Store pkce.codeVerifier for token exchange\n */\nexport async function generatePkce(): Promise<PkceParams> {\n // Generate cryptographically random code verifier (RFC 7636 ยง 4.1)\n const codeVerifier = generateRandomCodeVerifier();\n\n // Generate code challenge using S256 method (RFC 7636 ยง 4.2)\n // S256: BASE64URL(SHA256(ASCII(code_verifier)))\n const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);\n\n return {\n codeVerifier,\n codeChallenge,\n codeChallengeMethod: 'S256',\n };\n}\n"],"names":["generatePkce","generateRandomCodeVerifier","randomBytes","toString","calculatePKCECodeChallenge","codeVerifier","hash","createHash","update","digest","Buffer","from","codeChallenge","codeChallengeMethod"],"mappings":"AAAA;;;CAGC;;;;+BAmCqBA;;;eAAAA;;;0BAjCkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGxC;;;CAGC,GACD,SAASC;IACP,wCAAwC;IACxC,mDAAmD;IACnD,OAAOC,IAAAA,uBAAW,EAAC,IAAIC,QAAQ,CAAC;AAClC;AAEA;;;CAGC,GACD,SAAeC,2BAA2BC,YAAoB;;YACtDC;;YAAAA,OAAOC,IAAAA,sBAAU,EAAC,UAAUC,MAAM,CAACH,cAAc,SAASI,MAAM;YACtE;;gBAAOC,OAAOC,IAAI,CAACL,MAAMH,QAAQ,CAAC;;;IACpC;;AAaO,SAAeH;;YAEdK,cAIAO;;;;oBALN,mEAAmE;oBAC7DP,eAAeJ;oBAIC;;wBAAMG,2BAA2BC;;;oBAAjDO,gBAAgB;oBAEtB;;wBAAO;4BACLP,cAAAA;4BACAO,eAAAA;4BACAC,qBAAqB;wBACvB;;;;IACF"}
@@ -32,3 +32,10 @@ export declare function discoverProtectedResourceMetadata(resourceUrl: string):
32
32
  * // Returns: { issuer: "https://todoist.com", authorization_endpoint: "...", ... }
33
33
  */
34
34
  export declare function discoverAuthorizationServerMetadata(authServerUrl: string): Promise<AuthorizationServerMetadata | null>;
35
+ /**
36
+ * Discover OAuth Authorization Server Issuer from resource response (RFC 9207)
37
+ *
38
+ * @param resourceUrl - URL of the protected resource
39
+ * @returns Issuer URL if present in WWW-Authenticate header, null otherwise
40
+ */
41
+ export declare function discoverAuthorizationServerIssuer(resourceUrl: string): Promise<string | null>;
@@ -32,3 +32,10 @@ export declare function discoverProtectedResourceMetadata(resourceUrl: string):
32
32
  * // Returns: { issuer: "https://todoist.com", authorization_endpoint: "...", ... }
33
33
  */
34
34
  export declare function discoverAuthorizationServerMetadata(authServerUrl: string): Promise<AuthorizationServerMetadata | null>;
35
+ /**
36
+ * Discover OAuth Authorization Server Issuer from resource response (RFC 9207)
37
+ *
38
+ * @param resourceUrl - URL of the protected resource
39
+ * @returns Issuer URL if present in WWW-Authenticate header, null otherwise
40
+ */
41
+ export declare function discoverAuthorizationServerIssuer(resourceUrl: string): Promise<string | null>;