@trops/dash-core 0.1.610 → 0.1.611

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.
@@ -19,20 +19,20 @@ var require$$6 = require('csv-parser');
19
19
  var require$$0$6 = require('quickjs-emscripten');
20
20
  var require$$11 = require('https');
21
21
  var require$$0$8 = require('@modelcontextprotocol/sdk/client/index.js');
22
- var require$$1$5 = require('@modelcontextprotocol/sdk/client/stdio.js');
22
+ var require$$1$6 = require('@modelcontextprotocol/sdk/client/stdio.js');
23
23
  var require$$0$7 = require('pkce-challenge');
24
24
  var require$$12 = require('child_process');
25
+ var require$$1$5 = require('http');
25
26
  var require$$2$2 = require('algoliasearch');
26
27
  var require$$3$2 = require('node:path');
27
28
  var require$$0$9 = require('openai');
28
- var require$$0$c = require('@anthropic-ai/sdk');
29
+ var require$$0$b = require('@anthropic-ai/sdk');
29
30
  var require$$2$3 = require('zod-to-json-schema');
30
31
  var require$$8$1 = require('zod');
31
- var require$$0$a = require('http');
32
- var require$$1$6 = require('http2');
32
+ var require$$1$7 = require('http2');
33
33
  var require$$2$4 = require('node-forge');
34
- var require$$0$b = require('css');
35
- var require$$1$7 = require('node-vibrant/node');
34
+ var require$$0$a = require('css');
35
+ var require$$1$8 = require('node-vibrant/node');
36
36
  var require$$3$3 = require('ws');
37
37
 
38
38
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
@@ -411,6 +411,10 @@ const MCP_RUN_AUTH$1 = "mcp-run-auth";
411
411
  const MCP_RUN_AUTH_COMPLETE = "mcp-run-auth-complete";
412
412
  const MCP_RUN_AUTH_ERROR = "mcp-run-auth-error";
413
413
 
414
+ const MCP_AUTHORIZE$1 = "mcp-authorize";
415
+ const MCP_AUTHORIZE_COMPLETE = "mcp-authorize-complete";
416
+ const MCP_AUTHORIZE_ERROR = "mcp-authorize-error";
417
+
414
418
  var mcpEvents$1 = {
415
419
  MCP_START_SERVER: MCP_START_SERVER$1,
416
420
  MCP_START_SERVER_COMPLETE,
@@ -442,6 +446,9 @@ var mcpEvents$1 = {
442
446
  MCP_RUN_AUTH: MCP_RUN_AUTH$1,
443
447
  MCP_RUN_AUTH_COMPLETE,
444
448
  MCP_RUN_AUTH_ERROR,
449
+ MCP_AUTHORIZE: MCP_AUTHORIZE$1,
450
+ MCP_AUTHORIZE_COMPLETE,
451
+ MCP_AUTHORIZE_ERROR,
445
452
  };
446
453
 
447
454
  /**
@@ -18748,6 +18755,14 @@ function requireProviderController () {
18748
18755
  providerEntry.isDefaultForType = true;
18749
18756
  }
18750
18757
 
18758
+ // Preserve any OAuth state (tokens / DCR client info / PKCE verifier)
18759
+ // written out-of-band by saveOAuthState. A fresh providerEntry would
18760
+ // otherwise drop it, wiping tokens whenever the user edits/re-saves an
18761
+ // OAuth MCP provider.
18762
+ if (prev.oauth) {
18763
+ providerEntry.oauth = prev.oauth;
18764
+ }
18765
+
18751
18766
  // Single-winner invariant: when flipping this provider's flag to
18752
18767
  // true, clear it on every other provider with the same type. Run
18753
18768
  // this BEFORE we set `providers[providerName] = providerEntry` so
@@ -19025,6 +19040,86 @@ function requireProviderController () {
19025
19040
  };
19026
19041
  }
19027
19042
  },
19043
+
19044
+ /**
19045
+ * saveOAuthState
19046
+ * Persist an MCP OAuth session blob (tokens, DCR client info, PKCE
19047
+ * verifier) encrypted alongside the provider entry. Used by the SDK
19048
+ * OAuthClientProvider (mcpOAuthProvider.js). Creates a minimal MCP
19049
+ * provider entry if one doesn't exist yet, so "Authorize" can run
19050
+ * before the provider is formally saved (the later saveProvider
19051
+ * preserves this blob).
19052
+ *
19053
+ * @param {BrowserWindow} win the main window (unused; signature parity)
19054
+ * @param {string} appId the application id
19055
+ * @param {string} providerName the provider/server name
19056
+ * @param {object} oauthState { tokens?, clientInformation?, codeVerifier? }
19057
+ */
19058
+ saveOAuthState: (win, appId, providerName, oauthState) => {
19059
+ try {
19060
+ const filename = path.join(
19061
+ app.getPath("userData"),
19062
+ appName,
19063
+ appId,
19064
+ configFilename,
19065
+ );
19066
+ ensureDirectoryExistence(filename);
19067
+
19068
+ const providers = getFileContents(filename, {});
19069
+ const encrypted = safeStorage
19070
+ .encryptString(JSON.stringify(oauthState || {}))
19071
+ .toString("base64");
19072
+
19073
+ const prev = providers[providerName] || {};
19074
+ providers[providerName] = {
19075
+ ...prev,
19076
+ providerClass: prev.providerClass || "mcp",
19077
+ oauth: encrypted,
19078
+ dateCreated: prev.dateCreated || new Date().toISOString(),
19079
+ dateUpdated: new Date().toISOString(),
19080
+ };
19081
+
19082
+ writeFileSync(filename, JSON.stringify(providers, null, 2), {
19083
+ mode: 0o600,
19084
+ });
19085
+
19086
+ return { success: true };
19087
+ } catch (error) {
19088
+ console.error("[providerController] Error saving OAuth state:", error);
19089
+ return { error: true, message: error.message };
19090
+ }
19091
+ },
19092
+
19093
+ /**
19094
+ * getOAuthState
19095
+ * Read and decrypt the OAuth session blob for a provider, or `{}` if
19096
+ * none exists. Main-process only — never surfaced to the renderer.
19097
+ *
19098
+ * @param {BrowserWindow} win the main window (unused; signature parity)
19099
+ * @param {string} appId the application id
19100
+ * @param {string} providerName the provider/server name
19101
+ * @returns {{ oauth?: object }}
19102
+ */
19103
+ getOAuthState: (win, appId, providerName) => {
19104
+ try {
19105
+ const filename = path.join(
19106
+ app.getPath("userData"),
19107
+ appName,
19108
+ appId,
19109
+ configFilename,
19110
+ );
19111
+ const providers = getFileContents(filename, {});
19112
+ const entry = providers[providerName];
19113
+ if (!entry || !entry.oauth) return {};
19114
+
19115
+ const buffer = Buffer.from(entry.oauth, "base64");
19116
+ const decrypted = safeStorage.decryptString(buffer);
19117
+ return { oauth: JSON.parse(decrypted) };
19118
+ } catch (error) {
19119
+ console.error("[providerController] Error reading OAuth state:", error);
19120
+ return {};
19121
+ }
19122
+ },
19028
19123
  };
19029
19124
 
19030
19125
  providerController_1 = providerController;
@@ -30734,11 +30829,11 @@ var __importStar$4 = (commonjsGlobal && commonjsGlobal.__importStar) || function
30734
30829
  Object.defineProperty(parse$2, "__esModule", { value: true });
30735
30830
  parse$2.safeParseAsync = parse$2.safeParse = parse$2.parseAsync = parse$2.parse = void 0;
30736
30831
  const core$5 = __importStar$4(core$8);
30737
- const errors_js_1$2 = errors$3;
30738
- parse$2.parse = core$5._parse(errors_js_1$2.ZodRealError);
30739
- parse$2.parseAsync = core$5._parseAsync(errors_js_1$2.ZodRealError);
30740
- parse$2.safeParse = core$5._safeParse(errors_js_1$2.ZodRealError);
30741
- parse$2.safeParseAsync = core$5._safeParseAsync(errors_js_1$2.ZodRealError);
30832
+ const errors_js_1$1 = errors$3;
30833
+ parse$2.parse = core$5._parse(errors_js_1$1.ZodRealError);
30834
+ parse$2.parseAsync = core$5._parseAsync(errors_js_1$1.ZodRealError);
30835
+ parse$2.safeParse = core$5._safeParse(errors_js_1$1.ZodRealError);
30836
+ parse$2.safeParseAsync = core$5._safeParseAsync(errors_js_1$1.ZodRealError);
30742
30837
 
30743
30838
  var hasRequiredSchemas;
30744
30839
 
@@ -34204,1426 +34299,1457 @@ function date$1(params) {
34204
34299
 
34205
34300
  } (types$2));
34206
34301
 
34207
- var auth$2 = {};
34208
-
34209
34302
  var auth$1 = {};
34210
34303
 
34211
- (function (exports) {
34212
- var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
34213
- if (k2 === undefined) k2 = k;
34214
- var desc = Object.getOwnPropertyDescriptor(m, k);
34215
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
34216
- desc = { enumerable: true, get: function() { return m[k]; } };
34304
+ var auth = {};
34305
+
34306
+ var hasRequiredAuth$1;
34307
+
34308
+ function requireAuth$1 () {
34309
+ if (hasRequiredAuth$1) return auth;
34310
+ hasRequiredAuth$1 = 1;
34311
+ (function (exports) {
34312
+ var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
34313
+ if (k2 === undefined) k2 = k;
34314
+ var desc = Object.getOwnPropertyDescriptor(m, k);
34315
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
34316
+ desc = { enumerable: true, get: function() { return m[k]; } };
34317
+ }
34318
+ Object.defineProperty(o, k2, desc);
34319
+ }) : (function(o, m, k, k2) {
34320
+ if (k2 === undefined) k2 = k;
34321
+ o[k2] = m[k];
34322
+ }));
34323
+ var __setModuleDefault = (commonjsGlobal && commonjsGlobal.__setModuleDefault) || (Object.create ? (function(o, v) {
34324
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34325
+ }) : function(o, v) {
34326
+ o["default"] = v;
34327
+ });
34328
+ var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
34329
+ if (mod && mod.__esModule) return mod;
34330
+ var result = {};
34331
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
34332
+ __setModuleDefault(result, mod);
34333
+ return result;
34334
+ };
34335
+ Object.defineProperty(exports, "__esModule", { value: true });
34336
+ exports.OAuthTokenRevocationRequestSchema = exports.OAuthClientRegistrationErrorSchema = exports.OAuthClientInformationFullSchema = exports.OAuthClientInformationSchema = exports.OAuthClientMetadataSchema = exports.OptionalSafeUrlSchema = exports.OAuthErrorResponseSchema = exports.OAuthTokensSchema = exports.OpenIdProviderDiscoveryMetadataSchema = exports.OpenIdProviderMetadataSchema = exports.OAuthMetadataSchema = exports.OAuthProtectedResourceMetadataSchema = exports.SafeUrlSchema = void 0;
34337
+ const z = __importStar(v4);
34338
+ /**
34339
+ * Reusable URL validation that disallows javascript: scheme
34340
+ */
34341
+ exports.SafeUrlSchema = z
34342
+ .url()
34343
+ .superRefine((val, ctx) => {
34344
+ if (!URL.canParse(val)) {
34345
+ ctx.addIssue({
34346
+ code: z.ZodIssueCode.custom,
34347
+ message: 'URL must be parseable',
34348
+ fatal: true
34349
+ });
34350
+ return z.NEVER;
34351
+ }
34352
+ })
34353
+ .refine(url => {
34354
+ const u = new URL(url);
34355
+ return u.protocol !== 'javascript:' && u.protocol !== 'data:' && u.protocol !== 'vbscript:';
34356
+ }, { message: 'URL cannot use javascript:, data:, or vbscript: scheme' });
34357
+ /**
34358
+ * RFC 9728 OAuth Protected Resource Metadata
34359
+ */
34360
+ exports.OAuthProtectedResourceMetadataSchema = z.looseObject({
34361
+ resource: z.string().url(),
34362
+ authorization_servers: z.array(exports.SafeUrlSchema).optional(),
34363
+ jwks_uri: z.string().url().optional(),
34364
+ scopes_supported: z.array(z.string()).optional(),
34365
+ bearer_methods_supported: z.array(z.string()).optional(),
34366
+ resource_signing_alg_values_supported: z.array(z.string()).optional(),
34367
+ resource_name: z.string().optional(),
34368
+ resource_documentation: z.string().optional(),
34369
+ resource_policy_uri: z.string().url().optional(),
34370
+ resource_tos_uri: z.string().url().optional(),
34371
+ tls_client_certificate_bound_access_tokens: z.boolean().optional(),
34372
+ authorization_details_types_supported: z.array(z.string()).optional(),
34373
+ dpop_signing_alg_values_supported: z.array(z.string()).optional(),
34374
+ dpop_bound_access_tokens_required: z.boolean().optional()
34375
+ });
34376
+ /**
34377
+ * RFC 8414 OAuth 2.0 Authorization Server Metadata
34378
+ */
34379
+ exports.OAuthMetadataSchema = z.looseObject({
34380
+ issuer: z.string(),
34381
+ authorization_endpoint: exports.SafeUrlSchema,
34382
+ token_endpoint: exports.SafeUrlSchema,
34383
+ registration_endpoint: exports.SafeUrlSchema.optional(),
34384
+ scopes_supported: z.array(z.string()).optional(),
34385
+ response_types_supported: z.array(z.string()),
34386
+ response_modes_supported: z.array(z.string()).optional(),
34387
+ grant_types_supported: z.array(z.string()).optional(),
34388
+ token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34389
+ token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34390
+ service_documentation: exports.SafeUrlSchema.optional(),
34391
+ revocation_endpoint: exports.SafeUrlSchema.optional(),
34392
+ revocation_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34393
+ revocation_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34394
+ introspection_endpoint: z.string().optional(),
34395
+ introspection_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34396
+ introspection_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34397
+ code_challenge_methods_supported: z.array(z.string()).optional(),
34398
+ client_id_metadata_document_supported: z.boolean().optional()
34399
+ });
34400
+ /**
34401
+ * OpenID Connect Discovery 1.0 Provider Metadata
34402
+ * see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
34403
+ */
34404
+ exports.OpenIdProviderMetadataSchema = z.looseObject({
34405
+ issuer: z.string(),
34406
+ authorization_endpoint: exports.SafeUrlSchema,
34407
+ token_endpoint: exports.SafeUrlSchema,
34408
+ userinfo_endpoint: exports.SafeUrlSchema.optional(),
34409
+ jwks_uri: exports.SafeUrlSchema,
34410
+ registration_endpoint: exports.SafeUrlSchema.optional(),
34411
+ scopes_supported: z.array(z.string()).optional(),
34412
+ response_types_supported: z.array(z.string()),
34413
+ response_modes_supported: z.array(z.string()).optional(),
34414
+ grant_types_supported: z.array(z.string()).optional(),
34415
+ acr_values_supported: z.array(z.string()).optional(),
34416
+ subject_types_supported: z.array(z.string()),
34417
+ id_token_signing_alg_values_supported: z.array(z.string()),
34418
+ id_token_encryption_alg_values_supported: z.array(z.string()).optional(),
34419
+ id_token_encryption_enc_values_supported: z.array(z.string()).optional(),
34420
+ userinfo_signing_alg_values_supported: z.array(z.string()).optional(),
34421
+ userinfo_encryption_alg_values_supported: z.array(z.string()).optional(),
34422
+ userinfo_encryption_enc_values_supported: z.array(z.string()).optional(),
34423
+ request_object_signing_alg_values_supported: z.array(z.string()).optional(),
34424
+ request_object_encryption_alg_values_supported: z.array(z.string()).optional(),
34425
+ request_object_encryption_enc_values_supported: z.array(z.string()).optional(),
34426
+ token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34427
+ token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34428
+ display_values_supported: z.array(z.string()).optional(),
34429
+ claim_types_supported: z.array(z.string()).optional(),
34430
+ claims_supported: z.array(z.string()).optional(),
34431
+ service_documentation: z.string().optional(),
34432
+ claims_locales_supported: z.array(z.string()).optional(),
34433
+ ui_locales_supported: z.array(z.string()).optional(),
34434
+ claims_parameter_supported: z.boolean().optional(),
34435
+ request_parameter_supported: z.boolean().optional(),
34436
+ request_uri_parameter_supported: z.boolean().optional(),
34437
+ require_request_uri_registration: z.boolean().optional(),
34438
+ op_policy_uri: exports.SafeUrlSchema.optional(),
34439
+ op_tos_uri: exports.SafeUrlSchema.optional(),
34440
+ client_id_metadata_document_supported: z.boolean().optional()
34441
+ });
34442
+ /**
34443
+ * OpenID Connect Discovery metadata that may include OAuth 2.0 fields
34444
+ * This schema represents the real-world scenario where OIDC providers
34445
+ * return a mix of OpenID Connect and OAuth 2.0 metadata fields
34446
+ */
34447
+ exports.OpenIdProviderDiscoveryMetadataSchema = z.object({
34448
+ ...exports.OpenIdProviderMetadataSchema.shape,
34449
+ ...exports.OAuthMetadataSchema.pick({
34450
+ code_challenge_methods_supported: true
34451
+ }).shape
34452
+ });
34453
+ /**
34454
+ * OAuth 2.1 token response
34455
+ */
34456
+ exports.OAuthTokensSchema = z
34457
+ .object({
34458
+ access_token: z.string(),
34459
+ id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
34460
+ token_type: z.string(),
34461
+ expires_in: z.coerce.number().optional(),
34462
+ scope: z.string().optional(),
34463
+ refresh_token: z.string().optional()
34464
+ })
34465
+ .strip();
34466
+ /**
34467
+ * OAuth 2.1 error response
34468
+ */
34469
+ exports.OAuthErrorResponseSchema = z.object({
34470
+ error: z.string(),
34471
+ error_description: z.string().optional(),
34472
+ error_uri: z.string().optional()
34473
+ });
34474
+ /**
34475
+ * Optional version of SafeUrlSchema that allows empty string for retrocompatibility on tos_uri and logo_uri
34476
+ */
34477
+ exports.OptionalSafeUrlSchema = exports.SafeUrlSchema.optional().or(z.literal('').transform(() => undefined));
34478
+ /**
34479
+ * RFC 7591 OAuth 2.0 Dynamic Client Registration metadata
34480
+ */
34481
+ exports.OAuthClientMetadataSchema = z
34482
+ .object({
34483
+ redirect_uris: z.array(exports.SafeUrlSchema),
34484
+ token_endpoint_auth_method: z.string().optional(),
34485
+ grant_types: z.array(z.string()).optional(),
34486
+ response_types: z.array(z.string()).optional(),
34487
+ client_name: z.string().optional(),
34488
+ client_uri: exports.SafeUrlSchema.optional(),
34489
+ logo_uri: exports.OptionalSafeUrlSchema,
34490
+ scope: z.string().optional(),
34491
+ contacts: z.array(z.string()).optional(),
34492
+ tos_uri: exports.OptionalSafeUrlSchema,
34493
+ policy_uri: z.string().optional(),
34494
+ jwks_uri: exports.SafeUrlSchema.optional(),
34495
+ jwks: z.any().optional(),
34496
+ software_id: z.string().optional(),
34497
+ software_version: z.string().optional(),
34498
+ software_statement: z.string().optional()
34499
+ })
34500
+ .strip();
34501
+ /**
34502
+ * RFC 7591 OAuth 2.0 Dynamic Client Registration client information
34503
+ */
34504
+ exports.OAuthClientInformationSchema = z
34505
+ .object({
34506
+ client_id: z.string(),
34507
+ client_secret: z.string().optional(),
34508
+ client_id_issued_at: z.number().optional(),
34509
+ client_secret_expires_at: z.number().optional()
34510
+ })
34511
+ .strip();
34512
+ /**
34513
+ * RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
34514
+ */
34515
+ exports.OAuthClientInformationFullSchema = exports.OAuthClientMetadataSchema.merge(exports.OAuthClientInformationSchema);
34516
+ /**
34517
+ * RFC 7591 OAuth 2.0 Dynamic Client Registration error response
34518
+ */
34519
+ exports.OAuthClientRegistrationErrorSchema = z
34520
+ .object({
34521
+ error: z.string(),
34522
+ error_description: z.string().optional()
34523
+ })
34524
+ .strip();
34525
+ /**
34526
+ * RFC 7009 OAuth 2.0 Token Revocation request
34527
+ */
34528
+ exports.OAuthTokenRevocationRequestSchema = z
34529
+ .object({
34530
+ token: z.string(),
34531
+ token_type_hint: z.string().optional()
34532
+ })
34533
+ .strip();
34534
+
34535
+ } (auth));
34536
+ return auth;
34537
+ }
34538
+
34539
+ var authUtils = {};
34540
+
34541
+ var hasRequiredAuthUtils;
34542
+
34543
+ function requireAuthUtils () {
34544
+ if (hasRequiredAuthUtils) return authUtils;
34545
+ hasRequiredAuthUtils = 1;
34546
+ /**
34547
+ * Utilities for handling OAuth resource URIs.
34548
+ */
34549
+ Object.defineProperty(authUtils, "__esModule", { value: true });
34550
+ authUtils.resourceUrlFromServerUrl = resourceUrlFromServerUrl;
34551
+ authUtils.checkResourceAllowed = checkResourceAllowed;
34552
+ /**
34553
+ * Converts a server URL to a resource URL by removing the fragment.
34554
+ * RFC 8707 section 2 states that resource URIs "MUST NOT include a fragment component".
34555
+ * Keeps everything else unchanged (scheme, domain, port, path, query).
34556
+ */
34557
+ function resourceUrlFromServerUrl(url) {
34558
+ const resourceURL = typeof url === 'string' ? new URL(url) : new URL(url.href);
34559
+ resourceURL.hash = ''; // Remove fragment
34560
+ return resourceURL;
34561
+ }
34562
+ /**
34563
+ * Checks if a requested resource URL matches a configured resource URL.
34564
+ * A requested resource matches if it has the same scheme, domain, port,
34565
+ * and its path starts with the configured resource's path.
34566
+ *
34567
+ * @param requestedResource The resource URL being requested
34568
+ * @param configuredResource The resource URL that has been configured
34569
+ * @returns true if the requested resource matches the configured resource, false otherwise
34570
+ */
34571
+ function checkResourceAllowed({ requestedResource, configuredResource }) {
34572
+ const requested = typeof requestedResource === 'string' ? new URL(requestedResource) : new URL(requestedResource.href);
34573
+ const configured = typeof configuredResource === 'string' ? new URL(configuredResource) : new URL(configuredResource.href);
34574
+ // Compare the origin (scheme, domain, and port)
34575
+ if (requested.origin !== configured.origin) {
34576
+ return false;
34217
34577
  }
34218
- Object.defineProperty(o, k2, desc);
34219
- }) : (function(o, m, k, k2) {
34220
- if (k2 === undefined) k2 = k;
34221
- o[k2] = m[k];
34222
- }));
34223
- var __setModuleDefault = (commonjsGlobal && commonjsGlobal.__setModuleDefault) || (Object.create ? (function(o, v) {
34224
- Object.defineProperty(o, "default", { enumerable: true, value: v });
34225
- }) : function(o, v) {
34226
- o["default"] = v;
34227
- });
34228
- var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
34229
- if (mod && mod.__esModule) return mod;
34230
- var result = {};
34231
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
34232
- __setModuleDefault(result, mod);
34233
- return result;
34234
- };
34235
- Object.defineProperty(exports, "__esModule", { value: true });
34236
- exports.OAuthTokenRevocationRequestSchema = exports.OAuthClientRegistrationErrorSchema = exports.OAuthClientInformationFullSchema = exports.OAuthClientInformationSchema = exports.OAuthClientMetadataSchema = exports.OptionalSafeUrlSchema = exports.OAuthErrorResponseSchema = exports.OAuthTokensSchema = exports.OpenIdProviderDiscoveryMetadataSchema = exports.OpenIdProviderMetadataSchema = exports.OAuthMetadataSchema = exports.OAuthProtectedResourceMetadataSchema = exports.SafeUrlSchema = void 0;
34237
- const z = __importStar(v4);
34578
+ // Handle cases like requested=/foo and configured=/foo/
34579
+ if (requested.pathname.length < configured.pathname.length) {
34580
+ return false;
34581
+ }
34582
+ // Check if the requested path starts with the configured path
34583
+ // Ensure both paths end with / for proper comparison
34584
+ // This ensures that if we have paths like "/api" and "/api/users",
34585
+ // we properly detect that "/api/users" is a subpath of "/api"
34586
+ // By adding a trailing slash if missing, we avoid false positives
34587
+ // where paths like "/api123" would incorrectly match "/api"
34588
+ const requestedPath = requested.pathname.endsWith('/') ? requested.pathname : requested.pathname + '/';
34589
+ const configuredPath = configured.pathname.endsWith('/') ? configured.pathname : configured.pathname + '/';
34590
+ return requestedPath.startsWith(configuredPath);
34591
+ }
34592
+
34593
+ return authUtils;
34594
+ }
34595
+
34596
+ var errors$2 = {};
34597
+
34598
+ var hasRequiredErrors$1;
34599
+
34600
+ function requireErrors$1 () {
34601
+ if (hasRequiredErrors$1) return errors$2;
34602
+ hasRequiredErrors$1 = 1;
34603
+ Object.defineProperty(errors$2, "__esModule", { value: true });
34604
+ errors$2.OAUTH_ERRORS = errors$2.CustomOAuthError = errors$2.InvalidTargetError = errors$2.InsufficientScopeError = errors$2.InvalidClientMetadataError = errors$2.TooManyRequestsError = errors$2.MethodNotAllowedError = errors$2.InvalidTokenError = errors$2.UnsupportedTokenTypeError = errors$2.UnsupportedResponseTypeError = errors$2.TemporarilyUnavailableError = errors$2.ServerError = errors$2.AccessDeniedError = errors$2.InvalidScopeError = errors$2.UnsupportedGrantTypeError = errors$2.UnauthorizedClientError = errors$2.InvalidGrantError = errors$2.InvalidClientError = errors$2.InvalidRequestError = errors$2.OAuthError = void 0;
34238
34605
  /**
34239
- * Reusable URL validation that disallows javascript: scheme
34606
+ * Base class for all OAuth errors
34240
34607
  */
34241
- exports.SafeUrlSchema = z
34242
- .url()
34243
- .superRefine((val, ctx) => {
34244
- if (!URL.canParse(val)) {
34245
- ctx.addIssue({
34246
- code: z.ZodIssueCode.custom,
34247
- message: 'URL must be parseable',
34248
- fatal: true
34249
- });
34250
- return z.NEVER;
34608
+ class OAuthError extends Error {
34609
+ constructor(message, errorUri) {
34610
+ super(message);
34611
+ this.errorUri = errorUri;
34612
+ this.name = this.constructor.name;
34251
34613
  }
34252
- })
34253
- .refine(url => {
34254
- const u = new URL(url);
34255
- return u.protocol !== 'javascript:' && u.protocol !== 'data:' && u.protocol !== 'vbscript:';
34256
- }, { message: 'URL cannot use javascript:, data:, or vbscript: scheme' });
34614
+ /**
34615
+ * Converts the error to a standard OAuth error response object
34616
+ */
34617
+ toResponseObject() {
34618
+ const response = {
34619
+ error: this.errorCode,
34620
+ error_description: this.message
34621
+ };
34622
+ if (this.errorUri) {
34623
+ response.error_uri = this.errorUri;
34624
+ }
34625
+ return response;
34626
+ }
34627
+ get errorCode() {
34628
+ return this.constructor.errorCode;
34629
+ }
34630
+ }
34631
+ errors$2.OAuthError = OAuthError;
34257
34632
  /**
34258
- * RFC 9728 OAuth Protected Resource Metadata
34633
+ * Invalid request error - The request is missing a required parameter,
34634
+ * includes an invalid parameter value, includes a parameter more than once,
34635
+ * or is otherwise malformed.
34259
34636
  */
34260
- exports.OAuthProtectedResourceMetadataSchema = z.looseObject({
34261
- resource: z.string().url(),
34262
- authorization_servers: z.array(exports.SafeUrlSchema).optional(),
34263
- jwks_uri: z.string().url().optional(),
34264
- scopes_supported: z.array(z.string()).optional(),
34265
- bearer_methods_supported: z.array(z.string()).optional(),
34266
- resource_signing_alg_values_supported: z.array(z.string()).optional(),
34267
- resource_name: z.string().optional(),
34268
- resource_documentation: z.string().optional(),
34269
- resource_policy_uri: z.string().url().optional(),
34270
- resource_tos_uri: z.string().url().optional(),
34271
- tls_client_certificate_bound_access_tokens: z.boolean().optional(),
34272
- authorization_details_types_supported: z.array(z.string()).optional(),
34273
- dpop_signing_alg_values_supported: z.array(z.string()).optional(),
34274
- dpop_bound_access_tokens_required: z.boolean().optional()
34275
- });
34637
+ class InvalidRequestError extends OAuthError {
34638
+ }
34639
+ errors$2.InvalidRequestError = InvalidRequestError;
34640
+ InvalidRequestError.errorCode = 'invalid_request';
34276
34641
  /**
34277
- * RFC 8414 OAuth 2.0 Authorization Server Metadata
34642
+ * Invalid client error - Client authentication failed (e.g., unknown client, no client
34643
+ * authentication included, or unsupported authentication method).
34278
34644
  */
34279
- exports.OAuthMetadataSchema = z.looseObject({
34280
- issuer: z.string(),
34281
- authorization_endpoint: exports.SafeUrlSchema,
34282
- token_endpoint: exports.SafeUrlSchema,
34283
- registration_endpoint: exports.SafeUrlSchema.optional(),
34284
- scopes_supported: z.array(z.string()).optional(),
34285
- response_types_supported: z.array(z.string()),
34286
- response_modes_supported: z.array(z.string()).optional(),
34287
- grant_types_supported: z.array(z.string()).optional(),
34288
- token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34289
- token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34290
- service_documentation: exports.SafeUrlSchema.optional(),
34291
- revocation_endpoint: exports.SafeUrlSchema.optional(),
34292
- revocation_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34293
- revocation_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34294
- introspection_endpoint: z.string().optional(),
34295
- introspection_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34296
- introspection_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34297
- code_challenge_methods_supported: z.array(z.string()).optional(),
34298
- client_id_metadata_document_supported: z.boolean().optional()
34299
- });
34645
+ class InvalidClientError extends OAuthError {
34646
+ }
34647
+ errors$2.InvalidClientError = InvalidClientError;
34648
+ InvalidClientError.errorCode = 'invalid_client';
34300
34649
  /**
34301
- * OpenID Connect Discovery 1.0 Provider Metadata
34302
- * see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
34650
+ * Invalid grant error - The provided authorization grant or refresh token is
34651
+ * invalid, expired, revoked, does not match the redirection URI used in the
34652
+ * authorization request, or was issued to another client.
34303
34653
  */
34304
- exports.OpenIdProviderMetadataSchema = z.looseObject({
34305
- issuer: z.string(),
34306
- authorization_endpoint: exports.SafeUrlSchema,
34307
- token_endpoint: exports.SafeUrlSchema,
34308
- userinfo_endpoint: exports.SafeUrlSchema.optional(),
34309
- jwks_uri: exports.SafeUrlSchema,
34310
- registration_endpoint: exports.SafeUrlSchema.optional(),
34311
- scopes_supported: z.array(z.string()).optional(),
34312
- response_types_supported: z.array(z.string()),
34313
- response_modes_supported: z.array(z.string()).optional(),
34314
- grant_types_supported: z.array(z.string()).optional(),
34315
- acr_values_supported: z.array(z.string()).optional(),
34316
- subject_types_supported: z.array(z.string()),
34317
- id_token_signing_alg_values_supported: z.array(z.string()),
34318
- id_token_encryption_alg_values_supported: z.array(z.string()).optional(),
34319
- id_token_encryption_enc_values_supported: z.array(z.string()).optional(),
34320
- userinfo_signing_alg_values_supported: z.array(z.string()).optional(),
34321
- userinfo_encryption_alg_values_supported: z.array(z.string()).optional(),
34322
- userinfo_encryption_enc_values_supported: z.array(z.string()).optional(),
34323
- request_object_signing_alg_values_supported: z.array(z.string()).optional(),
34324
- request_object_encryption_alg_values_supported: z.array(z.string()).optional(),
34325
- request_object_encryption_enc_values_supported: z.array(z.string()).optional(),
34326
- token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
34327
- token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
34328
- display_values_supported: z.array(z.string()).optional(),
34329
- claim_types_supported: z.array(z.string()).optional(),
34330
- claims_supported: z.array(z.string()).optional(),
34331
- service_documentation: z.string().optional(),
34332
- claims_locales_supported: z.array(z.string()).optional(),
34333
- ui_locales_supported: z.array(z.string()).optional(),
34334
- claims_parameter_supported: z.boolean().optional(),
34335
- request_parameter_supported: z.boolean().optional(),
34336
- request_uri_parameter_supported: z.boolean().optional(),
34337
- require_request_uri_registration: z.boolean().optional(),
34338
- op_policy_uri: exports.SafeUrlSchema.optional(),
34339
- op_tos_uri: exports.SafeUrlSchema.optional(),
34340
- client_id_metadata_document_supported: z.boolean().optional()
34341
- });
34654
+ class InvalidGrantError extends OAuthError {
34655
+ }
34656
+ errors$2.InvalidGrantError = InvalidGrantError;
34657
+ InvalidGrantError.errorCode = 'invalid_grant';
34342
34658
  /**
34343
- * OpenID Connect Discovery metadata that may include OAuth 2.0 fields
34344
- * This schema represents the real-world scenario where OIDC providers
34345
- * return a mix of OpenID Connect and OAuth 2.0 metadata fields
34659
+ * Unauthorized client error - The authenticated client is not authorized to use
34660
+ * this authorization grant type.
34346
34661
  */
34347
- exports.OpenIdProviderDiscoveryMetadataSchema = z.object({
34348
- ...exports.OpenIdProviderMetadataSchema.shape,
34349
- ...exports.OAuthMetadataSchema.pick({
34350
- code_challenge_methods_supported: true
34351
- }).shape
34352
- });
34662
+ class UnauthorizedClientError extends OAuthError {
34663
+ }
34664
+ errors$2.UnauthorizedClientError = UnauthorizedClientError;
34665
+ UnauthorizedClientError.errorCode = 'unauthorized_client';
34353
34666
  /**
34354
- * OAuth 2.1 token response
34667
+ * Unsupported grant type error - The authorization grant type is not supported
34668
+ * by the authorization server.
34355
34669
  */
34356
- exports.OAuthTokensSchema = z
34357
- .object({
34358
- access_token: z.string(),
34359
- id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
34360
- token_type: z.string(),
34361
- expires_in: z.coerce.number().optional(),
34362
- scope: z.string().optional(),
34363
- refresh_token: z.string().optional()
34364
- })
34365
- .strip();
34670
+ class UnsupportedGrantTypeError extends OAuthError {
34671
+ }
34672
+ errors$2.UnsupportedGrantTypeError = UnsupportedGrantTypeError;
34673
+ UnsupportedGrantTypeError.errorCode = 'unsupported_grant_type';
34366
34674
  /**
34367
- * OAuth 2.1 error response
34675
+ * Invalid scope error - The requested scope is invalid, unknown, malformed, or
34676
+ * exceeds the scope granted by the resource owner.
34368
34677
  */
34369
- exports.OAuthErrorResponseSchema = z.object({
34370
- error: z.string(),
34371
- error_description: z.string().optional(),
34372
- error_uri: z.string().optional()
34373
- });
34678
+ class InvalidScopeError extends OAuthError {
34679
+ }
34680
+ errors$2.InvalidScopeError = InvalidScopeError;
34681
+ InvalidScopeError.errorCode = 'invalid_scope';
34682
+ /**
34683
+ * Access denied error - The resource owner or authorization server denied the request.
34684
+ */
34685
+ class AccessDeniedError extends OAuthError {
34686
+ }
34687
+ errors$2.AccessDeniedError = AccessDeniedError;
34688
+ AccessDeniedError.errorCode = 'access_denied';
34374
34689
  /**
34375
- * Optional version of SafeUrlSchema that allows empty string for retrocompatibility on tos_uri and logo_uri
34690
+ * Server error - The authorization server encountered an unexpected condition
34691
+ * that prevented it from fulfilling the request.
34376
34692
  */
34377
- exports.OptionalSafeUrlSchema = exports.SafeUrlSchema.optional().or(z.literal('').transform(() => undefined));
34693
+ class ServerError extends OAuthError {
34694
+ }
34695
+ errors$2.ServerError = ServerError;
34696
+ ServerError.errorCode = 'server_error';
34378
34697
  /**
34379
- * RFC 7591 OAuth 2.0 Dynamic Client Registration metadata
34698
+ * Temporarily unavailable error - The authorization server is currently unable to
34699
+ * handle the request due to a temporary overloading or maintenance of the server.
34380
34700
  */
34381
- exports.OAuthClientMetadataSchema = z
34382
- .object({
34383
- redirect_uris: z.array(exports.SafeUrlSchema),
34384
- token_endpoint_auth_method: z.string().optional(),
34385
- grant_types: z.array(z.string()).optional(),
34386
- response_types: z.array(z.string()).optional(),
34387
- client_name: z.string().optional(),
34388
- client_uri: exports.SafeUrlSchema.optional(),
34389
- logo_uri: exports.OptionalSafeUrlSchema,
34390
- scope: z.string().optional(),
34391
- contacts: z.array(z.string()).optional(),
34392
- tos_uri: exports.OptionalSafeUrlSchema,
34393
- policy_uri: z.string().optional(),
34394
- jwks_uri: exports.SafeUrlSchema.optional(),
34395
- jwks: z.any().optional(),
34396
- software_id: z.string().optional(),
34397
- software_version: z.string().optional(),
34398
- software_statement: z.string().optional()
34399
- })
34400
- .strip();
34701
+ class TemporarilyUnavailableError extends OAuthError {
34702
+ }
34703
+ errors$2.TemporarilyUnavailableError = TemporarilyUnavailableError;
34704
+ TemporarilyUnavailableError.errorCode = 'temporarily_unavailable';
34401
34705
  /**
34402
- * RFC 7591 OAuth 2.0 Dynamic Client Registration client information
34706
+ * Unsupported response type error - The authorization server does not support
34707
+ * obtaining an authorization code using this method.
34403
34708
  */
34404
- exports.OAuthClientInformationSchema = z
34405
- .object({
34406
- client_id: z.string(),
34407
- client_secret: z.string().optional(),
34408
- client_id_issued_at: z.number().optional(),
34409
- client_secret_expires_at: z.number().optional()
34410
- })
34411
- .strip();
34709
+ class UnsupportedResponseTypeError extends OAuthError {
34710
+ }
34711
+ errors$2.UnsupportedResponseTypeError = UnsupportedResponseTypeError;
34712
+ UnsupportedResponseTypeError.errorCode = 'unsupported_response_type';
34412
34713
  /**
34413
- * RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
34714
+ * Unsupported token type error - The authorization server does not support
34715
+ * the requested token type.
34414
34716
  */
34415
- exports.OAuthClientInformationFullSchema = exports.OAuthClientMetadataSchema.merge(exports.OAuthClientInformationSchema);
34717
+ class UnsupportedTokenTypeError extends OAuthError {
34718
+ }
34719
+ errors$2.UnsupportedTokenTypeError = UnsupportedTokenTypeError;
34720
+ UnsupportedTokenTypeError.errorCode = 'unsupported_token_type';
34416
34721
  /**
34417
- * RFC 7591 OAuth 2.0 Dynamic Client Registration error response
34722
+ * Invalid token error - The access token provided is expired, revoked, malformed,
34723
+ * or invalid for other reasons.
34418
34724
  */
34419
- exports.OAuthClientRegistrationErrorSchema = z
34420
- .object({
34421
- error: z.string(),
34422
- error_description: z.string().optional()
34423
- })
34424
- .strip();
34725
+ class InvalidTokenError extends OAuthError {
34726
+ }
34727
+ errors$2.InvalidTokenError = InvalidTokenError;
34728
+ InvalidTokenError.errorCode = 'invalid_token';
34425
34729
  /**
34426
- * RFC 7009 OAuth 2.0 Token Revocation request
34730
+ * Method not allowed error - The HTTP method used is not allowed for this endpoint.
34731
+ * (Custom, non-standard error)
34427
34732
  */
34428
- exports.OAuthTokenRevocationRequestSchema = z
34429
- .object({
34430
- token: z.string(),
34431
- token_type_hint: z.string().optional()
34432
- })
34433
- .strip();
34733
+ class MethodNotAllowedError extends OAuthError {
34734
+ }
34735
+ errors$2.MethodNotAllowedError = MethodNotAllowedError;
34736
+ MethodNotAllowedError.errorCode = 'method_not_allowed';
34737
+ /**
34738
+ * Too many requests error - Rate limit exceeded.
34739
+ * (Custom, non-standard error based on RFC 6585)
34740
+ */
34741
+ class TooManyRequestsError extends OAuthError {
34742
+ }
34743
+ errors$2.TooManyRequestsError = TooManyRequestsError;
34744
+ TooManyRequestsError.errorCode = 'too_many_requests';
34745
+ /**
34746
+ * Invalid client metadata error - The client metadata is invalid.
34747
+ * (Custom error for dynamic client registration - RFC 7591)
34748
+ */
34749
+ class InvalidClientMetadataError extends OAuthError {
34750
+ }
34751
+ errors$2.InvalidClientMetadataError = InvalidClientMetadataError;
34752
+ InvalidClientMetadataError.errorCode = 'invalid_client_metadata';
34753
+ /**
34754
+ * Insufficient scope error - The request requires higher privileges than provided by the access token.
34755
+ */
34756
+ class InsufficientScopeError extends OAuthError {
34757
+ }
34758
+ errors$2.InsufficientScopeError = InsufficientScopeError;
34759
+ InsufficientScopeError.errorCode = 'insufficient_scope';
34760
+ /**
34761
+ * Invalid target error - The requested resource is invalid, missing, unknown, or malformed.
34762
+ * (Custom error for resource indicators - RFC 8707)
34763
+ */
34764
+ class InvalidTargetError extends OAuthError {
34765
+ }
34766
+ errors$2.InvalidTargetError = InvalidTargetError;
34767
+ InvalidTargetError.errorCode = 'invalid_target';
34768
+ /**
34769
+ * A utility class for defining one-off error codes
34770
+ */
34771
+ class CustomOAuthError extends OAuthError {
34772
+ constructor(customErrorCode, message, errorUri) {
34773
+ super(message, errorUri);
34774
+ this.customErrorCode = customErrorCode;
34775
+ }
34776
+ get errorCode() {
34777
+ return this.customErrorCode;
34778
+ }
34779
+ }
34780
+ errors$2.CustomOAuthError = CustomOAuthError;
34781
+ /**
34782
+ * A full list of all OAuthErrors, enabling parsing from error responses
34783
+ */
34784
+ errors$2.OAUTH_ERRORS = {
34785
+ [InvalidRequestError.errorCode]: InvalidRequestError,
34786
+ [InvalidClientError.errorCode]: InvalidClientError,
34787
+ [InvalidGrantError.errorCode]: InvalidGrantError,
34788
+ [UnauthorizedClientError.errorCode]: UnauthorizedClientError,
34789
+ [UnsupportedGrantTypeError.errorCode]: UnsupportedGrantTypeError,
34790
+ [InvalidScopeError.errorCode]: InvalidScopeError,
34791
+ [AccessDeniedError.errorCode]: AccessDeniedError,
34792
+ [ServerError.errorCode]: ServerError,
34793
+ [TemporarilyUnavailableError.errorCode]: TemporarilyUnavailableError,
34794
+ [UnsupportedResponseTypeError.errorCode]: UnsupportedResponseTypeError,
34795
+ [UnsupportedTokenTypeError.errorCode]: UnsupportedTokenTypeError,
34796
+ [InvalidTokenError.errorCode]: InvalidTokenError,
34797
+ [MethodNotAllowedError.errorCode]: MethodNotAllowedError,
34798
+ [TooManyRequestsError.errorCode]: TooManyRequestsError,
34799
+ [InvalidClientMetadataError.errorCode]: InvalidClientMetadataError,
34800
+ [InsufficientScopeError.errorCode]: InsufficientScopeError,
34801
+ [InvalidTargetError.errorCode]: InvalidTargetError
34802
+ };
34434
34803
 
34435
- } (auth$1));
34436
-
34437
- var authUtils = {};
34438
-
34439
- /**
34440
- * Utilities for handling OAuth resource URIs.
34441
- */
34442
- Object.defineProperty(authUtils, "__esModule", { value: true });
34443
- authUtils.resourceUrlFromServerUrl = resourceUrlFromServerUrl;
34444
- authUtils.checkResourceAllowed = checkResourceAllowed;
34445
- /**
34446
- * Converts a server URL to a resource URL by removing the fragment.
34447
- * RFC 8707 section 2 states that resource URIs "MUST NOT include a fragment component".
34448
- * Keeps everything else unchanged (scheme, domain, port, path, query).
34449
- */
34450
- function resourceUrlFromServerUrl(url) {
34451
- const resourceURL = typeof url === 'string' ? new URL(url) : new URL(url.href);
34452
- resourceURL.hash = ''; // Remove fragment
34453
- return resourceURL;
34804
+ return errors$2;
34454
34805
  }
34455
- /**
34456
- * Checks if a requested resource URL matches a configured resource URL.
34457
- * A requested resource matches if it has the same scheme, domain, port,
34458
- * and its path starts with the configured resource's path.
34459
- *
34460
- * @param requestedResource The resource URL being requested
34461
- * @param configuredResource The resource URL that has been configured
34462
- * @returns true if the requested resource matches the configured resource, false otherwise
34463
- */
34464
- function checkResourceAllowed({ requestedResource, configuredResource }) {
34465
- const requested = typeof requestedResource === 'string' ? new URL(requestedResource) : new URL(requestedResource.href);
34466
- const configured = typeof configuredResource === 'string' ? new URL(configuredResource) : new URL(configuredResource.href);
34467
- // Compare the origin (scheme, domain, and port)
34468
- if (requested.origin !== configured.origin) {
34469
- return false;
34470
- }
34471
- // Handle cases like requested=/foo and configured=/foo/
34472
- if (requested.pathname.length < configured.pathname.length) {
34473
- return false;
34474
- }
34475
- // Check if the requested path starts with the configured path
34476
- // Ensure both paths end with / for proper comparison
34477
- // This ensures that if we have paths like "/api" and "/api/users",
34478
- // we properly detect that "/api/users" is a subpath of "/api"
34479
- // By adding a trailing slash if missing, we avoid false positives
34480
- // where paths like "/api123" would incorrectly match "/api"
34481
- const requestedPath = requested.pathname.endsWith('/') ? requested.pathname : requested.pathname + '/';
34482
- const configuredPath = configured.pathname.endsWith('/') ? configured.pathname : configured.pathname + '/';
34483
- return requestedPath.startsWith(configuredPath);
34484
- }
34485
-
34486
- var errors$2 = {};
34487
34806
 
34488
- Object.defineProperty(errors$2, "__esModule", { value: true });
34489
- errors$2.OAUTH_ERRORS = errors$2.CustomOAuthError = errors$2.InvalidTargetError = errors$2.InsufficientScopeError = errors$2.InvalidClientMetadataError = errors$2.TooManyRequestsError = errors$2.MethodNotAllowedError = errors$2.InvalidTokenError = errors$2.UnsupportedTokenTypeError = errors$2.UnsupportedResponseTypeError = errors$2.TemporarilyUnavailableError = errors$2.ServerError = errors$2.AccessDeniedError = errors$2.InvalidScopeError = errors$2.UnsupportedGrantTypeError = errors$2.UnauthorizedClientError = errors$2.InvalidGrantError = errors$2.InvalidClientError = errors$2.InvalidRequestError = errors$2.OAuthError = void 0;
34490
- /**
34491
- * Base class for all OAuth errors
34492
- */
34493
- class OAuthError extends Error {
34494
- constructor(message, errorUri) {
34495
- super(message);
34496
- this.errorUri = errorUri;
34497
- this.name = this.constructor.name;
34498
- }
34499
- /**
34500
- * Converts the error to a standard OAuth error response object
34501
- */
34502
- toResponseObject() {
34503
- const response = {
34504
- error: this.errorCode,
34505
- error_description: this.message
34506
- };
34507
- if (this.errorUri) {
34508
- response.error_uri = this.errorUri;
34509
- }
34510
- return response;
34511
- }
34512
- get errorCode() {
34513
- return this.constructor.errorCode;
34514
- }
34515
- }
34516
- errors$2.OAuthError = OAuthError;
34517
- /**
34518
- * Invalid request error - The request is missing a required parameter,
34519
- * includes an invalid parameter value, includes a parameter more than once,
34520
- * or is otherwise malformed.
34521
- */
34522
- class InvalidRequestError extends OAuthError {
34523
- }
34524
- errors$2.InvalidRequestError = InvalidRequestError;
34525
- InvalidRequestError.errorCode = 'invalid_request';
34526
- /**
34527
- * Invalid client error - Client authentication failed (e.g., unknown client, no client
34528
- * authentication included, or unsupported authentication method).
34529
- */
34530
- class InvalidClientError extends OAuthError {
34531
- }
34532
- errors$2.InvalidClientError = InvalidClientError;
34533
- InvalidClientError.errorCode = 'invalid_client';
34534
- /**
34535
- * Invalid grant error - The provided authorization grant or refresh token is
34536
- * invalid, expired, revoked, does not match the redirection URI used in the
34537
- * authorization request, or was issued to another client.
34538
- */
34539
- class InvalidGrantError extends OAuthError {
34540
- }
34541
- errors$2.InvalidGrantError = InvalidGrantError;
34542
- InvalidGrantError.errorCode = 'invalid_grant';
34543
- /**
34544
- * Unauthorized client error - The authenticated client is not authorized to use
34545
- * this authorization grant type.
34546
- */
34547
- class UnauthorizedClientError extends OAuthError {
34548
- }
34549
- errors$2.UnauthorizedClientError = UnauthorizedClientError;
34550
- UnauthorizedClientError.errorCode = 'unauthorized_client';
34551
- /**
34552
- * Unsupported grant type error - The authorization grant type is not supported
34553
- * by the authorization server.
34554
- */
34555
- class UnsupportedGrantTypeError extends OAuthError {
34556
- }
34557
- errors$2.UnsupportedGrantTypeError = UnsupportedGrantTypeError;
34558
- UnsupportedGrantTypeError.errorCode = 'unsupported_grant_type';
34559
- /**
34560
- * Invalid scope error - The requested scope is invalid, unknown, malformed, or
34561
- * exceeds the scope granted by the resource owner.
34562
- */
34563
- class InvalidScopeError extends OAuthError {
34564
- }
34565
- errors$2.InvalidScopeError = InvalidScopeError;
34566
- InvalidScopeError.errorCode = 'invalid_scope';
34567
- /**
34568
- * Access denied error - The resource owner or authorization server denied the request.
34569
- */
34570
- class AccessDeniedError extends OAuthError {
34571
- }
34572
- errors$2.AccessDeniedError = AccessDeniedError;
34573
- AccessDeniedError.errorCode = 'access_denied';
34574
- /**
34575
- * Server error - The authorization server encountered an unexpected condition
34576
- * that prevented it from fulfilling the request.
34577
- */
34578
- class ServerError extends OAuthError {
34579
- }
34580
- errors$2.ServerError = ServerError;
34581
- ServerError.errorCode = 'server_error';
34582
- /**
34583
- * Temporarily unavailable error - The authorization server is currently unable to
34584
- * handle the request due to a temporary overloading or maintenance of the server.
34585
- */
34586
- class TemporarilyUnavailableError extends OAuthError {
34587
- }
34588
- errors$2.TemporarilyUnavailableError = TemporarilyUnavailableError;
34589
- TemporarilyUnavailableError.errorCode = 'temporarily_unavailable';
34590
- /**
34591
- * Unsupported response type error - The authorization server does not support
34592
- * obtaining an authorization code using this method.
34593
- */
34594
- class UnsupportedResponseTypeError extends OAuthError {
34595
- }
34596
- errors$2.UnsupportedResponseTypeError = UnsupportedResponseTypeError;
34597
- UnsupportedResponseTypeError.errorCode = 'unsupported_response_type';
34598
- /**
34599
- * Unsupported token type error - The authorization server does not support
34600
- * the requested token type.
34601
- */
34602
- class UnsupportedTokenTypeError extends OAuthError {
34603
- }
34604
- errors$2.UnsupportedTokenTypeError = UnsupportedTokenTypeError;
34605
- UnsupportedTokenTypeError.errorCode = 'unsupported_token_type';
34606
- /**
34607
- * Invalid token error - The access token provided is expired, revoked, malformed,
34608
- * or invalid for other reasons.
34609
- */
34610
- class InvalidTokenError extends OAuthError {
34611
- }
34612
- errors$2.InvalidTokenError = InvalidTokenError;
34613
- InvalidTokenError.errorCode = 'invalid_token';
34614
- /**
34615
- * Method not allowed error - The HTTP method used is not allowed for this endpoint.
34616
- * (Custom, non-standard error)
34617
- */
34618
- class MethodNotAllowedError extends OAuthError {
34619
- }
34620
- errors$2.MethodNotAllowedError = MethodNotAllowedError;
34621
- MethodNotAllowedError.errorCode = 'method_not_allowed';
34622
- /**
34623
- * Too many requests error - Rate limit exceeded.
34624
- * (Custom, non-standard error based on RFC 6585)
34625
- */
34626
- class TooManyRequestsError extends OAuthError {
34627
- }
34628
- errors$2.TooManyRequestsError = TooManyRequestsError;
34629
- TooManyRequestsError.errorCode = 'too_many_requests';
34630
- /**
34631
- * Invalid client metadata error - The client metadata is invalid.
34632
- * (Custom error for dynamic client registration - RFC 7591)
34633
- */
34634
- class InvalidClientMetadataError extends OAuthError {
34635
- }
34636
- errors$2.InvalidClientMetadataError = InvalidClientMetadataError;
34637
- InvalidClientMetadataError.errorCode = 'invalid_client_metadata';
34638
- /**
34639
- * Insufficient scope error - The request requires higher privileges than provided by the access token.
34640
- */
34641
- class InsufficientScopeError extends OAuthError {
34642
- }
34643
- errors$2.InsufficientScopeError = InsufficientScopeError;
34644
- InsufficientScopeError.errorCode = 'insufficient_scope';
34645
- /**
34646
- * Invalid target error - The requested resource is invalid, missing, unknown, or malformed.
34647
- * (Custom error for resource indicators - RFC 8707)
34648
- */
34649
- class InvalidTargetError extends OAuthError {
34650
- }
34651
- errors$2.InvalidTargetError = InvalidTargetError;
34652
- InvalidTargetError.errorCode = 'invalid_target';
34653
- /**
34654
- * A utility class for defining one-off error codes
34655
- */
34656
- class CustomOAuthError extends OAuthError {
34657
- constructor(customErrorCode, message, errorUri) {
34658
- super(message, errorUri);
34659
- this.customErrorCode = customErrorCode;
34660
- }
34661
- get errorCode() {
34662
- return this.customErrorCode;
34663
- }
34664
- }
34665
- errors$2.CustomOAuthError = CustomOAuthError;
34666
- /**
34667
- * A full list of all OAuthErrors, enabling parsing from error responses
34668
- */
34669
- errors$2.OAUTH_ERRORS = {
34670
- [InvalidRequestError.errorCode]: InvalidRequestError,
34671
- [InvalidClientError.errorCode]: InvalidClientError,
34672
- [InvalidGrantError.errorCode]: InvalidGrantError,
34673
- [UnauthorizedClientError.errorCode]: UnauthorizedClientError,
34674
- [UnsupportedGrantTypeError.errorCode]: UnsupportedGrantTypeError,
34675
- [InvalidScopeError.errorCode]: InvalidScopeError,
34676
- [AccessDeniedError.errorCode]: AccessDeniedError,
34677
- [ServerError.errorCode]: ServerError,
34678
- [TemporarilyUnavailableError.errorCode]: TemporarilyUnavailableError,
34679
- [UnsupportedResponseTypeError.errorCode]: UnsupportedResponseTypeError,
34680
- [UnsupportedTokenTypeError.errorCode]: UnsupportedTokenTypeError,
34681
- [InvalidTokenError.errorCode]: InvalidTokenError,
34682
- [MethodNotAllowedError.errorCode]: MethodNotAllowedError,
34683
- [TooManyRequestsError.errorCode]: TooManyRequestsError,
34684
- [InvalidClientMetadataError.errorCode]: InvalidClientMetadataError,
34685
- [InsufficientScopeError.errorCode]: InsufficientScopeError,
34686
- [InvalidTargetError.errorCode]: InvalidTargetError
34687
- };
34807
+ var hasRequiredAuth;
34688
34808
 
34689
- var __importDefault$2 = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
34690
- return (mod && mod.__esModule) ? mod : { "default": mod };
34691
- };
34692
- Object.defineProperty(auth$2, "__esModule", { value: true });
34693
- auth$2.UnauthorizedError = void 0;
34694
- auth$2.selectClientAuthMethod = selectClientAuthMethod;
34695
- auth$2.parseErrorResponse = parseErrorResponse;
34696
- auth$2.auth = auth;
34697
- auth$2.isHttpsUrl = isHttpsUrl;
34698
- auth$2.selectResourceURL = selectResourceURL;
34699
- auth$2.extractWWWAuthenticateParams = extractWWWAuthenticateParams;
34700
- auth$2.extractResourceMetadataUrl = extractResourceMetadataUrl;
34701
- auth$2.discoverOAuthProtectedResourceMetadata = discoverOAuthProtectedResourceMetadata;
34702
- auth$2.discoverOAuthMetadata = discoverOAuthMetadata;
34703
- auth$2.buildDiscoveryUrls = buildDiscoveryUrls;
34704
- auth$2.discoverAuthorizationServerMetadata = discoverAuthorizationServerMetadata;
34705
- auth$2.discoverOAuthServerInfo = discoverOAuthServerInfo;
34706
- auth$2.startAuthorization = startAuthorization;
34707
- auth$2.prepareAuthorizationCodeRequest = prepareAuthorizationCodeRequest;
34708
- auth$2.exchangeAuthorization = exchangeAuthorization;
34709
- auth$2.refreshAuthorization = refreshAuthorization;
34710
- auth$2.fetchToken = fetchToken;
34711
- auth$2.registerClient = registerClient;
34712
- const pkce_challenge_1 = __importDefault$2(require$$0$7);
34713
- const types_js_1$5 = types$2;
34714
- const auth_js_1$1 = auth$1;
34715
- const auth_js_2 = auth$1;
34716
- const auth_utils_js_1 = authUtils;
34717
- const errors_js_1$1 = errors$2;
34718
- class UnauthorizedError extends Error {
34719
- constructor(message) {
34720
- super(message ?? 'Unauthorized');
34721
- }
34722
- }
34723
- auth$2.UnauthorizedError = UnauthorizedError;
34724
- function isClientAuthMethod(method) {
34725
- return ['client_secret_basic', 'client_secret_post', 'none'].includes(method);
34726
- }
34727
- const AUTHORIZATION_CODE_RESPONSE_TYPE = 'code';
34728
- const AUTHORIZATION_CODE_CHALLENGE_METHOD = 'S256';
34729
- /**
34730
- * Determines the best client authentication method to use based on server support and client configuration.
34731
- *
34732
- * Priority order (highest to lowest):
34733
- * 1. client_secret_basic (if client secret is available)
34734
- * 2. client_secret_post (if client secret is available)
34735
- * 3. none (for public clients)
34736
- *
34737
- * @param clientInformation - OAuth client information containing credentials
34738
- * @param supportedMethods - Authentication methods supported by the authorization server
34739
- * @returns The selected authentication method
34740
- */
34741
- function selectClientAuthMethod(clientInformation, supportedMethods) {
34742
- const hasClientSecret = clientInformation.client_secret !== undefined;
34743
- // Prefer the method returned by the server during client registration, if valid.
34744
- // When server metadata is present we also require the method to be listed as supported;
34745
- // when supportedMethods is empty (metadata omitted the field) the DCR hint stands alone.
34746
- if ('token_endpoint_auth_method' in clientInformation &&
34747
- clientInformation.token_endpoint_auth_method &&
34748
- isClientAuthMethod(clientInformation.token_endpoint_auth_method) &&
34749
- (supportedMethods.length === 0 || supportedMethods.includes(clientInformation.token_endpoint_auth_method))) {
34750
- return clientInformation.token_endpoint_auth_method;
34751
- }
34752
- // If server metadata omits token_endpoint_auth_methods_supported, RFC 8414 §2 says the
34753
- // default is client_secret_basic. RFC 6749 §2.3.1 also requires servers to support HTTP
34754
- // Basic authentication for clients with a secret, making it the safest default.
34755
- if (supportedMethods.length === 0) {
34756
- return hasClientSecret ? 'client_secret_basic' : 'none';
34757
- }
34758
- // Try methods in priority order (most secure first)
34759
- if (hasClientSecret && supportedMethods.includes('client_secret_basic')) {
34760
- return 'client_secret_basic';
34761
- }
34762
- if (hasClientSecret && supportedMethods.includes('client_secret_post')) {
34763
- return 'client_secret_post';
34764
- }
34765
- if (supportedMethods.includes('none')) {
34766
- return 'none';
34767
- }
34768
- // Fallback: use what we have
34769
- return hasClientSecret ? 'client_secret_post' : 'none';
34770
- }
34771
- /**
34772
- * Applies client authentication to the request based on the specified method.
34773
- *
34774
- * Implements OAuth 2.1 client authentication methods:
34775
- * - client_secret_basic: HTTP Basic authentication (RFC 6749 Section 2.3.1)
34776
- * - client_secret_post: Credentials in request body (RFC 6749 Section 2.3.1)
34777
- * - none: Public client authentication (RFC 6749 Section 2.1)
34778
- *
34779
- * @param method - The authentication method to use
34780
- * @param clientInformation - OAuth client information containing credentials
34781
- * @param headers - HTTP headers object to modify
34782
- * @param params - URL search parameters to modify
34783
- * @throws {Error} When required credentials are missing
34784
- */
34785
- function applyClientAuthentication(method, clientInformation, headers, params) {
34786
- const { client_id, client_secret } = clientInformation;
34787
- switch (method) {
34788
- case 'client_secret_basic':
34789
- applyBasicAuth(client_id, client_secret, headers);
34790
- return;
34791
- case 'client_secret_post':
34792
- applyPostAuth(client_id, client_secret, params);
34793
- return;
34794
- case 'none':
34795
- applyPublicAuth(client_id, params);
34796
- return;
34797
- default:
34798
- throw new Error(`Unsupported client authentication method: ${method}`);
34799
- }
34800
- }
34801
- /**
34802
- * Applies HTTP Basic authentication (RFC 6749 Section 2.3.1)
34803
- */
34804
- function applyBasicAuth(clientId, clientSecret, headers) {
34805
- if (!clientSecret) {
34806
- throw new Error('client_secret_basic authentication requires a client_secret');
34807
- }
34808
- const credentials = btoa(`${clientId}:${clientSecret}`);
34809
- headers.set('Authorization', `Basic ${credentials}`);
34810
- }
34811
- /**
34812
- * Applies POST body authentication (RFC 6749 Section 2.3.1)
34813
- */
34814
- function applyPostAuth(clientId, clientSecret, params) {
34815
- params.set('client_id', clientId);
34816
- if (clientSecret) {
34817
- params.set('client_secret', clientSecret);
34818
- }
34819
- }
34820
- /**
34821
- * Applies public client authentication (RFC 6749 Section 2.1)
34822
- */
34823
- function applyPublicAuth(clientId, params) {
34824
- params.set('client_id', clientId);
34825
- }
34826
- /**
34827
- * Parses an OAuth error response from a string or Response object.
34828
- *
34829
- * If the input is a standard OAuth2.0 error response, it will be parsed according to the spec
34830
- * and an instance of the appropriate OAuthError subclass will be returned.
34831
- * If parsing fails, it falls back to a generic ServerError that includes
34832
- * the response status (if available) and original content.
34833
- *
34834
- * @param input - A Response object or string containing the error response
34835
- * @returns A Promise that resolves to an OAuthError instance
34836
- */
34837
- async function parseErrorResponse(input) {
34838
- const statusCode = input instanceof Response ? input.status : undefined;
34839
- const body = input instanceof Response ? await input.text() : input;
34840
- try {
34841
- const result = auth_js_1$1.OAuthErrorResponseSchema.parse(JSON.parse(body));
34842
- const { error, error_description, error_uri } = result;
34843
- const errorClass = errors_js_1$1.OAUTH_ERRORS[error] || errors_js_1$1.ServerError;
34844
- return new errorClass(error_description || '', error_uri);
34845
- }
34846
- catch (error) {
34847
- // Not a valid OAuth error response, but try to inform the user of the raw data anyway
34848
- const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ''}Invalid OAuth error response: ${error}. Raw body: ${body}`;
34849
- return new errors_js_1$1.ServerError(errorMessage);
34850
- }
34851
- }
34852
- /**
34853
- * Orchestrates the full auth flow with a server.
34854
- *
34855
- * This can be used as a single entry point for all authorization functionality,
34856
- * instead of linking together the other lower-level functions in this module.
34857
- */
34858
- async function auth(provider, options) {
34859
- try {
34860
- return await authInternal(provider, options);
34861
- }
34862
- catch (error) {
34863
- // Handle recoverable error types by invalidating credentials and retrying
34864
- if (error instanceof errors_js_1$1.InvalidClientError || error instanceof errors_js_1$1.UnauthorizedClientError) {
34865
- await provider.invalidateCredentials?.('all');
34866
- return await authInternal(provider, options);
34867
- }
34868
- else if (error instanceof errors_js_1$1.InvalidGrantError) {
34869
- await provider.invalidateCredentials?.('tokens');
34870
- return await authInternal(provider, options);
34871
- }
34872
- // Throw otherwise
34873
- throw error;
34874
- }
34875
- }
34876
- async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
34877
- // Check if the provider has cached discovery state to skip discovery
34878
- const cachedState = await provider.discoveryState?.();
34879
- let resourceMetadata;
34880
- let authorizationServerUrl;
34881
- let metadata;
34882
- // If resourceMetadataUrl is not provided, try to load it from cached state
34883
- // This handles browser redirects where the URL was saved before navigation
34884
- let effectiveResourceMetadataUrl = resourceMetadataUrl;
34885
- if (!effectiveResourceMetadataUrl && cachedState?.resourceMetadataUrl) {
34886
- effectiveResourceMetadataUrl = new URL(cachedState.resourceMetadataUrl);
34887
- }
34888
- if (cachedState?.authorizationServerUrl) {
34889
- // Restore discovery state from cache
34890
- authorizationServerUrl = cachedState.authorizationServerUrl;
34891
- resourceMetadata = cachedState.resourceMetadata;
34892
- metadata =
34893
- cachedState.authorizationServerMetadata ?? (await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn }));
34894
- // If resource metadata wasn't cached, try to fetch it for selectResourceURL
34895
- if (!resourceMetadata) {
34896
- try {
34897
- resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl }, fetchFn);
34898
- }
34899
- catch {
34900
- // RFC 9728 not available — selectResourceURL will handle undefined
34901
- }
34902
- }
34903
- // Re-save if we enriched the cached state with missing metadata
34904
- if (metadata !== cachedState.authorizationServerMetadata || resourceMetadata !== cachedState.resourceMetadata) {
34905
- await provider.saveDiscoveryState?.({
34906
- authorizationServerUrl: String(authorizationServerUrl),
34907
- resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
34908
- resourceMetadata,
34909
- authorizationServerMetadata: metadata
34910
- });
34911
- }
34912
- }
34913
- else {
34914
- // Full discovery via RFC 9728
34915
- const serverInfo = await discoverOAuthServerInfo(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl, fetchFn });
34916
- authorizationServerUrl = serverInfo.authorizationServerUrl;
34917
- metadata = serverInfo.authorizationServerMetadata;
34918
- resourceMetadata = serverInfo.resourceMetadata;
34919
- // Persist discovery state for future use
34920
- // TODO: resourceMetadataUrl is only populated when explicitly provided via options
34921
- // or loaded from cached state. The URL derived internally by
34922
- // discoverOAuthProtectedResourceMetadata() is not captured back here.
34923
- await provider.saveDiscoveryState?.({
34924
- authorizationServerUrl: String(authorizationServerUrl),
34925
- resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
34926
- resourceMetadata,
34927
- authorizationServerMetadata: metadata
34928
- });
34929
- }
34930
- const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
34931
- // Apply scope selection strategy (SEP-835):
34932
- // 1. WWW-Authenticate scope (passed via `scope` param)
34933
- // 2. PRM scopes_supported
34934
- // 3. Client metadata scope (user-configured fallback)
34935
- // The resolved scope is used consistently for both DCR and the authorization request.
34936
- const resolvedScope = scope || resourceMetadata?.scopes_supported?.join(' ') || provider.clientMetadata.scope;
34937
- // Handle client registration if needed
34938
- let clientInformation = await Promise.resolve(provider.clientInformation());
34939
- if (!clientInformation) {
34940
- if (authorizationCode !== undefined) {
34941
- throw new Error('Existing OAuth client information is required when exchanging an authorization code');
34942
- }
34943
- const supportsUrlBasedClientId = metadata?.client_id_metadata_document_supported === true;
34944
- const clientMetadataUrl = provider.clientMetadataUrl;
34945
- if (clientMetadataUrl && !isHttpsUrl(clientMetadataUrl)) {
34946
- throw new errors_js_1$1.InvalidClientMetadataError(`clientMetadataUrl must be a valid HTTPS URL with a non-root pathname, got: ${clientMetadataUrl}`);
34947
- }
34948
- const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
34949
- if (shouldUseUrlBasedClientId) {
34950
- // SEP-991: URL-based Client IDs
34951
- clientInformation = {
34952
- client_id: clientMetadataUrl
34953
- };
34954
- await provider.saveClientInformation?.(clientInformation);
34955
- }
34956
- else {
34957
- // Fallback to dynamic registration
34958
- if (!provider.saveClientInformation) {
34959
- throw new Error('OAuth client information must be saveable for dynamic registration');
34960
- }
34961
- const fullInformation = await registerClient(authorizationServerUrl, {
34962
- metadata,
34963
- clientMetadata: provider.clientMetadata,
34964
- scope: resolvedScope,
34965
- fetchFn
34966
- });
34967
- await provider.saveClientInformation(fullInformation);
34968
- clientInformation = fullInformation;
34969
- }
34970
- }
34971
- // Non-interactive flows (e.g., client_credentials, jwt-bearer) don't need a redirect URL
34972
- const nonInteractiveFlow = !provider.redirectUrl;
34973
- // Exchange authorization code for tokens, or fetch tokens directly for non-interactive flows
34974
- if (authorizationCode !== undefined || nonInteractiveFlow) {
34975
- const tokens = await fetchToken(provider, authorizationServerUrl, {
34976
- metadata,
34977
- resource,
34978
- authorizationCode,
34979
- fetchFn
34980
- });
34981
- await provider.saveTokens(tokens);
34982
- return 'AUTHORIZED';
34983
- }
34984
- const tokens = await provider.tokens();
34985
- // Handle token refresh or new authorization
34986
- if (tokens?.refresh_token) {
34987
- try {
34988
- // Attempt to refresh the token
34989
- const newTokens = await refreshAuthorization(authorizationServerUrl, {
34990
- metadata,
34991
- clientInformation,
34992
- refreshToken: tokens.refresh_token,
34993
- resource,
34994
- addClientAuthentication: provider.addClientAuthentication,
34995
- fetchFn
34996
- });
34997
- await provider.saveTokens(newTokens);
34998
- return 'AUTHORIZED';
34999
- }
35000
- catch (error) {
35001
- // If this is a ServerError, or an unknown type, log it out and try to continue. Otherwise, escalate so we can fix things and retry.
35002
- if (!(error instanceof errors_js_1$1.OAuthError) || error instanceof errors_js_1$1.ServerError) ;
35003
- else {
35004
- // Refresh failed for another reason, re-throw
35005
- throw error;
35006
- }
35007
- }
35008
- }
35009
- const state = provider.state ? await provider.state() : undefined;
35010
- // Start new authorization flow
35011
- const { authorizationUrl, codeVerifier } = await startAuthorization(authorizationServerUrl, {
35012
- metadata,
35013
- clientInformation,
35014
- state,
35015
- redirectUrl: provider.redirectUrl,
35016
- scope: resolvedScope,
35017
- resource
35018
- });
35019
- await provider.saveCodeVerifier(codeVerifier);
35020
- await provider.redirectToAuthorization(authorizationUrl);
35021
- return 'REDIRECT';
35022
- }
35023
- /**
35024
- * SEP-991: URL-based Client IDs
35025
- * Validate that the client_id is a valid URL with https scheme
35026
- */
35027
- function isHttpsUrl(value) {
35028
- if (!value)
35029
- return false;
35030
- try {
35031
- const url = new URL(value);
35032
- return url.protocol === 'https:' && url.pathname !== '/';
35033
- }
35034
- catch {
35035
- return false;
35036
- }
35037
- }
35038
- async function selectResourceURL(serverUrl, provider, resourceMetadata) {
35039
- const defaultResource = (0, auth_utils_js_1.resourceUrlFromServerUrl)(serverUrl);
35040
- // If provider has custom validation, delegate to it
35041
- if (provider.validateResourceURL) {
35042
- return await provider.validateResourceURL(defaultResource, resourceMetadata?.resource);
35043
- }
35044
- // Only include resource parameter when Protected Resource Metadata is present
35045
- if (!resourceMetadata) {
35046
- return undefined;
35047
- }
35048
- // Validate that the metadata's resource is compatible with our request
35049
- if (!(0, auth_utils_js_1.checkResourceAllowed)({ requestedResource: defaultResource, configuredResource: resourceMetadata.resource })) {
35050
- throw new Error(`Protected resource ${resourceMetadata.resource} does not match expected ${defaultResource} (or origin)`);
35051
- }
35052
- // Prefer the resource from metadata since it's what the server is telling us to request
35053
- return new URL(resourceMetadata.resource);
35054
- }
35055
- /**
35056
- * Extract resource_metadata, scope, and error from WWW-Authenticate header.
35057
- */
35058
- function extractWWWAuthenticateParams(res) {
35059
- const authenticateHeader = res.headers.get('WWW-Authenticate');
35060
- if (!authenticateHeader) {
35061
- return {};
35062
- }
35063
- const [type, scheme] = authenticateHeader.split(' ');
35064
- if (type.toLowerCase() !== 'bearer' || !scheme) {
35065
- return {};
35066
- }
35067
- const resourceMetadataMatch = extractFieldFromWwwAuth(res, 'resource_metadata') || undefined;
35068
- let resourceMetadataUrl;
35069
- if (resourceMetadataMatch) {
35070
- try {
35071
- resourceMetadataUrl = new URL(resourceMetadataMatch);
35072
- }
35073
- catch {
35074
- // Ignore invalid URL
35075
- }
35076
- }
35077
- const scope = extractFieldFromWwwAuth(res, 'scope') || undefined;
35078
- const error = extractFieldFromWwwAuth(res, 'error') || undefined;
35079
- return {
35080
- resourceMetadataUrl,
35081
- scope,
35082
- error
35083
- };
35084
- }
35085
- /**
35086
- * Extracts a specific field's value from the WWW-Authenticate header string.
35087
- *
35088
- * @param response The HTTP response object containing the headers.
35089
- * @param fieldName The name of the field to extract (e.g., "realm", "nonce").
35090
- * @returns The field value
35091
- */
35092
- function extractFieldFromWwwAuth(response, fieldName) {
35093
- const wwwAuthHeader = response.headers.get('WWW-Authenticate');
35094
- if (!wwwAuthHeader) {
35095
- return null;
35096
- }
35097
- const pattern = new RegExp(`${fieldName}=(?:"([^"]+)"|([^\\s,]+))`);
35098
- const match = wwwAuthHeader.match(pattern);
35099
- if (match) {
35100
- // Pattern matches: field_name="value" or field_name=value (unquoted)
35101
- return match[1] || match[2];
35102
- }
35103
- return null;
35104
- }
35105
- /**
35106
- * Extract resource_metadata from response header.
35107
- * @deprecated Use `extractWWWAuthenticateParams` instead.
35108
- */
35109
- function extractResourceMetadataUrl(res) {
35110
- const authenticateHeader = res.headers.get('WWW-Authenticate');
35111
- if (!authenticateHeader) {
35112
- return undefined;
35113
- }
35114
- const [type, scheme] = authenticateHeader.split(' ');
35115
- if (type.toLowerCase() !== 'bearer' || !scheme) {
35116
- return undefined;
35117
- }
35118
- const regex = /resource_metadata="([^"]*)"/;
35119
- const match = regex.exec(authenticateHeader);
35120
- if (!match) {
35121
- return undefined;
35122
- }
35123
- try {
35124
- return new URL(match[1]);
35125
- }
35126
- catch {
35127
- return undefined;
35128
- }
35129
- }
35130
- /**
35131
- * Looks up RFC 9728 OAuth 2.0 Protected Resource Metadata.
35132
- *
35133
- * If the server returns a 404 for the well-known endpoint, this function will
35134
- * return `undefined`. Any other errors will be thrown as exceptions.
35135
- */
35136
- async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn = fetch) {
35137
- const response = await discoverMetadataWithFallback(serverUrl, 'oauth-protected-resource', fetchFn, {
35138
- protocolVersion: opts?.protocolVersion,
35139
- metadataUrl: opts?.resourceMetadataUrl
35140
- });
35141
- if (!response || response.status === 404) {
35142
- await response?.body?.cancel();
35143
- throw new Error(`Resource server does not implement OAuth 2.0 Protected Resource Metadata.`);
35144
- }
35145
- if (!response.ok) {
35146
- await response.body?.cancel();
35147
- throw new Error(`HTTP ${response.status} trying to load well-known OAuth protected resource metadata.`);
35148
- }
35149
- return auth_js_2.OAuthProtectedResourceMetadataSchema.parse(await response.json());
35150
- }
35151
- /**
35152
- * Helper function to handle fetch with CORS retry logic
35153
- */
35154
- async function fetchWithCorsRetry(url, headers, fetchFn = fetch) {
35155
- try {
35156
- return await fetchFn(url, { headers });
35157
- }
35158
- catch (error) {
35159
- if (error instanceof TypeError) {
35160
- if (headers) {
35161
- // CORS errors come back as TypeError, retry without headers
35162
- return fetchWithCorsRetry(url, undefined, fetchFn);
35163
- }
35164
- else {
35165
- // We're getting CORS errors on retry too, return undefined
35166
- return undefined;
35167
- }
35168
- }
35169
- throw error;
35170
- }
35171
- }
35172
- /**
35173
- * Constructs the well-known path for auth-related metadata discovery
35174
- */
35175
- function buildWellKnownPath(wellKnownPrefix, pathname = '', options = {}) {
35176
- // Strip trailing slash from pathname to avoid double slashes
35177
- if (pathname.endsWith('/')) {
35178
- pathname = pathname.slice(0, -1);
35179
- }
35180
- return options.prependPathname ? `${pathname}/.well-known/${wellKnownPrefix}` : `/.well-known/${wellKnownPrefix}${pathname}`;
35181
- }
35182
- /**
35183
- * Tries to discover OAuth metadata at a specific URL
35184
- */
35185
- async function tryMetadataDiscovery(url, protocolVersion, fetchFn = fetch) {
35186
- const headers = {
35187
- 'MCP-Protocol-Version': protocolVersion
35188
- };
35189
- return await fetchWithCorsRetry(url, headers, fetchFn);
35190
- }
35191
- /**
35192
- * Determines if fallback to root discovery should be attempted
35193
- */
35194
- function shouldAttemptFallback(response, pathname) {
35195
- return !response || (response.status >= 400 && response.status < 500 && pathname !== '/');
35196
- }
35197
- /**
35198
- * Generic function for discovering OAuth metadata with fallback support
35199
- */
35200
- async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
35201
- const issuer = new URL(serverUrl);
35202
- const protocolVersion = opts?.protocolVersion ?? types_js_1$5.LATEST_PROTOCOL_VERSION;
35203
- let url;
35204
- if (opts?.metadataUrl) {
35205
- url = new URL(opts.metadataUrl);
35206
- }
35207
- else {
35208
- // Try path-aware discovery first
35209
- const wellKnownPath = buildWellKnownPath(wellKnownType, issuer.pathname);
35210
- url = new URL(wellKnownPath, opts?.metadataServerUrl ?? issuer);
35211
- url.search = issuer.search;
35212
- }
35213
- let response = await tryMetadataDiscovery(url, protocolVersion, fetchFn);
35214
- // If path-aware discovery fails with 404 and we're not already at root, try fallback to root discovery
35215
- if (!opts?.metadataUrl && shouldAttemptFallback(response, issuer.pathname)) {
35216
- const rootUrl = new URL(`/.well-known/${wellKnownType}`, issuer);
35217
- response = await tryMetadataDiscovery(rootUrl, protocolVersion, fetchFn);
35218
- }
35219
- return response;
35220
- }
35221
- /**
35222
- * Looks up RFC 8414 OAuth 2.0 Authorization Server Metadata.
35223
- *
35224
- * If the server returns a 404 for the well-known endpoint, this function will
35225
- * return `undefined`. Any other errors will be thrown as exceptions.
35226
- *
35227
- * @deprecated This function is deprecated in favor of `discoverAuthorizationServerMetadata`.
35228
- */
35229
- async function discoverOAuthMetadata(issuer, { authorizationServerUrl, protocolVersion } = {}, fetchFn = fetch) {
35230
- if (typeof issuer === 'string') {
35231
- issuer = new URL(issuer);
35232
- }
35233
- if (!authorizationServerUrl) {
35234
- authorizationServerUrl = issuer;
35235
- }
35236
- if (typeof authorizationServerUrl === 'string') {
35237
- authorizationServerUrl = new URL(authorizationServerUrl);
35238
- }
35239
- protocolVersion ?? (protocolVersion = types_js_1$5.LATEST_PROTOCOL_VERSION);
35240
- const response = await discoverMetadataWithFallback(authorizationServerUrl, 'oauth-authorization-server', fetchFn, {
35241
- protocolVersion,
35242
- metadataServerUrl: authorizationServerUrl
35243
- });
35244
- if (!response || response.status === 404) {
35245
- await response?.body?.cancel();
35246
- return undefined;
35247
- }
35248
- if (!response.ok) {
35249
- await response.body?.cancel();
35250
- throw new Error(`HTTP ${response.status} trying to load well-known OAuth metadata`);
35251
- }
35252
- return auth_js_2.OAuthMetadataSchema.parse(await response.json());
35253
- }
35254
- /**
35255
- * Builds a list of discovery URLs to try for authorization server metadata.
35256
- * URLs are returned in priority order:
35257
- * 1. OAuth metadata at the given URL
35258
- * 2. OIDC metadata endpoints at the given URL
35259
- */
35260
- function buildDiscoveryUrls(authorizationServerUrl) {
35261
- const url = typeof authorizationServerUrl === 'string' ? new URL(authorizationServerUrl) : authorizationServerUrl;
35262
- const hasPath = url.pathname !== '/';
35263
- const urlsToTry = [];
35264
- if (!hasPath) {
35265
- // Root path: https://example.com/.well-known/oauth-authorization-server
35266
- urlsToTry.push({
35267
- url: new URL('/.well-known/oauth-authorization-server', url.origin),
35268
- type: 'oauth'
35269
- });
35270
- // OIDC: https://example.com/.well-known/openid-configuration
35271
- urlsToTry.push({
35272
- url: new URL(`/.well-known/openid-configuration`, url.origin),
35273
- type: 'oidc'
35274
- });
35275
- return urlsToTry;
35276
- }
35277
- // Strip trailing slash from pathname to avoid double slashes
35278
- let pathname = url.pathname;
35279
- if (pathname.endsWith('/')) {
35280
- pathname = pathname.slice(0, -1);
35281
- }
35282
- // 1. OAuth metadata at the given URL
35283
- // Insert well-known before the path: https://example.com/.well-known/oauth-authorization-server/tenant1
35284
- urlsToTry.push({
35285
- url: new URL(`/.well-known/oauth-authorization-server${pathname}`, url.origin),
35286
- type: 'oauth'
35287
- });
35288
- // 2. OIDC metadata endpoints
35289
- // RFC 8414 style: Insert /.well-known/openid-configuration before the path
35290
- urlsToTry.push({
35291
- url: new URL(`/.well-known/openid-configuration${pathname}`, url.origin),
35292
- type: 'oidc'
35293
- });
35294
- // OIDC Discovery 1.0 style: Append /.well-known/openid-configuration after the path
35295
- urlsToTry.push({
35296
- url: new URL(`${pathname}/.well-known/openid-configuration`, url.origin),
35297
- type: 'oidc'
35298
- });
35299
- return urlsToTry;
35300
- }
35301
- /**
35302
- * Discovers authorization server metadata with support for RFC 8414 OAuth 2.0 Authorization Server Metadata
35303
- * and OpenID Connect Discovery 1.0 specifications.
35304
- *
35305
- * This function implements a fallback strategy for authorization server discovery:
35306
- * 1. Attempts RFC 8414 OAuth metadata discovery first
35307
- * 2. If OAuth discovery fails, falls back to OpenID Connect Discovery
35308
- *
35309
- * @param authorizationServerUrl - The authorization server URL obtained from the MCP Server's
35310
- * protected resource metadata, or the MCP server's URL if the
35311
- * metadata was not found.
35312
- * @param options - Configuration options
35313
- * @param options.fetchFn - Optional fetch function for making HTTP requests, defaults to global fetch
35314
- * @param options.protocolVersion - MCP protocol version to use, defaults to LATEST_PROTOCOL_VERSION
35315
- * @returns Promise resolving to authorization server metadata, or undefined if discovery fails
35316
- */
35317
- async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = types_js_1$5.LATEST_PROTOCOL_VERSION } = {}) {
35318
- const headers = {
35319
- 'MCP-Protocol-Version': protocolVersion,
35320
- Accept: 'application/json'
35321
- };
35322
- // Get the list of URLs to try
35323
- const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
35324
- // Try each URL in order
35325
- for (const { url: endpointUrl, type } of urlsToTry) {
35326
- const response = await fetchWithCorsRetry(endpointUrl, headers, fetchFn);
35327
- if (!response) {
35328
- /**
35329
- * CORS error occurred - don't throw as the endpoint may not allow CORS,
35330
- * continue trying other possible endpoints
35331
- */
35332
- continue;
35333
- }
35334
- if (!response.ok) {
35335
- await response.body?.cancel();
35336
- // Continue looking for any 4xx response code.
35337
- if (response.status >= 400 && response.status < 500) {
35338
- continue; // Try next URL
35339
- }
35340
- throw new Error(`HTTP ${response.status} trying to load ${type === 'oauth' ? 'OAuth' : 'OpenID provider'} metadata from ${endpointUrl}`);
35341
- }
35342
- // Parse and validate based on type
35343
- if (type === 'oauth') {
35344
- return auth_js_2.OAuthMetadataSchema.parse(await response.json());
35345
- }
35346
- else {
35347
- return auth_js_1$1.OpenIdProviderDiscoveryMetadataSchema.parse(await response.json());
35348
- }
35349
- }
35350
- return undefined;
35351
- }
35352
- /**
35353
- * Discovers the authorization server for an MCP server following
35354
- * {@link https://datatracker.ietf.org/doc/html/rfc9728 | RFC 9728} (OAuth 2.0 Protected
35355
- * Resource Metadata), with fallback to treating the server URL as the
35356
- * authorization server.
35357
- *
35358
- * This function combines two discovery steps into one call:
35359
- * 1. Probes `/.well-known/oauth-protected-resource` on the MCP server to find the
35360
- * authorization server URL (RFC 9728).
35361
- * 2. Fetches authorization server metadata from that URL (RFC 8414 / OpenID Connect Discovery).
35362
- *
35363
- * Use this when you need the authorization server metadata for operations outside the
35364
- * {@linkcode auth} orchestrator, such as token refresh or token revocation.
35365
- *
35366
- * @param serverUrl - The MCP resource server URL
35367
- * @param opts - Optional configuration
35368
- * @param opts.resourceMetadataUrl - Override URL for the protected resource metadata endpoint
35369
- * @param opts.fetchFn - Custom fetch function for HTTP requests
35370
- * @returns Authorization server URL, metadata, and resource metadata (if available)
35371
- */
35372
- async function discoverOAuthServerInfo(serverUrl, opts) {
35373
- let resourceMetadata;
35374
- let authorizationServerUrl;
35375
- try {
35376
- resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: opts?.resourceMetadataUrl }, opts?.fetchFn);
35377
- if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
35378
- authorizationServerUrl = resourceMetadata.authorization_servers[0];
35379
- }
35380
- }
35381
- catch {
35382
- // RFC 9728 not supported -- fall back to treating the server URL as the authorization server
35383
- }
35384
- // If we don't get a valid authorization server from protected resource metadata,
35385
- // fall back to the legacy MCP spec behavior: MCP server base URL acts as the authorization server
35386
- if (!authorizationServerUrl) {
35387
- authorizationServerUrl = String(new URL('/', serverUrl));
35388
- }
35389
- const authorizationServerMetadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn: opts?.fetchFn });
35390
- return {
35391
- authorizationServerUrl,
35392
- authorizationServerMetadata,
35393
- resourceMetadata
35394
- };
35395
- }
35396
- /**
35397
- * Begins the authorization flow with the given server, by generating a PKCE challenge and constructing the authorization URL.
35398
- */
35399
- async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
35400
- let authorizationUrl;
35401
- if (metadata) {
35402
- authorizationUrl = new URL(metadata.authorization_endpoint);
35403
- if (!metadata.response_types_supported.includes(AUTHORIZATION_CODE_RESPONSE_TYPE)) {
35404
- throw new Error(`Incompatible auth server: does not support response type ${AUTHORIZATION_CODE_RESPONSE_TYPE}`);
35405
- }
35406
- if (metadata.code_challenge_methods_supported &&
35407
- !metadata.code_challenge_methods_supported.includes(AUTHORIZATION_CODE_CHALLENGE_METHOD)) {
35408
- throw new Error(`Incompatible auth server: does not support code challenge method ${AUTHORIZATION_CODE_CHALLENGE_METHOD}`);
35409
- }
35410
- }
35411
- else {
35412
- authorizationUrl = new URL('/authorize', authorizationServerUrl);
35413
- }
35414
- // Generate PKCE challenge
35415
- const challenge = await (0, pkce_challenge_1.default)();
35416
- const codeVerifier = challenge.code_verifier;
35417
- const codeChallenge = challenge.code_challenge;
35418
- authorizationUrl.searchParams.set('response_type', AUTHORIZATION_CODE_RESPONSE_TYPE);
35419
- authorizationUrl.searchParams.set('client_id', clientInformation.client_id);
35420
- authorizationUrl.searchParams.set('code_challenge', codeChallenge);
35421
- authorizationUrl.searchParams.set('code_challenge_method', AUTHORIZATION_CODE_CHALLENGE_METHOD);
35422
- authorizationUrl.searchParams.set('redirect_uri', String(redirectUrl));
35423
- if (state) {
35424
- authorizationUrl.searchParams.set('state', state);
35425
- }
35426
- if (scope) {
35427
- authorizationUrl.searchParams.set('scope', scope);
35428
- }
35429
- if (scope?.includes('offline_access')) {
35430
- // if the request includes the OIDC-only "offline_access" scope,
35431
- // we need to set the prompt to "consent" to ensure the user is prompted to grant offline access
35432
- // https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
35433
- authorizationUrl.searchParams.append('prompt', 'consent');
35434
- }
35435
- if (resource) {
35436
- authorizationUrl.searchParams.set('resource', resource.href);
35437
- }
35438
- return { authorizationUrl, codeVerifier };
35439
- }
35440
- /**
35441
- * Prepares token request parameters for an authorization code exchange.
35442
- *
35443
- * This is the default implementation used by fetchToken when the provider
35444
- * doesn't implement prepareTokenRequest.
35445
- *
35446
- * @param authorizationCode - The authorization code received from the authorization endpoint
35447
- * @param codeVerifier - The PKCE code verifier
35448
- * @param redirectUri - The redirect URI used in the authorization request
35449
- * @returns URLSearchParams for the authorization_code grant
35450
- */
35451
- function prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri) {
35452
- return new URLSearchParams({
35453
- grant_type: 'authorization_code',
35454
- code: authorizationCode,
35455
- code_verifier: codeVerifier,
35456
- redirect_uri: String(redirectUri)
35457
- });
35458
- }
35459
- /**
35460
- * Internal helper to execute a token request with the given parameters.
35461
- * Used by exchangeAuthorization, refreshAuthorization, and fetchToken.
35462
- */
35463
- async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, fetchFn }) {
35464
- const tokenUrl = metadata?.token_endpoint ? new URL(metadata.token_endpoint) : new URL('/token', authorizationServerUrl);
35465
- const headers = new Headers({
35466
- 'Content-Type': 'application/x-www-form-urlencoded',
35467
- Accept: 'application/json'
35468
- });
35469
- if (resource) {
35470
- tokenRequestParams.set('resource', resource.href);
35471
- }
35472
- if (addClientAuthentication) {
35473
- await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
35474
- }
35475
- else if (clientInformation) {
35476
- const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? [];
35477
- const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
35478
- applyClientAuthentication(authMethod, clientInformation, headers, tokenRequestParams);
35479
- }
35480
- const response = await (fetchFn ?? fetch)(tokenUrl, {
35481
- method: 'POST',
35482
- headers,
35483
- body: tokenRequestParams
35484
- });
35485
- if (!response.ok) {
35486
- throw await parseErrorResponse(response);
35487
- }
35488
- return auth_js_2.OAuthTokensSchema.parse(await response.json());
35489
- }
35490
- /**
35491
- * Exchanges an authorization code for an access token with the given server.
35492
- *
35493
- * Supports multiple client authentication methods as specified in OAuth 2.1:
35494
- * - Automatically selects the best authentication method based on server support
35495
- * - Falls back to appropriate defaults when server metadata is unavailable
35496
- *
35497
- * @param authorizationServerUrl - The authorization server's base URL
35498
- * @param options - Configuration object containing client info, auth code, etc.
35499
- * @returns Promise resolving to OAuth tokens
35500
- * @throws {Error} When token exchange fails or authentication is invalid
35501
- */
35502
- async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn }) {
35503
- const tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri);
35504
- return executeTokenRequest(authorizationServerUrl, {
35505
- metadata,
35506
- tokenRequestParams,
35507
- clientInformation,
35508
- addClientAuthentication,
35509
- resource,
35510
- fetchFn
35511
- });
35512
- }
35513
- /**
35514
- * Exchange a refresh token for an updated access token.
35515
- *
35516
- * Supports multiple client authentication methods as specified in OAuth 2.1:
35517
- * - Automatically selects the best authentication method based on server support
35518
- * - Preserves the original refresh token if a new one is not returned
35519
- *
35520
- * @param authorizationServerUrl - The authorization server's base URL
35521
- * @param options - Configuration object containing client info, refresh token, etc.
35522
- * @returns Promise resolving to OAuth tokens (preserves original refresh_token if not replaced)
35523
- * @throws {Error} When token refresh fails or authentication is invalid
35524
- */
35525
- async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
35526
- const tokenRequestParams = new URLSearchParams({
35527
- grant_type: 'refresh_token',
35528
- refresh_token: refreshToken
35529
- });
35530
- const tokens = await executeTokenRequest(authorizationServerUrl, {
35531
- metadata,
35532
- tokenRequestParams,
35533
- clientInformation,
35534
- addClientAuthentication,
35535
- resource,
35536
- fetchFn
35537
- });
35538
- // Preserve original refresh token if server didn't return a new one
35539
- return { refresh_token: refreshToken, ...tokens };
35540
- }
35541
- /**
35542
- * Unified token fetching that works with any grant type via provider.prepareTokenRequest().
35543
- *
35544
- * This function provides a single entry point for obtaining tokens regardless of the
35545
- * OAuth grant type. The provider's prepareTokenRequest() method determines which grant
35546
- * to use and supplies the grant-specific parameters.
35547
- *
35548
- * @param provider - OAuth client provider that implements prepareTokenRequest()
35549
- * @param authorizationServerUrl - The authorization server's base URL
35550
- * @param options - Configuration for the token request
35551
- * @returns Promise resolving to OAuth tokens
35552
- * @throws {Error} When provider doesn't implement prepareTokenRequest or token fetch fails
35553
- *
35554
- * @example
35555
- * // Provider for client_credentials:
35556
- * class MyProvider implements OAuthClientProvider {
35557
- * prepareTokenRequest(scope) {
35558
- * const params = new URLSearchParams({ grant_type: 'client_credentials' });
35559
- * if (scope) params.set('scope', scope);
35560
- * return params;
35561
- * }
35562
- * // ... other methods
35563
- * }
35564
- *
35565
- * const tokens = await fetchToken(provider, authServerUrl, { metadata });
35566
- */
35567
- async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
35568
- const scope = provider.clientMetadata.scope;
35569
- // Use provider's prepareTokenRequest if available, otherwise fall back to authorization_code
35570
- let tokenRequestParams;
35571
- if (provider.prepareTokenRequest) {
35572
- tokenRequestParams = await provider.prepareTokenRequest(scope);
35573
- }
35574
- // Default to authorization_code grant if no custom prepareTokenRequest
35575
- if (!tokenRequestParams) {
35576
- if (!authorizationCode) {
35577
- throw new Error('Either provider.prepareTokenRequest() or authorizationCode is required');
35578
- }
35579
- if (!provider.redirectUrl) {
35580
- throw new Error('redirectUrl is required for authorization_code flow');
35581
- }
35582
- const codeVerifier = await provider.codeVerifier();
35583
- tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
35584
- }
35585
- const clientInformation = await provider.clientInformation();
35586
- return executeTokenRequest(authorizationServerUrl, {
35587
- metadata,
35588
- tokenRequestParams,
35589
- clientInformation: clientInformation ?? undefined,
35590
- addClientAuthentication: provider.addClientAuthentication,
35591
- resource,
35592
- fetchFn
35593
- });
35594
- }
35595
- /**
35596
- * Performs OAuth 2.0 Dynamic Client Registration according to RFC 7591.
35597
- *
35598
- * If `scope` is provided, it overrides `clientMetadata.scope` in the registration
35599
- * request body. This allows callers to apply the Scope Selection Strategy (SEP-835)
35600
- * consistently across both DCR and the subsequent authorization request.
35601
- */
35602
- async function registerClient(authorizationServerUrl, { metadata, clientMetadata, scope, fetchFn }) {
35603
- let registrationUrl;
35604
- if (metadata) {
35605
- if (!metadata.registration_endpoint) {
35606
- throw new Error('Incompatible auth server: does not support dynamic client registration');
35607
- }
35608
- registrationUrl = new URL(metadata.registration_endpoint);
35609
- }
35610
- else {
35611
- registrationUrl = new URL('/register', authorizationServerUrl);
35612
- }
35613
- const response = await (fetchFn ?? fetch)(registrationUrl, {
35614
- method: 'POST',
35615
- headers: {
35616
- 'Content-Type': 'application/json'
35617
- },
35618
- body: JSON.stringify({
35619
- ...clientMetadata,
35620
- ...(scope !== undefined ? { scope } : {})
35621
- })
35622
- });
35623
- if (!response.ok) {
35624
- throw await parseErrorResponse(response);
35625
- }
35626
- return auth_js_2.OAuthClientInformationFullSchema.parse(await response.json());
34809
+ function requireAuth () {
34810
+ if (hasRequiredAuth) return auth$1;
34811
+ hasRequiredAuth = 1;
34812
+ var __importDefault = (commonjsGlobal && commonjsGlobal.__importDefault) || function (mod) {
34813
+ return (mod && mod.__esModule) ? mod : { "default": mod };
34814
+ };
34815
+ Object.defineProperty(auth$1, "__esModule", { value: true });
34816
+ auth$1.UnauthorizedError = void 0;
34817
+ auth$1.selectClientAuthMethod = selectClientAuthMethod;
34818
+ auth$1.parseErrorResponse = parseErrorResponse;
34819
+ auth$1.auth = auth;
34820
+ auth$1.isHttpsUrl = isHttpsUrl;
34821
+ auth$1.selectResourceURL = selectResourceURL;
34822
+ auth$1.extractWWWAuthenticateParams = extractWWWAuthenticateParams;
34823
+ auth$1.extractResourceMetadataUrl = extractResourceMetadataUrl;
34824
+ auth$1.discoverOAuthProtectedResourceMetadata = discoverOAuthProtectedResourceMetadata;
34825
+ auth$1.discoverOAuthMetadata = discoverOAuthMetadata;
34826
+ auth$1.buildDiscoveryUrls = buildDiscoveryUrls;
34827
+ auth$1.discoverAuthorizationServerMetadata = discoverAuthorizationServerMetadata;
34828
+ auth$1.discoverOAuthServerInfo = discoverOAuthServerInfo;
34829
+ auth$1.startAuthorization = startAuthorization;
34830
+ auth$1.prepareAuthorizationCodeRequest = prepareAuthorizationCodeRequest;
34831
+ auth$1.exchangeAuthorization = exchangeAuthorization;
34832
+ auth$1.refreshAuthorization = refreshAuthorization;
34833
+ auth$1.fetchToken = fetchToken;
34834
+ auth$1.registerClient = registerClient;
34835
+ const pkce_challenge_1 = __importDefault(require$$0$7);
34836
+ const types_js_1 = types$2;
34837
+ const auth_js_1 = requireAuth$1();
34838
+ const auth_js_2 = requireAuth$1();
34839
+ const auth_utils_js_1 = requireAuthUtils();
34840
+ const errors_js_1 = requireErrors$1();
34841
+ class UnauthorizedError extends Error {
34842
+ constructor(message) {
34843
+ super(message ?? 'Unauthorized');
34844
+ }
34845
+ }
34846
+ auth$1.UnauthorizedError = UnauthorizedError;
34847
+ function isClientAuthMethod(method) {
34848
+ return ['client_secret_basic', 'client_secret_post', 'none'].includes(method);
34849
+ }
34850
+ const AUTHORIZATION_CODE_RESPONSE_TYPE = 'code';
34851
+ const AUTHORIZATION_CODE_CHALLENGE_METHOD = 'S256';
34852
+ /**
34853
+ * Determines the best client authentication method to use based on server support and client configuration.
34854
+ *
34855
+ * Priority order (highest to lowest):
34856
+ * 1. client_secret_basic (if client secret is available)
34857
+ * 2. client_secret_post (if client secret is available)
34858
+ * 3. none (for public clients)
34859
+ *
34860
+ * @param clientInformation - OAuth client information containing credentials
34861
+ * @param supportedMethods - Authentication methods supported by the authorization server
34862
+ * @returns The selected authentication method
34863
+ */
34864
+ function selectClientAuthMethod(clientInformation, supportedMethods) {
34865
+ const hasClientSecret = clientInformation.client_secret !== undefined;
34866
+ // Prefer the method returned by the server during client registration, if valid.
34867
+ // When server metadata is present we also require the method to be listed as supported;
34868
+ // when supportedMethods is empty (metadata omitted the field) the DCR hint stands alone.
34869
+ if ('token_endpoint_auth_method' in clientInformation &&
34870
+ clientInformation.token_endpoint_auth_method &&
34871
+ isClientAuthMethod(clientInformation.token_endpoint_auth_method) &&
34872
+ (supportedMethods.length === 0 || supportedMethods.includes(clientInformation.token_endpoint_auth_method))) {
34873
+ return clientInformation.token_endpoint_auth_method;
34874
+ }
34875
+ // If server metadata omits token_endpoint_auth_methods_supported, RFC 8414 §2 says the
34876
+ // default is client_secret_basic. RFC 6749 §2.3.1 also requires servers to support HTTP
34877
+ // Basic authentication for clients with a secret, making it the safest default.
34878
+ if (supportedMethods.length === 0) {
34879
+ return hasClientSecret ? 'client_secret_basic' : 'none';
34880
+ }
34881
+ // Try methods in priority order (most secure first)
34882
+ if (hasClientSecret && supportedMethods.includes('client_secret_basic')) {
34883
+ return 'client_secret_basic';
34884
+ }
34885
+ if (hasClientSecret && supportedMethods.includes('client_secret_post')) {
34886
+ return 'client_secret_post';
34887
+ }
34888
+ if (supportedMethods.includes('none')) {
34889
+ return 'none';
34890
+ }
34891
+ // Fallback: use what we have
34892
+ return hasClientSecret ? 'client_secret_post' : 'none';
34893
+ }
34894
+ /**
34895
+ * Applies client authentication to the request based on the specified method.
34896
+ *
34897
+ * Implements OAuth 2.1 client authentication methods:
34898
+ * - client_secret_basic: HTTP Basic authentication (RFC 6749 Section 2.3.1)
34899
+ * - client_secret_post: Credentials in request body (RFC 6749 Section 2.3.1)
34900
+ * - none: Public client authentication (RFC 6749 Section 2.1)
34901
+ *
34902
+ * @param method - The authentication method to use
34903
+ * @param clientInformation - OAuth client information containing credentials
34904
+ * @param headers - HTTP headers object to modify
34905
+ * @param params - URL search parameters to modify
34906
+ * @throws {Error} When required credentials are missing
34907
+ */
34908
+ function applyClientAuthentication(method, clientInformation, headers, params) {
34909
+ const { client_id, client_secret } = clientInformation;
34910
+ switch (method) {
34911
+ case 'client_secret_basic':
34912
+ applyBasicAuth(client_id, client_secret, headers);
34913
+ return;
34914
+ case 'client_secret_post':
34915
+ applyPostAuth(client_id, client_secret, params);
34916
+ return;
34917
+ case 'none':
34918
+ applyPublicAuth(client_id, params);
34919
+ return;
34920
+ default:
34921
+ throw new Error(`Unsupported client authentication method: ${method}`);
34922
+ }
34923
+ }
34924
+ /**
34925
+ * Applies HTTP Basic authentication (RFC 6749 Section 2.3.1)
34926
+ */
34927
+ function applyBasicAuth(clientId, clientSecret, headers) {
34928
+ if (!clientSecret) {
34929
+ throw new Error('client_secret_basic authentication requires a client_secret');
34930
+ }
34931
+ const credentials = btoa(`${clientId}:${clientSecret}`);
34932
+ headers.set('Authorization', `Basic ${credentials}`);
34933
+ }
34934
+ /**
34935
+ * Applies POST body authentication (RFC 6749 Section 2.3.1)
34936
+ */
34937
+ function applyPostAuth(clientId, clientSecret, params) {
34938
+ params.set('client_id', clientId);
34939
+ if (clientSecret) {
34940
+ params.set('client_secret', clientSecret);
34941
+ }
34942
+ }
34943
+ /**
34944
+ * Applies public client authentication (RFC 6749 Section 2.1)
34945
+ */
34946
+ function applyPublicAuth(clientId, params) {
34947
+ params.set('client_id', clientId);
34948
+ }
34949
+ /**
34950
+ * Parses an OAuth error response from a string or Response object.
34951
+ *
34952
+ * If the input is a standard OAuth2.0 error response, it will be parsed according to the spec
34953
+ * and an instance of the appropriate OAuthError subclass will be returned.
34954
+ * If parsing fails, it falls back to a generic ServerError that includes
34955
+ * the response status (if available) and original content.
34956
+ *
34957
+ * @param input - A Response object or string containing the error response
34958
+ * @returns A Promise that resolves to an OAuthError instance
34959
+ */
34960
+ async function parseErrorResponse(input) {
34961
+ const statusCode = input instanceof Response ? input.status : undefined;
34962
+ const body = input instanceof Response ? await input.text() : input;
34963
+ try {
34964
+ const result = auth_js_1.OAuthErrorResponseSchema.parse(JSON.parse(body));
34965
+ const { error, error_description, error_uri } = result;
34966
+ const errorClass = errors_js_1.OAUTH_ERRORS[error] || errors_js_1.ServerError;
34967
+ return new errorClass(error_description || '', error_uri);
34968
+ }
34969
+ catch (error) {
34970
+ // Not a valid OAuth error response, but try to inform the user of the raw data anyway
34971
+ const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ''}Invalid OAuth error response: ${error}. Raw body: ${body}`;
34972
+ return new errors_js_1.ServerError(errorMessage);
34973
+ }
34974
+ }
34975
+ /**
34976
+ * Orchestrates the full auth flow with a server.
34977
+ *
34978
+ * This can be used as a single entry point for all authorization functionality,
34979
+ * instead of linking together the other lower-level functions in this module.
34980
+ */
34981
+ async function auth(provider, options) {
34982
+ try {
34983
+ return await authInternal(provider, options);
34984
+ }
34985
+ catch (error) {
34986
+ // Handle recoverable error types by invalidating credentials and retrying
34987
+ if (error instanceof errors_js_1.InvalidClientError || error instanceof errors_js_1.UnauthorizedClientError) {
34988
+ await provider.invalidateCredentials?.('all');
34989
+ return await authInternal(provider, options);
34990
+ }
34991
+ else if (error instanceof errors_js_1.InvalidGrantError) {
34992
+ await provider.invalidateCredentials?.('tokens');
34993
+ return await authInternal(provider, options);
34994
+ }
34995
+ // Throw otherwise
34996
+ throw error;
34997
+ }
34998
+ }
34999
+ async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
35000
+ // Check if the provider has cached discovery state to skip discovery
35001
+ const cachedState = await provider.discoveryState?.();
35002
+ let resourceMetadata;
35003
+ let authorizationServerUrl;
35004
+ let metadata;
35005
+ // If resourceMetadataUrl is not provided, try to load it from cached state
35006
+ // This handles browser redirects where the URL was saved before navigation
35007
+ let effectiveResourceMetadataUrl = resourceMetadataUrl;
35008
+ if (!effectiveResourceMetadataUrl && cachedState?.resourceMetadataUrl) {
35009
+ effectiveResourceMetadataUrl = new URL(cachedState.resourceMetadataUrl);
35010
+ }
35011
+ if (cachedState?.authorizationServerUrl) {
35012
+ // Restore discovery state from cache
35013
+ authorizationServerUrl = cachedState.authorizationServerUrl;
35014
+ resourceMetadata = cachedState.resourceMetadata;
35015
+ metadata =
35016
+ cachedState.authorizationServerMetadata ?? (await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn }));
35017
+ // If resource metadata wasn't cached, try to fetch it for selectResourceURL
35018
+ if (!resourceMetadata) {
35019
+ try {
35020
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl }, fetchFn);
35021
+ }
35022
+ catch {
35023
+ // RFC 9728 not available — selectResourceURL will handle undefined
35024
+ }
35025
+ }
35026
+ // Re-save if we enriched the cached state with missing metadata
35027
+ if (metadata !== cachedState.authorizationServerMetadata || resourceMetadata !== cachedState.resourceMetadata) {
35028
+ await provider.saveDiscoveryState?.({
35029
+ authorizationServerUrl: String(authorizationServerUrl),
35030
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
35031
+ resourceMetadata,
35032
+ authorizationServerMetadata: metadata
35033
+ });
35034
+ }
35035
+ }
35036
+ else {
35037
+ // Full discovery via RFC 9728
35038
+ const serverInfo = await discoverOAuthServerInfo(serverUrl, { resourceMetadataUrl: effectiveResourceMetadataUrl, fetchFn });
35039
+ authorizationServerUrl = serverInfo.authorizationServerUrl;
35040
+ metadata = serverInfo.authorizationServerMetadata;
35041
+ resourceMetadata = serverInfo.resourceMetadata;
35042
+ // Persist discovery state for future use
35043
+ // TODO: resourceMetadataUrl is only populated when explicitly provided via options
35044
+ // or loaded from cached state. The URL derived internally by
35045
+ // discoverOAuthProtectedResourceMetadata() is not captured back here.
35046
+ await provider.saveDiscoveryState?.({
35047
+ authorizationServerUrl: String(authorizationServerUrl),
35048
+ resourceMetadataUrl: effectiveResourceMetadataUrl?.toString(),
35049
+ resourceMetadata,
35050
+ authorizationServerMetadata: metadata
35051
+ });
35052
+ }
35053
+ const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
35054
+ // Apply scope selection strategy (SEP-835):
35055
+ // 1. WWW-Authenticate scope (passed via `scope` param)
35056
+ // 2. PRM scopes_supported
35057
+ // 3. Client metadata scope (user-configured fallback)
35058
+ // The resolved scope is used consistently for both DCR and the authorization request.
35059
+ const resolvedScope = scope || resourceMetadata?.scopes_supported?.join(' ') || provider.clientMetadata.scope;
35060
+ // Handle client registration if needed
35061
+ let clientInformation = await Promise.resolve(provider.clientInformation());
35062
+ if (!clientInformation) {
35063
+ if (authorizationCode !== undefined) {
35064
+ throw new Error('Existing OAuth client information is required when exchanging an authorization code');
35065
+ }
35066
+ const supportsUrlBasedClientId = metadata?.client_id_metadata_document_supported === true;
35067
+ const clientMetadataUrl = provider.clientMetadataUrl;
35068
+ if (clientMetadataUrl && !isHttpsUrl(clientMetadataUrl)) {
35069
+ throw new errors_js_1.InvalidClientMetadataError(`clientMetadataUrl must be a valid HTTPS URL with a non-root pathname, got: ${clientMetadataUrl}`);
35070
+ }
35071
+ const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
35072
+ if (shouldUseUrlBasedClientId) {
35073
+ // SEP-991: URL-based Client IDs
35074
+ clientInformation = {
35075
+ client_id: clientMetadataUrl
35076
+ };
35077
+ await provider.saveClientInformation?.(clientInformation);
35078
+ }
35079
+ else {
35080
+ // Fallback to dynamic registration
35081
+ if (!provider.saveClientInformation) {
35082
+ throw new Error('OAuth client information must be saveable for dynamic registration');
35083
+ }
35084
+ const fullInformation = await registerClient(authorizationServerUrl, {
35085
+ metadata,
35086
+ clientMetadata: provider.clientMetadata,
35087
+ scope: resolvedScope,
35088
+ fetchFn
35089
+ });
35090
+ await provider.saveClientInformation(fullInformation);
35091
+ clientInformation = fullInformation;
35092
+ }
35093
+ }
35094
+ // Non-interactive flows (e.g., client_credentials, jwt-bearer) don't need a redirect URL
35095
+ const nonInteractiveFlow = !provider.redirectUrl;
35096
+ // Exchange authorization code for tokens, or fetch tokens directly for non-interactive flows
35097
+ if (authorizationCode !== undefined || nonInteractiveFlow) {
35098
+ const tokens = await fetchToken(provider, authorizationServerUrl, {
35099
+ metadata,
35100
+ resource,
35101
+ authorizationCode,
35102
+ fetchFn
35103
+ });
35104
+ await provider.saveTokens(tokens);
35105
+ return 'AUTHORIZED';
35106
+ }
35107
+ const tokens = await provider.tokens();
35108
+ // Handle token refresh or new authorization
35109
+ if (tokens?.refresh_token) {
35110
+ try {
35111
+ // Attempt to refresh the token
35112
+ const newTokens = await refreshAuthorization(authorizationServerUrl, {
35113
+ metadata,
35114
+ clientInformation,
35115
+ refreshToken: tokens.refresh_token,
35116
+ resource,
35117
+ addClientAuthentication: provider.addClientAuthentication,
35118
+ fetchFn
35119
+ });
35120
+ await provider.saveTokens(newTokens);
35121
+ return 'AUTHORIZED';
35122
+ }
35123
+ catch (error) {
35124
+ // If this is a ServerError, or an unknown type, log it out and try to continue. Otherwise, escalate so we can fix things and retry.
35125
+ if (!(error instanceof errors_js_1.OAuthError) || error instanceof errors_js_1.ServerError) ;
35126
+ else {
35127
+ // Refresh failed for another reason, re-throw
35128
+ throw error;
35129
+ }
35130
+ }
35131
+ }
35132
+ const state = provider.state ? await provider.state() : undefined;
35133
+ // Start new authorization flow
35134
+ const { authorizationUrl, codeVerifier } = await startAuthorization(authorizationServerUrl, {
35135
+ metadata,
35136
+ clientInformation,
35137
+ state,
35138
+ redirectUrl: provider.redirectUrl,
35139
+ scope: resolvedScope,
35140
+ resource
35141
+ });
35142
+ await provider.saveCodeVerifier(codeVerifier);
35143
+ await provider.redirectToAuthorization(authorizationUrl);
35144
+ return 'REDIRECT';
35145
+ }
35146
+ /**
35147
+ * SEP-991: URL-based Client IDs
35148
+ * Validate that the client_id is a valid URL with https scheme
35149
+ */
35150
+ function isHttpsUrl(value) {
35151
+ if (!value)
35152
+ return false;
35153
+ try {
35154
+ const url = new URL(value);
35155
+ return url.protocol === 'https:' && url.pathname !== '/';
35156
+ }
35157
+ catch {
35158
+ return false;
35159
+ }
35160
+ }
35161
+ async function selectResourceURL(serverUrl, provider, resourceMetadata) {
35162
+ const defaultResource = (0, auth_utils_js_1.resourceUrlFromServerUrl)(serverUrl);
35163
+ // If provider has custom validation, delegate to it
35164
+ if (provider.validateResourceURL) {
35165
+ return await provider.validateResourceURL(defaultResource, resourceMetadata?.resource);
35166
+ }
35167
+ // Only include resource parameter when Protected Resource Metadata is present
35168
+ if (!resourceMetadata) {
35169
+ return undefined;
35170
+ }
35171
+ // Validate that the metadata's resource is compatible with our request
35172
+ if (!(0, auth_utils_js_1.checkResourceAllowed)({ requestedResource: defaultResource, configuredResource: resourceMetadata.resource })) {
35173
+ throw new Error(`Protected resource ${resourceMetadata.resource} does not match expected ${defaultResource} (or origin)`);
35174
+ }
35175
+ // Prefer the resource from metadata since it's what the server is telling us to request
35176
+ return new URL(resourceMetadata.resource);
35177
+ }
35178
+ /**
35179
+ * Extract resource_metadata, scope, and error from WWW-Authenticate header.
35180
+ */
35181
+ function extractWWWAuthenticateParams(res) {
35182
+ const authenticateHeader = res.headers.get('WWW-Authenticate');
35183
+ if (!authenticateHeader) {
35184
+ return {};
35185
+ }
35186
+ const [type, scheme] = authenticateHeader.split(' ');
35187
+ if (type.toLowerCase() !== 'bearer' || !scheme) {
35188
+ return {};
35189
+ }
35190
+ const resourceMetadataMatch = extractFieldFromWwwAuth(res, 'resource_metadata') || undefined;
35191
+ let resourceMetadataUrl;
35192
+ if (resourceMetadataMatch) {
35193
+ try {
35194
+ resourceMetadataUrl = new URL(resourceMetadataMatch);
35195
+ }
35196
+ catch {
35197
+ // Ignore invalid URL
35198
+ }
35199
+ }
35200
+ const scope = extractFieldFromWwwAuth(res, 'scope') || undefined;
35201
+ const error = extractFieldFromWwwAuth(res, 'error') || undefined;
35202
+ return {
35203
+ resourceMetadataUrl,
35204
+ scope,
35205
+ error
35206
+ };
35207
+ }
35208
+ /**
35209
+ * Extracts a specific field's value from the WWW-Authenticate header string.
35210
+ *
35211
+ * @param response The HTTP response object containing the headers.
35212
+ * @param fieldName The name of the field to extract (e.g., "realm", "nonce").
35213
+ * @returns The field value
35214
+ */
35215
+ function extractFieldFromWwwAuth(response, fieldName) {
35216
+ const wwwAuthHeader = response.headers.get('WWW-Authenticate');
35217
+ if (!wwwAuthHeader) {
35218
+ return null;
35219
+ }
35220
+ const pattern = new RegExp(`${fieldName}=(?:"([^"]+)"|([^\\s,]+))`);
35221
+ const match = wwwAuthHeader.match(pattern);
35222
+ if (match) {
35223
+ // Pattern matches: field_name="value" or field_name=value (unquoted)
35224
+ return match[1] || match[2];
35225
+ }
35226
+ return null;
35227
+ }
35228
+ /**
35229
+ * Extract resource_metadata from response header.
35230
+ * @deprecated Use `extractWWWAuthenticateParams` instead.
35231
+ */
35232
+ function extractResourceMetadataUrl(res) {
35233
+ const authenticateHeader = res.headers.get('WWW-Authenticate');
35234
+ if (!authenticateHeader) {
35235
+ return undefined;
35236
+ }
35237
+ const [type, scheme] = authenticateHeader.split(' ');
35238
+ if (type.toLowerCase() !== 'bearer' || !scheme) {
35239
+ return undefined;
35240
+ }
35241
+ const regex = /resource_metadata="([^"]*)"/;
35242
+ const match = regex.exec(authenticateHeader);
35243
+ if (!match) {
35244
+ return undefined;
35245
+ }
35246
+ try {
35247
+ return new URL(match[1]);
35248
+ }
35249
+ catch {
35250
+ return undefined;
35251
+ }
35252
+ }
35253
+ /**
35254
+ * Looks up RFC 9728 OAuth 2.0 Protected Resource Metadata.
35255
+ *
35256
+ * If the server returns a 404 for the well-known endpoint, this function will
35257
+ * return `undefined`. Any other errors will be thrown as exceptions.
35258
+ */
35259
+ async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn = fetch) {
35260
+ const response = await discoverMetadataWithFallback(serverUrl, 'oauth-protected-resource', fetchFn, {
35261
+ protocolVersion: opts?.protocolVersion,
35262
+ metadataUrl: opts?.resourceMetadataUrl
35263
+ });
35264
+ if (!response || response.status === 404) {
35265
+ await response?.body?.cancel();
35266
+ throw new Error(`Resource server does not implement OAuth 2.0 Protected Resource Metadata.`);
35267
+ }
35268
+ if (!response.ok) {
35269
+ await response.body?.cancel();
35270
+ throw new Error(`HTTP ${response.status} trying to load well-known OAuth protected resource metadata.`);
35271
+ }
35272
+ return auth_js_2.OAuthProtectedResourceMetadataSchema.parse(await response.json());
35273
+ }
35274
+ /**
35275
+ * Helper function to handle fetch with CORS retry logic
35276
+ */
35277
+ async function fetchWithCorsRetry(url, headers, fetchFn = fetch) {
35278
+ try {
35279
+ return await fetchFn(url, { headers });
35280
+ }
35281
+ catch (error) {
35282
+ if (error instanceof TypeError) {
35283
+ if (headers) {
35284
+ // CORS errors come back as TypeError, retry without headers
35285
+ return fetchWithCorsRetry(url, undefined, fetchFn);
35286
+ }
35287
+ else {
35288
+ // We're getting CORS errors on retry too, return undefined
35289
+ return undefined;
35290
+ }
35291
+ }
35292
+ throw error;
35293
+ }
35294
+ }
35295
+ /**
35296
+ * Constructs the well-known path for auth-related metadata discovery
35297
+ */
35298
+ function buildWellKnownPath(wellKnownPrefix, pathname = '', options = {}) {
35299
+ // Strip trailing slash from pathname to avoid double slashes
35300
+ if (pathname.endsWith('/')) {
35301
+ pathname = pathname.slice(0, -1);
35302
+ }
35303
+ return options.prependPathname ? `${pathname}/.well-known/${wellKnownPrefix}` : `/.well-known/${wellKnownPrefix}${pathname}`;
35304
+ }
35305
+ /**
35306
+ * Tries to discover OAuth metadata at a specific URL
35307
+ */
35308
+ async function tryMetadataDiscovery(url, protocolVersion, fetchFn = fetch) {
35309
+ const headers = {
35310
+ 'MCP-Protocol-Version': protocolVersion
35311
+ };
35312
+ return await fetchWithCorsRetry(url, headers, fetchFn);
35313
+ }
35314
+ /**
35315
+ * Determines if fallback to root discovery should be attempted
35316
+ */
35317
+ function shouldAttemptFallback(response, pathname) {
35318
+ return !response || (response.status >= 400 && response.status < 500 && pathname !== '/');
35319
+ }
35320
+ /**
35321
+ * Generic function for discovering OAuth metadata with fallback support
35322
+ */
35323
+ async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
35324
+ const issuer = new URL(serverUrl);
35325
+ const protocolVersion = opts?.protocolVersion ?? types_js_1.LATEST_PROTOCOL_VERSION;
35326
+ let url;
35327
+ if (opts?.metadataUrl) {
35328
+ url = new URL(opts.metadataUrl);
35329
+ }
35330
+ else {
35331
+ // Try path-aware discovery first
35332
+ const wellKnownPath = buildWellKnownPath(wellKnownType, issuer.pathname);
35333
+ url = new URL(wellKnownPath, opts?.metadataServerUrl ?? issuer);
35334
+ url.search = issuer.search;
35335
+ }
35336
+ let response = await tryMetadataDiscovery(url, protocolVersion, fetchFn);
35337
+ // If path-aware discovery fails with 404 and we're not already at root, try fallback to root discovery
35338
+ if (!opts?.metadataUrl && shouldAttemptFallback(response, issuer.pathname)) {
35339
+ const rootUrl = new URL(`/.well-known/${wellKnownType}`, issuer);
35340
+ response = await tryMetadataDiscovery(rootUrl, protocolVersion, fetchFn);
35341
+ }
35342
+ return response;
35343
+ }
35344
+ /**
35345
+ * Looks up RFC 8414 OAuth 2.0 Authorization Server Metadata.
35346
+ *
35347
+ * If the server returns a 404 for the well-known endpoint, this function will
35348
+ * return `undefined`. Any other errors will be thrown as exceptions.
35349
+ *
35350
+ * @deprecated This function is deprecated in favor of `discoverAuthorizationServerMetadata`.
35351
+ */
35352
+ async function discoverOAuthMetadata(issuer, { authorizationServerUrl, protocolVersion } = {}, fetchFn = fetch) {
35353
+ if (typeof issuer === 'string') {
35354
+ issuer = new URL(issuer);
35355
+ }
35356
+ if (!authorizationServerUrl) {
35357
+ authorizationServerUrl = issuer;
35358
+ }
35359
+ if (typeof authorizationServerUrl === 'string') {
35360
+ authorizationServerUrl = new URL(authorizationServerUrl);
35361
+ }
35362
+ protocolVersion ?? (protocolVersion = types_js_1.LATEST_PROTOCOL_VERSION);
35363
+ const response = await discoverMetadataWithFallback(authorizationServerUrl, 'oauth-authorization-server', fetchFn, {
35364
+ protocolVersion,
35365
+ metadataServerUrl: authorizationServerUrl
35366
+ });
35367
+ if (!response || response.status === 404) {
35368
+ await response?.body?.cancel();
35369
+ return undefined;
35370
+ }
35371
+ if (!response.ok) {
35372
+ await response.body?.cancel();
35373
+ throw new Error(`HTTP ${response.status} trying to load well-known OAuth metadata`);
35374
+ }
35375
+ return auth_js_2.OAuthMetadataSchema.parse(await response.json());
35376
+ }
35377
+ /**
35378
+ * Builds a list of discovery URLs to try for authorization server metadata.
35379
+ * URLs are returned in priority order:
35380
+ * 1. OAuth metadata at the given URL
35381
+ * 2. OIDC metadata endpoints at the given URL
35382
+ */
35383
+ function buildDiscoveryUrls(authorizationServerUrl) {
35384
+ const url = typeof authorizationServerUrl === 'string' ? new URL(authorizationServerUrl) : authorizationServerUrl;
35385
+ const hasPath = url.pathname !== '/';
35386
+ const urlsToTry = [];
35387
+ if (!hasPath) {
35388
+ // Root path: https://example.com/.well-known/oauth-authorization-server
35389
+ urlsToTry.push({
35390
+ url: new URL('/.well-known/oauth-authorization-server', url.origin),
35391
+ type: 'oauth'
35392
+ });
35393
+ // OIDC: https://example.com/.well-known/openid-configuration
35394
+ urlsToTry.push({
35395
+ url: new URL(`/.well-known/openid-configuration`, url.origin),
35396
+ type: 'oidc'
35397
+ });
35398
+ return urlsToTry;
35399
+ }
35400
+ // Strip trailing slash from pathname to avoid double slashes
35401
+ let pathname = url.pathname;
35402
+ if (pathname.endsWith('/')) {
35403
+ pathname = pathname.slice(0, -1);
35404
+ }
35405
+ // 1. OAuth metadata at the given URL
35406
+ // Insert well-known before the path: https://example.com/.well-known/oauth-authorization-server/tenant1
35407
+ urlsToTry.push({
35408
+ url: new URL(`/.well-known/oauth-authorization-server${pathname}`, url.origin),
35409
+ type: 'oauth'
35410
+ });
35411
+ // 2. OIDC metadata endpoints
35412
+ // RFC 8414 style: Insert /.well-known/openid-configuration before the path
35413
+ urlsToTry.push({
35414
+ url: new URL(`/.well-known/openid-configuration${pathname}`, url.origin),
35415
+ type: 'oidc'
35416
+ });
35417
+ // OIDC Discovery 1.0 style: Append /.well-known/openid-configuration after the path
35418
+ urlsToTry.push({
35419
+ url: new URL(`${pathname}/.well-known/openid-configuration`, url.origin),
35420
+ type: 'oidc'
35421
+ });
35422
+ return urlsToTry;
35423
+ }
35424
+ /**
35425
+ * Discovers authorization server metadata with support for RFC 8414 OAuth 2.0 Authorization Server Metadata
35426
+ * and OpenID Connect Discovery 1.0 specifications.
35427
+ *
35428
+ * This function implements a fallback strategy for authorization server discovery:
35429
+ * 1. Attempts RFC 8414 OAuth metadata discovery first
35430
+ * 2. If OAuth discovery fails, falls back to OpenID Connect Discovery
35431
+ *
35432
+ * @param authorizationServerUrl - The authorization server URL obtained from the MCP Server's
35433
+ * protected resource metadata, or the MCP server's URL if the
35434
+ * metadata was not found.
35435
+ * @param options - Configuration options
35436
+ * @param options.fetchFn - Optional fetch function for making HTTP requests, defaults to global fetch
35437
+ * @param options.protocolVersion - MCP protocol version to use, defaults to LATEST_PROTOCOL_VERSION
35438
+ * @returns Promise resolving to authorization server metadata, or undefined if discovery fails
35439
+ */
35440
+ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = types_js_1.LATEST_PROTOCOL_VERSION } = {}) {
35441
+ const headers = {
35442
+ 'MCP-Protocol-Version': protocolVersion,
35443
+ Accept: 'application/json'
35444
+ };
35445
+ // Get the list of URLs to try
35446
+ const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
35447
+ // Try each URL in order
35448
+ for (const { url: endpointUrl, type } of urlsToTry) {
35449
+ const response = await fetchWithCorsRetry(endpointUrl, headers, fetchFn);
35450
+ if (!response) {
35451
+ /**
35452
+ * CORS error occurred - don't throw as the endpoint may not allow CORS,
35453
+ * continue trying other possible endpoints
35454
+ */
35455
+ continue;
35456
+ }
35457
+ if (!response.ok) {
35458
+ await response.body?.cancel();
35459
+ // Continue looking for any 4xx response code.
35460
+ if (response.status >= 400 && response.status < 500) {
35461
+ continue; // Try next URL
35462
+ }
35463
+ throw new Error(`HTTP ${response.status} trying to load ${type === 'oauth' ? 'OAuth' : 'OpenID provider'} metadata from ${endpointUrl}`);
35464
+ }
35465
+ // Parse and validate based on type
35466
+ if (type === 'oauth') {
35467
+ return auth_js_2.OAuthMetadataSchema.parse(await response.json());
35468
+ }
35469
+ else {
35470
+ return auth_js_1.OpenIdProviderDiscoveryMetadataSchema.parse(await response.json());
35471
+ }
35472
+ }
35473
+ return undefined;
35474
+ }
35475
+ /**
35476
+ * Discovers the authorization server for an MCP server following
35477
+ * {@link https://datatracker.ietf.org/doc/html/rfc9728 | RFC 9728} (OAuth 2.0 Protected
35478
+ * Resource Metadata), with fallback to treating the server URL as the
35479
+ * authorization server.
35480
+ *
35481
+ * This function combines two discovery steps into one call:
35482
+ * 1. Probes `/.well-known/oauth-protected-resource` on the MCP server to find the
35483
+ * authorization server URL (RFC 9728).
35484
+ * 2. Fetches authorization server metadata from that URL (RFC 8414 / OpenID Connect Discovery).
35485
+ *
35486
+ * Use this when you need the authorization server metadata for operations outside the
35487
+ * {@linkcode auth} orchestrator, such as token refresh or token revocation.
35488
+ *
35489
+ * @param serverUrl - The MCP resource server URL
35490
+ * @param opts - Optional configuration
35491
+ * @param opts.resourceMetadataUrl - Override URL for the protected resource metadata endpoint
35492
+ * @param opts.fetchFn - Custom fetch function for HTTP requests
35493
+ * @returns Authorization server URL, metadata, and resource metadata (if available)
35494
+ */
35495
+ async function discoverOAuthServerInfo(serverUrl, opts) {
35496
+ let resourceMetadata;
35497
+ let authorizationServerUrl;
35498
+ try {
35499
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl: opts?.resourceMetadataUrl }, opts?.fetchFn);
35500
+ if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
35501
+ authorizationServerUrl = resourceMetadata.authorization_servers[0];
35502
+ }
35503
+ }
35504
+ catch {
35505
+ // RFC 9728 not supported -- fall back to treating the server URL as the authorization server
35506
+ }
35507
+ // If we don't get a valid authorization server from protected resource metadata,
35508
+ // fall back to the legacy MCP spec behavior: MCP server base URL acts as the authorization server
35509
+ if (!authorizationServerUrl) {
35510
+ authorizationServerUrl = String(new URL('/', serverUrl));
35511
+ }
35512
+ const authorizationServerMetadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn: opts?.fetchFn });
35513
+ return {
35514
+ authorizationServerUrl,
35515
+ authorizationServerMetadata,
35516
+ resourceMetadata
35517
+ };
35518
+ }
35519
+ /**
35520
+ * Begins the authorization flow with the given server, by generating a PKCE challenge and constructing the authorization URL.
35521
+ */
35522
+ async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
35523
+ let authorizationUrl;
35524
+ if (metadata) {
35525
+ authorizationUrl = new URL(metadata.authorization_endpoint);
35526
+ if (!metadata.response_types_supported.includes(AUTHORIZATION_CODE_RESPONSE_TYPE)) {
35527
+ throw new Error(`Incompatible auth server: does not support response type ${AUTHORIZATION_CODE_RESPONSE_TYPE}`);
35528
+ }
35529
+ if (metadata.code_challenge_methods_supported &&
35530
+ !metadata.code_challenge_methods_supported.includes(AUTHORIZATION_CODE_CHALLENGE_METHOD)) {
35531
+ throw new Error(`Incompatible auth server: does not support code challenge method ${AUTHORIZATION_CODE_CHALLENGE_METHOD}`);
35532
+ }
35533
+ }
35534
+ else {
35535
+ authorizationUrl = new URL('/authorize', authorizationServerUrl);
35536
+ }
35537
+ // Generate PKCE challenge
35538
+ const challenge = await (0, pkce_challenge_1.default)();
35539
+ const codeVerifier = challenge.code_verifier;
35540
+ const codeChallenge = challenge.code_challenge;
35541
+ authorizationUrl.searchParams.set('response_type', AUTHORIZATION_CODE_RESPONSE_TYPE);
35542
+ authorizationUrl.searchParams.set('client_id', clientInformation.client_id);
35543
+ authorizationUrl.searchParams.set('code_challenge', codeChallenge);
35544
+ authorizationUrl.searchParams.set('code_challenge_method', AUTHORIZATION_CODE_CHALLENGE_METHOD);
35545
+ authorizationUrl.searchParams.set('redirect_uri', String(redirectUrl));
35546
+ if (state) {
35547
+ authorizationUrl.searchParams.set('state', state);
35548
+ }
35549
+ if (scope) {
35550
+ authorizationUrl.searchParams.set('scope', scope);
35551
+ }
35552
+ if (scope?.includes('offline_access')) {
35553
+ // if the request includes the OIDC-only "offline_access" scope,
35554
+ // we need to set the prompt to "consent" to ensure the user is prompted to grant offline access
35555
+ // https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
35556
+ authorizationUrl.searchParams.append('prompt', 'consent');
35557
+ }
35558
+ if (resource) {
35559
+ authorizationUrl.searchParams.set('resource', resource.href);
35560
+ }
35561
+ return { authorizationUrl, codeVerifier };
35562
+ }
35563
+ /**
35564
+ * Prepares token request parameters for an authorization code exchange.
35565
+ *
35566
+ * This is the default implementation used by fetchToken when the provider
35567
+ * doesn't implement prepareTokenRequest.
35568
+ *
35569
+ * @param authorizationCode - The authorization code received from the authorization endpoint
35570
+ * @param codeVerifier - The PKCE code verifier
35571
+ * @param redirectUri - The redirect URI used in the authorization request
35572
+ * @returns URLSearchParams for the authorization_code grant
35573
+ */
35574
+ function prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri) {
35575
+ return new URLSearchParams({
35576
+ grant_type: 'authorization_code',
35577
+ code: authorizationCode,
35578
+ code_verifier: codeVerifier,
35579
+ redirect_uri: String(redirectUri)
35580
+ });
35581
+ }
35582
+ /**
35583
+ * Internal helper to execute a token request with the given parameters.
35584
+ * Used by exchangeAuthorization, refreshAuthorization, and fetchToken.
35585
+ */
35586
+ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, fetchFn }) {
35587
+ const tokenUrl = metadata?.token_endpoint ? new URL(metadata.token_endpoint) : new URL('/token', authorizationServerUrl);
35588
+ const headers = new Headers({
35589
+ 'Content-Type': 'application/x-www-form-urlencoded',
35590
+ Accept: 'application/json'
35591
+ });
35592
+ if (resource) {
35593
+ tokenRequestParams.set('resource', resource.href);
35594
+ }
35595
+ if (addClientAuthentication) {
35596
+ await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
35597
+ }
35598
+ else if (clientInformation) {
35599
+ const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? [];
35600
+ const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
35601
+ applyClientAuthentication(authMethod, clientInformation, headers, tokenRequestParams);
35602
+ }
35603
+ const response = await (fetchFn ?? fetch)(tokenUrl, {
35604
+ method: 'POST',
35605
+ headers,
35606
+ body: tokenRequestParams
35607
+ });
35608
+ if (!response.ok) {
35609
+ throw await parseErrorResponse(response);
35610
+ }
35611
+ return auth_js_2.OAuthTokensSchema.parse(await response.json());
35612
+ }
35613
+ /**
35614
+ * Exchanges an authorization code for an access token with the given server.
35615
+ *
35616
+ * Supports multiple client authentication methods as specified in OAuth 2.1:
35617
+ * - Automatically selects the best authentication method based on server support
35618
+ * - Falls back to appropriate defaults when server metadata is unavailable
35619
+ *
35620
+ * @param authorizationServerUrl - The authorization server's base URL
35621
+ * @param options - Configuration object containing client info, auth code, etc.
35622
+ * @returns Promise resolving to OAuth tokens
35623
+ * @throws {Error} When token exchange fails or authentication is invalid
35624
+ */
35625
+ async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn }) {
35626
+ const tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri);
35627
+ return executeTokenRequest(authorizationServerUrl, {
35628
+ metadata,
35629
+ tokenRequestParams,
35630
+ clientInformation,
35631
+ addClientAuthentication,
35632
+ resource,
35633
+ fetchFn
35634
+ });
35635
+ }
35636
+ /**
35637
+ * Exchange a refresh token for an updated access token.
35638
+ *
35639
+ * Supports multiple client authentication methods as specified in OAuth 2.1:
35640
+ * - Automatically selects the best authentication method based on server support
35641
+ * - Preserves the original refresh token if a new one is not returned
35642
+ *
35643
+ * @param authorizationServerUrl - The authorization server's base URL
35644
+ * @param options - Configuration object containing client info, refresh token, etc.
35645
+ * @returns Promise resolving to OAuth tokens (preserves original refresh_token if not replaced)
35646
+ * @throws {Error} When token refresh fails or authentication is invalid
35647
+ */
35648
+ async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
35649
+ const tokenRequestParams = new URLSearchParams({
35650
+ grant_type: 'refresh_token',
35651
+ refresh_token: refreshToken
35652
+ });
35653
+ const tokens = await executeTokenRequest(authorizationServerUrl, {
35654
+ metadata,
35655
+ tokenRequestParams,
35656
+ clientInformation,
35657
+ addClientAuthentication,
35658
+ resource,
35659
+ fetchFn
35660
+ });
35661
+ // Preserve original refresh token if server didn't return a new one
35662
+ return { refresh_token: refreshToken, ...tokens };
35663
+ }
35664
+ /**
35665
+ * Unified token fetching that works with any grant type via provider.prepareTokenRequest().
35666
+ *
35667
+ * This function provides a single entry point for obtaining tokens regardless of the
35668
+ * OAuth grant type. The provider's prepareTokenRequest() method determines which grant
35669
+ * to use and supplies the grant-specific parameters.
35670
+ *
35671
+ * @param provider - OAuth client provider that implements prepareTokenRequest()
35672
+ * @param authorizationServerUrl - The authorization server's base URL
35673
+ * @param options - Configuration for the token request
35674
+ * @returns Promise resolving to OAuth tokens
35675
+ * @throws {Error} When provider doesn't implement prepareTokenRequest or token fetch fails
35676
+ *
35677
+ * @example
35678
+ * // Provider for client_credentials:
35679
+ * class MyProvider implements OAuthClientProvider {
35680
+ * prepareTokenRequest(scope) {
35681
+ * const params = new URLSearchParams({ grant_type: 'client_credentials' });
35682
+ * if (scope) params.set('scope', scope);
35683
+ * return params;
35684
+ * }
35685
+ * // ... other methods
35686
+ * }
35687
+ *
35688
+ * const tokens = await fetchToken(provider, authServerUrl, { metadata });
35689
+ */
35690
+ async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
35691
+ const scope = provider.clientMetadata.scope;
35692
+ // Use provider's prepareTokenRequest if available, otherwise fall back to authorization_code
35693
+ let tokenRequestParams;
35694
+ if (provider.prepareTokenRequest) {
35695
+ tokenRequestParams = await provider.prepareTokenRequest(scope);
35696
+ }
35697
+ // Default to authorization_code grant if no custom prepareTokenRequest
35698
+ if (!tokenRequestParams) {
35699
+ if (!authorizationCode) {
35700
+ throw new Error('Either provider.prepareTokenRequest() or authorizationCode is required');
35701
+ }
35702
+ if (!provider.redirectUrl) {
35703
+ throw new Error('redirectUrl is required for authorization_code flow');
35704
+ }
35705
+ const codeVerifier = await provider.codeVerifier();
35706
+ tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
35707
+ }
35708
+ const clientInformation = await provider.clientInformation();
35709
+ return executeTokenRequest(authorizationServerUrl, {
35710
+ metadata,
35711
+ tokenRequestParams,
35712
+ clientInformation: clientInformation ?? undefined,
35713
+ addClientAuthentication: provider.addClientAuthentication,
35714
+ resource,
35715
+ fetchFn
35716
+ });
35717
+ }
35718
+ /**
35719
+ * Performs OAuth 2.0 Dynamic Client Registration according to RFC 7591.
35720
+ *
35721
+ * If `scope` is provided, it overrides `clientMetadata.scope` in the registration
35722
+ * request body. This allows callers to apply the Scope Selection Strategy (SEP-835)
35723
+ * consistently across both DCR and the subsequent authorization request.
35724
+ */
35725
+ async function registerClient(authorizationServerUrl, { metadata, clientMetadata, scope, fetchFn }) {
35726
+ let registrationUrl;
35727
+ if (metadata) {
35728
+ if (!metadata.registration_endpoint) {
35729
+ throw new Error('Incompatible auth server: does not support dynamic client registration');
35730
+ }
35731
+ registrationUrl = new URL(metadata.registration_endpoint);
35732
+ }
35733
+ else {
35734
+ registrationUrl = new URL('/register', authorizationServerUrl);
35735
+ }
35736
+ const response = await (fetchFn ?? fetch)(registrationUrl, {
35737
+ method: 'POST',
35738
+ headers: {
35739
+ 'Content-Type': 'application/json'
35740
+ },
35741
+ body: JSON.stringify({
35742
+ ...clientMetadata,
35743
+ ...(scope !== undefined ? { scope } : {})
35744
+ })
35745
+ });
35746
+ if (!response.ok) {
35747
+ throw await parseErrorResponse(response);
35748
+ }
35749
+ return auth_js_2.OAuthClientInformationFullSchema.parse(await response.json());
35750
+ }
35751
+
35752
+ return auth$1;
35627
35753
  }
35628
35754
 
35629
35755
  var stream = {};
@@ -35826,7 +35952,7 @@ Object.defineProperty(streamableHttp$1, "__esModule", { value: true });
35826
35952
  streamableHttp$1.StreamableHTTPClientTransport = streamableHttp$1.StreamableHTTPError = void 0;
35827
35953
  const transport_js_1 = transport$1;
35828
35954
  const types_js_1$4 = types$2;
35829
- const auth_js_1 = auth$2;
35955
+ const auth_js_1 = requireAuth();
35830
35956
  const stream_1 = stream;
35831
35957
  // Default reconnection options for StreamableHTTP connections
35832
35958
  const DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS = {
@@ -36950,6 +37076,244 @@ var mcpScopeResolver = {
36950
37076
  applyPathScopeToCredentials: applyPathScopeToCredentials$1,
36951
37077
  };
36952
37078
 
37079
+ /**
37080
+ * mcpOAuthProvider.js
37081
+ *
37082
+ * Implements the MCP SDK's `OAuthClientProvider` interface for custom
37083
+ * remote (streamable_http) MCP servers that authenticate with standard
37084
+ * OAuth 2.0 (e.g. Granola, Linear). This is the generic, SDK-native
37085
+ * alternative to the catalog-only subprocess auth model (`runAuth`)
37086
+ * used by the bundled Google servers.
37087
+ *
37088
+ * Flow (driven by mcpController.connectStreamableHttpWithOAuth):
37089
+ * 1. A loopback HTTP server is started on 127.0.0.1:<ephemeral> to
37090
+ * catch the OAuth redirect. `redirectUrl` reflects that port.
37091
+ * 2. On first connect with no/expired tokens, the SDK calls
37092
+ * `redirectToAuthorization`, which opens the system browser.
37093
+ * 3. The user consents; the authorization server redirects back to the
37094
+ * loopback `/callback?code=...`, which resolves `_waitForCode()`.
37095
+ * 4. The controller calls `transport.finishAuth(code)` → `saveTokens`.
37096
+ *
37097
+ * Tokens, Dynamic Client Registration info, and the PKCE verifier are
37098
+ * persisted encrypted in the SAME providers.json store as credentials
37099
+ * (via providerController), keyed by (appId, serverName). They are never
37100
+ * returned to the renderer.
37101
+ */
37102
+
37103
+ var mcpOAuthProvider;
37104
+ var hasRequiredMcpOAuthProvider;
37105
+
37106
+ function requireMcpOAuthProvider () {
37107
+ if (hasRequiredMcpOAuthProvider) return mcpOAuthProvider;
37108
+ hasRequiredMcpOAuthProvider = 1;
37109
+ const { shell } = require$$0$2;
37110
+ const http = require$$1$5;
37111
+ const providerController = requireProviderController();
37112
+
37113
+ /**
37114
+ * Start a loopback HTTP server bound to 127.0.0.1 on an OS-assigned port.
37115
+ * Resolves once listening with `{ server, port, codePromise }` where
37116
+ * `codePromise` resolves with the OAuth `code` (or rejects on `error`).
37117
+ */
37118
+ function startLoopbackServer() {
37119
+ let resolveCode;
37120
+ let rejectCode;
37121
+ const codePromise = new Promise((resolve, reject) => {
37122
+ resolveCode = resolve;
37123
+ rejectCode = reject;
37124
+ });
37125
+
37126
+ const server = http.createServer((req, res) => {
37127
+ try {
37128
+ const reqUrl = new URL(req.url, "http://127.0.0.1");
37129
+ if (reqUrl.pathname !== "/callback") {
37130
+ res.writeHead(404, { "Content-Type": "text/plain" });
37131
+ res.end("Not found");
37132
+ return;
37133
+ }
37134
+ const error = reqUrl.searchParams.get("error");
37135
+ const code = reqUrl.searchParams.get("code");
37136
+ const heading = error ? "Authorization failed" : "Authorization complete";
37137
+ const body = error
37138
+ ? "You can close this window and try again in Dash."
37139
+ : "You can close this window and return to Dash.";
37140
+ const html =
37141
+ "<!doctype html><html><head><meta charset='utf-8'>" +
37142
+ "<title>Dash</title></head>" +
37143
+ "<body style='font-family:system-ui,sans-serif;background:#111;color:#eee;" +
37144
+ "display:flex;align-items:center;justify-content:center;height:100vh;margin:0;'>" +
37145
+ `<div style='text-align:center;'><h2>${heading}</h2><p>${body}</p></div>` +
37146
+ "</body></html>";
37147
+ res.writeHead(error ? 400 : 200, { "Content-Type": "text/html" });
37148
+ res.end(html);
37149
+
37150
+ if (error) {
37151
+ rejectCode(new Error(`OAuth authorization error: ${error}`));
37152
+ } else if (code) {
37153
+ resolveCode(code);
37154
+ } else {
37155
+ rejectCode(new Error("OAuth callback missing authorization code"));
37156
+ }
37157
+ } catch (e) {
37158
+ rejectCode(e);
37159
+ }
37160
+ });
37161
+
37162
+ return new Promise((resolve, reject) => {
37163
+ server.once("error", reject);
37164
+ // Bind to loopback only (never 0.0.0.0) — port 0 = OS-assigned.
37165
+ server.listen(0, "127.0.0.1", () => {
37166
+ const addr = server.address();
37167
+ resolve({ server, port: addr.port, codePromise });
37168
+ });
37169
+ });
37170
+ }
37171
+
37172
+ /**
37173
+ * Create an OAuthClientProvider for one (appId, serverName) pair.
37174
+ *
37175
+ * @param {object} args
37176
+ * @param {BrowserWindow|null} args.win unused today; reserved for future
37177
+ * window-scoped flows.
37178
+ * @param {string} args.appId application id (providers.json namespace)
37179
+ * @param {string} args.serverName provider/server name (storage key)
37180
+ * @param {object} args.mcpConfig the server config; `mcpConfig.oauth` may
37181
+ * carry `{ scopes, clientId?, clientSecret? }`.
37182
+ */
37183
+ function createMcpOAuthProvider({ win, appId, serverName, mcpConfig }) {
37184
+ const oauth = (mcpConfig && mcpConfig.oauth) || {};
37185
+
37186
+ // Lazy-hydrated cache of persisted { tokens, clientInformation, codeVerifier }.
37187
+ let state = null;
37188
+ const load = () => {
37189
+ if (state) return state;
37190
+ const res = providerController.getOAuthState(win, appId, serverName);
37191
+ state = (res && res.oauth) || {};
37192
+ return state;
37193
+ };
37194
+ const persist = (patch) => {
37195
+ state = { ...load(), ...patch };
37196
+ providerController.saveOAuthState(win, appId, serverName, state);
37197
+ };
37198
+
37199
+ // Loopback server for the current flow (null when idle).
37200
+ let loopback = null;
37201
+
37202
+ return {
37203
+ get redirectUrl() {
37204
+ return loopback
37205
+ ? `http://127.0.0.1:${loopback.port}/callback`
37206
+ : undefined;
37207
+ },
37208
+
37209
+ get clientMetadata() {
37210
+ const redirectUrl = this.redirectUrl;
37211
+ return {
37212
+ client_name: `Dash – ${serverName}`,
37213
+ redirect_uris: redirectUrl ? [redirectUrl] : [],
37214
+ grant_types: ["authorization_code", "refresh_token"],
37215
+ response_types: ["code"],
37216
+ token_endpoint_auth_method: oauth.clientSecret
37217
+ ? "client_secret_post"
37218
+ : "none",
37219
+ ...(oauth.scopes ? { scope: oauth.scopes } : {}),
37220
+ };
37221
+ },
37222
+
37223
+ clientInformation() {
37224
+ // Pre-registered (manual) client wins over Dynamic Client Registration.
37225
+ if (oauth.clientId) {
37226
+ return {
37227
+ client_id: oauth.clientId,
37228
+ ...(oauth.clientSecret ? { client_secret: oauth.clientSecret } : {}),
37229
+ };
37230
+ }
37231
+ return load().clientInformation;
37232
+ },
37233
+
37234
+ saveClientInformation(clientInformation) {
37235
+ persist({ clientInformation });
37236
+ },
37237
+
37238
+ tokens() {
37239
+ return load().tokens;
37240
+ },
37241
+
37242
+ // Called by the SDK on initial authorization AND on every refresh.
37243
+ saveTokens(tokens) {
37244
+ persist({ tokens });
37245
+ },
37246
+
37247
+ saveCodeVerifier(codeVerifier) {
37248
+ persist({ codeVerifier });
37249
+ },
37250
+
37251
+ codeVerifier() {
37252
+ const v = load().codeVerifier;
37253
+ if (!v) throw new Error("No OAuth code verifier saved");
37254
+ return v;
37255
+ },
37256
+
37257
+ async redirectToAuthorization(authorizationUrl) {
37258
+ await shell.openExternal(authorizationUrl.toString());
37259
+ },
37260
+
37261
+ // Lets the SDK clear bad credentials so the next attempt re-auths
37262
+ // without manual intervention (e.g. refresh token revoked).
37263
+ invalidateCredentials(scope) {
37264
+ if (scope === "all" || scope === "tokens") persist({ tokens: undefined });
37265
+ if (scope === "all" || scope === "client")
37266
+ persist({ clientInformation: undefined });
37267
+ if (scope === "all" || scope === "verifier")
37268
+ persist({ codeVerifier: undefined });
37269
+ },
37270
+
37271
+ // --- helpers used by the controller (not part of the SDK interface) ---
37272
+
37273
+ async _startLoopback() {
37274
+ if (!loopback) {
37275
+ loopback = await startLoopbackServer();
37276
+ }
37277
+ return loopback;
37278
+ },
37279
+
37280
+ _stopLoopback() {
37281
+ if (loopback) {
37282
+ try {
37283
+ loopback.server.close();
37284
+ } catch (_e) {
37285
+ /* already closing */
37286
+ }
37287
+ loopback = null;
37288
+ }
37289
+ },
37290
+
37291
+ _waitForCode(timeoutMs = 120000) {
37292
+ if (!loopback) {
37293
+ return Promise.reject(new Error("OAuth loopback server not started"));
37294
+ }
37295
+ return Promise.race([
37296
+ loopback.codePromise,
37297
+ new Promise((_resolve, reject) =>
37298
+ setTimeout(
37299
+ () =>
37300
+ reject(
37301
+ new Error(
37302
+ "OAuth authorization timed out. Close the browser tab and try again.",
37303
+ ),
37304
+ ),
37305
+ timeoutMs,
37306
+ ),
37307
+ ),
37308
+ ]);
37309
+ },
37310
+ };
37311
+ }
37312
+
37313
+ mcpOAuthProvider = { createMcpOAuthProvider, startLoopbackServer };
37314
+ return mcpOAuthProvider;
37315
+ }
37316
+
36953
37317
  /**
36954
37318
  * mcpController.js
36955
37319
  *
@@ -36966,7 +37330,7 @@ var mcpScopeResolver = {
36966
37330
  const { Client } = require$$0$8;
36967
37331
  const {
36968
37332
  StdioClientTransport,
36969
- } = require$$1$5;
37333
+ } = require$$1$6;
36970
37334
  const {
36971
37335
  StreamableHTTPClientTransport,
36972
37336
  } = streamableHttp$1;
@@ -37377,6 +37741,57 @@ async function refreshGoogleOAuthToken(tokenRefresh) {
37377
37741
  console.log("[mcpController] Google OAuth token refreshed successfully");
37378
37742
  }
37379
37743
 
37744
+ /**
37745
+ * Connect a streamable_http MCP server that authenticates via OAuth 2.0,
37746
+ * using the SDK's native OAuthClientProvider.
37747
+ *
37748
+ * Drives the connect → UnauthorizedError → finishAuth → reconnect dance:
37749
+ * - With valid (or refreshable) tokens, the first connect succeeds and no
37750
+ * browser opens (fast path).
37751
+ * - Otherwise the SDK opens the system browser via
37752
+ * `redirectToAuthorization`, throws UnauthorizedError, and we wait for
37753
+ * the loopback callback, exchange the code, then reconnect.
37754
+ *
37755
+ * The authorized reconnect uses a FRESH transport because the first
37756
+ * transport already called `start()` and cannot be restarted. `finishAuth`
37757
+ * runs on the original transport so it reuses that attempt's discovery state.
37758
+ *
37759
+ * @returns {Promise<{ client, transport }>} a connected client + transport
37760
+ */
37761
+ async function connectStreamableHttpWithOAuth(urlObj, oauthProvider) {
37762
+ const {
37763
+ UnauthorizedError,
37764
+ } = requireAuth();
37765
+
37766
+ await oauthProvider._startLoopback();
37767
+ try {
37768
+ const client = new Client({ name: "dash", version: "1.0.0" });
37769
+ const transport = new StreamableHTTPClientTransport(urlObj, {
37770
+ authProvider: oauthProvider,
37771
+ });
37772
+ try {
37773
+ await client.connect(transport);
37774
+ return { client, transport };
37775
+ } catch (err) {
37776
+ if (!(err instanceof UnauthorizedError)) throw err;
37777
+ console.log(
37778
+ "[mcpController] OAuth required — waiting for browser authorization...",
37779
+ );
37780
+ const code = await oauthProvider._waitForCode();
37781
+ await transport.finishAuth(code);
37782
+
37783
+ const client2 = new Client({ name: "dash", version: "1.0.0" });
37784
+ const transport2 = new StreamableHTTPClientTransport(urlObj, {
37785
+ authProvider: oauthProvider,
37786
+ });
37787
+ await client2.connect(transport2);
37788
+ return { client: client2, transport: transport2 };
37789
+ }
37790
+ } finally {
37791
+ oauthProvider._stopLoopback();
37792
+ }
37793
+ }
37794
+
37380
37795
  const mcpController$3 = {
37381
37796
  /**
37382
37797
  * startServer
@@ -37409,6 +37824,7 @@ const mcpController$3 = {
37409
37824
  credentials,
37410
37825
  workspaceId,
37411
37826
  pathScope = null,
37827
+ appId = null,
37412
37828
  ) => {
37413
37829
  const key = serverKey(workspaceId, serverName);
37414
37830
  // Slice 3b: when the gate is enforced, override credentials with
@@ -37483,6 +37899,11 @@ const mcpController$3 = {
37483
37899
 
37484
37900
  // Create transport based on type
37485
37901
  let transport;
37902
+ // OAuth state, populated below for OAuth streamable_http servers.
37903
+ // When set, the connect step runs the interactive OAuth dance
37904
+ // (which builds its own transport) instead of the generic connect.
37905
+ let oauthProvider = null;
37906
+ let oauthUrl = null;
37486
37907
  if (mcpConfig.transport === "streamable_http") {
37487
37908
  // Remote HTTP transport - connect to a hosted MCP server
37488
37909
  const url = interpolate$1(mcpConfig.url, credentials);
@@ -37490,21 +37911,37 @@ const mcpController$3 = {
37490
37911
  throw new Error("Streamable HTTP transport requires a URL");
37491
37912
  }
37492
37913
 
37493
- // Build request headers from headerTemplate
37494
- const headers = {};
37495
- if (mcpConfig.headerTemplate && credentials) {
37496
- Object.entries(mcpConfig.headerTemplate).forEach(
37497
- ([headerName, template]) => {
37498
- headers[headerName] = interpolate$1(template, credentials);
37914
+ if (mcpConfig.auth === "oauth" || mcpConfig.oauth) {
37915
+ // OAuth 2.0 server (e.g. Granola, Linear): authenticate via the
37916
+ // SDK's native OAuthClientProvider + browser flow + token
37917
+ // refresh. Transport is created during the connect dance below.
37918
+ const {
37919
+ createMcpOAuthProvider,
37920
+ } = requireMcpOAuthProvider();
37921
+ oauthProvider = createMcpOAuthProvider({
37922
+ win,
37923
+ appId,
37924
+ serverName,
37925
+ mcpConfig,
37926
+ });
37927
+ oauthUrl = new URL(url);
37928
+ } else {
37929
+ // Build request headers from headerTemplate
37930
+ const headers = {};
37931
+ if (mcpConfig.headerTemplate && credentials) {
37932
+ Object.entries(mcpConfig.headerTemplate).forEach(
37933
+ ([headerName, template]) => {
37934
+ headers[headerName] = interpolate$1(template, credentials);
37935
+ },
37936
+ );
37937
+ }
37938
+
37939
+ transport = new StreamableHTTPClientTransport(new URL(url), {
37940
+ requestInit: {
37941
+ headers,
37499
37942
  },
37500
- );
37943
+ });
37501
37944
  }
37502
-
37503
- transport = new StreamableHTTPClientTransport(new URL(url), {
37504
- requestInit: {
37505
- headers,
37506
- },
37507
- });
37508
37945
  } else {
37509
37946
  // stdio transport (default) - spawn a local child process
37510
37947
  const env = cleanEnvForChildProcess();
@@ -37589,14 +38026,26 @@ const mcpController$3 = {
37589
38026
  serverName,
37590
38027
  });
37591
38028
 
37592
- // Create MCP client
37593
- const client = new Client({
37594
- name: "dash",
37595
- version: "1.0.0",
37596
- });
37597
-
37598
- // Connect to the server
37599
- await client.connect(transport);
38029
+ // Create / connect MCP client. OAuth servers run the interactive
38030
+ // connect → authorize → reconnect dance, which yields its own
38031
+ // client + transport (a fresh transport is required for the
38032
+ // authorized reconnect).
38033
+ let client;
38034
+ if (oauthProvider) {
38035
+ const oauthResult = await connectStreamableHttpWithOAuth(
38036
+ oauthUrl,
38037
+ oauthProvider,
38038
+ );
38039
+ client = oauthResult.client;
38040
+ transport = oauthResult.transport;
38041
+ } else {
38042
+ client = new Client({
38043
+ name: "dash",
38044
+ version: "1.0.0",
38045
+ });
38046
+ // Connect to the server
38047
+ await client.connect(transport);
38048
+ }
37600
38049
 
37601
38050
  // List available tools
37602
38051
  let tools = [];
@@ -37682,6 +38131,83 @@ const mcpController$3 = {
37682
38131
  return startPromise;
37683
38132
  },
37684
38133
 
38134
+ /**
38135
+ * authorizeServer
38136
+ * Run the interactive OAuth 2.0 authorization flow on demand for a
38137
+ * custom streamable_http MCP server (the Settings "Authorize" button
38138
+ * and the re-auth banner). Opens the system browser, completes the
38139
+ * flow, and persists tokens encrypted. A subsequent startServer hits
38140
+ * the valid-token fast path with no browser.
38141
+ *
38142
+ * @param {BrowserWindow} win the main window
38143
+ * @param {string} serverName the provider/server name
38144
+ * @param {object} mcpConfig must be streamable_http with auth: "oauth"
38145
+ * @param {object} credentials decrypted credentials (for URL interpolation)
38146
+ * @param {string} appId application id (token storage namespace)
38147
+ * @returns {Promise<{ success, serverName, tools } | { error, message }>}
38148
+ */
38149
+ authorizeServer: async (win, serverName, mcpConfig, credentials, appId) => {
38150
+ try {
38151
+ if (
38152
+ !mcpConfig ||
38153
+ mcpConfig.transport !== "streamable_http" ||
38154
+ !(mcpConfig.auth === "oauth" || mcpConfig.oauth)
38155
+ ) {
38156
+ return {
38157
+ error: true,
38158
+ message: "Server is not configured for OAuth authorization",
38159
+ };
38160
+ }
38161
+
38162
+ const url = interpolate$1(mcpConfig.url, credentials);
38163
+ if (!url) {
38164
+ return {
38165
+ error: true,
38166
+ message: "Streamable HTTP transport requires a URL",
38167
+ };
38168
+ }
38169
+
38170
+ const { createMcpOAuthProvider } = requireMcpOAuthProvider();
38171
+ const oauthProvider = createMcpOAuthProvider({
38172
+ win,
38173
+ appId,
38174
+ serverName,
38175
+ mcpConfig,
38176
+ });
38177
+
38178
+ const { client } = await connectStreamableHttpWithOAuth(
38179
+ new URL(url),
38180
+ oauthProvider,
38181
+ );
38182
+
38183
+ // Verify the connection by listing tools, then close — startServer
38184
+ // will reconnect later using the freshly stored tokens.
38185
+ let tools = [];
38186
+ try {
38187
+ const toolsResult = await client.listTools();
38188
+ tools = toolsResult.tools || [];
38189
+ } catch (_e) {
38190
+ // Tools optional for the auth check
38191
+ }
38192
+ try {
38193
+ await client.close();
38194
+ } catch (_e) {
38195
+ /* ignore close errors */
38196
+ }
38197
+
38198
+ console.log(
38199
+ `[mcpController] OAuth authorization succeeded for ${serverName} (${tools.length} tools)`,
38200
+ );
38201
+ return { success: true, serverName, tools };
38202
+ } catch (error) {
38203
+ console.error(
38204
+ `[mcpController] OAuth authorization failed for ${serverName}:`,
38205
+ error,
38206
+ );
38207
+ return { error: true, message: error.message };
38208
+ }
38209
+ },
38210
+
37685
38211
  /**
37686
38212
  * stopServer
37687
38213
  * Stop a running MCP server
@@ -56494,13 +57020,13 @@ __export(src_exports, {
56494
57020
  var dist = __toCommonJS(src_exports);
56495
57021
 
56496
57022
  // src/server.ts
56497
- var import_node_http = require$$0$a;
57023
+ var import_node_http = require$$1$5;
56498
57024
 
56499
57025
  // src/listener.ts
56500
- var import_node_http22 = require$$1$6;
57026
+ var import_node_http22 = require$$1$7;
56501
57027
 
56502
57028
  // src/request.ts
56503
- var import_node_http2 = require$$1$6;
57029
+ var import_node_http2 = require$$1$7;
56504
57030
  var import_node_stream = require$$5$1;
56505
57031
  var RequestError = class extends Error {
56506
57032
  constructor(message, options) {
@@ -60154,10 +60680,10 @@ var themeFromUrlErrors$1 = {
60154
60680
  * computed styles, and favicon/logo images (via node-vibrant).
60155
60681
  */
60156
60682
 
60157
- const css = require$$0$b;
60158
- const { Vibrant } = require$$1$7;
60683
+ const css = require$$0$a;
60684
+ const { Vibrant } = require$$1$8;
60159
60685
  const https = require$$11;
60160
- const http = require$$0$a;
60686
+ const http = require$$1$5;
60161
60687
  const { URL: URL$1 } = require$$4;
60162
60688
  const {
60163
60689
  UrlUnreachableError,
@@ -65005,7 +65531,7 @@ var toolHandlers$1 = {
65005
65531
  * per-request, receiving the full messages array each time.
65006
65532
  */
65007
65533
 
65008
- const Anthropic = require$$0$c;
65534
+ const Anthropic = require$$0$b;
65009
65535
  const mcpController$2 = mcpControllerExports;
65010
65536
  const cliController$1 = cliControllerExports;
65011
65537
  const toolDefinitions = toolDefinitions$1;
@@ -74947,6 +75473,7 @@ const {
74947
75473
  MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM: MCP_INSTALL_KNOWN_EXTERNAL_CONFIRM$1,
74948
75474
  MCP_INSTALL_KNOWN_EXTERNAL_RESULT: MCP_INSTALL_KNOWN_EXTERNAL_RESULT$1,
74949
75475
  MCP_RUN_AUTH,
75476
+ MCP_AUTHORIZE,
74950
75477
  } = events$8;
74951
75478
 
74952
75479
  const mcpApi$2 = {
@@ -74971,6 +75498,7 @@ const mcpApi$2 = {
74971
75498
  credentials,
74972
75499
  workspaceId = null,
74973
75500
  pathScope = null,
75501
+ appId = null,
74974
75502
  ) =>
74975
75503
  ipcRenderer$j.invoke(MCP_START_SERVER, {
74976
75504
  serverName,
@@ -74978,6 +75506,7 @@ const mcpApi$2 = {
74978
75506
  credentials,
74979
75507
  workspaceId,
74980
75508
  pathScope,
75509
+ appId,
74981
75510
  }),
74982
75511
 
74983
75512
  /**
@@ -75131,6 +75660,27 @@ const mcpApi$2 = {
75131
75660
  */
75132
75661
  runAuth: (mcpConfig, credentials, authCommand) =>
75133
75662
  ipcRenderer$j.invoke(MCP_RUN_AUTH, { mcpConfig, credentials, authCommand }),
75663
+
75664
+ /**
75665
+ * authorize
75666
+ * Run the interactive OAuth 2.0 authorization flow for a custom
75667
+ * streamable_http MCP server (opens the system browser). Tokens are
75668
+ * persisted encrypted in the main process; a later startServer reuses
75669
+ * them with no browser.
75670
+ *
75671
+ * @param {string} serverName the provider/server name (token storage key)
75672
+ * @param {object} mcpConfig { transport: "streamable_http", auth: "oauth", url, oauth }
75673
+ * @param {object} credentials decrypted credentials (URL interpolation)
75674
+ * @param {string} appId application id (token storage namespace)
75675
+ * @returns {Promise<{ success, serverName, tools } | { error, message }>}
75676
+ */
75677
+ authorize: (serverName, mcpConfig, credentials, appId = null) =>
75678
+ ipcRenderer$j.invoke(MCP_AUTHORIZE, {
75679
+ serverName,
75680
+ mcpConfig,
75681
+ credentials,
75682
+ appId,
75683
+ }),
75134
75684
  };
75135
75685
 
75136
75686
  var mcpApi_1 = mcpApi$2;