@ai-sdk/mcp 0.0.42 → 0.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -269,15 +269,6 @@ import pkceChallenge from "pkce-challenge";
269
269
 
270
270
  // src/tool/oauth-types.ts
271
271
  import { z as z3 } from "zod/v4";
272
- var OAuthTokensSchema = z3.object({
273
- access_token: z3.string(),
274
- id_token: z3.string().optional(),
275
- // Optional for OAuth 2.1, but necessary in OpenID Connect
276
- token_type: z3.string(),
277
- expires_in: z3.number().optional(),
278
- scope: z3.string().optional(),
279
- refresh_token: z3.string().optional()
280
- }).strip();
281
272
  var SafeUrlSchema = z3.string().url().superRefine((val, ctx) => {
282
273
  if (!URL.canParse(val)) {
283
274
  ctx.addIssue({
@@ -294,6 +285,18 @@ var SafeUrlSchema = z3.string().url().superRefine((val, ctx) => {
294
285
  },
295
286
  { message: "URL cannot use javascript:, data:, or vbscript: scheme" }
296
287
  );
288
+ var OAuthTokensSchema = z3.object({
289
+ access_token: z3.string(),
290
+ id_token: z3.string().optional(),
291
+ // Optional for OAuth 2.1, but necessary in OpenID Connect
292
+ token_type: z3.string(),
293
+ expires_in: z3.number().optional(),
294
+ scope: z3.string().optional(),
295
+ refresh_token: z3.string().optional(),
296
+ issuer: SafeUrlSchema.optional(),
297
+ authorization_server: SafeUrlSchema.optional(),
298
+ token_endpoint: SafeUrlSchema.optional()
299
+ }).strip();
297
300
  var OAuthProtectedResourceMetadataSchema = z3.object({
298
301
  resource: z3.string().url(),
299
302
  authorization_servers: z3.array(SafeUrlSchema).optional(),
@@ -346,7 +349,10 @@ var OAuthClientInformationSchema = z3.object({
346
349
  client_id: z3.string(),
347
350
  client_secret: z3.string().optional(),
348
351
  client_id_issued_at: z3.number().optional(),
349
- client_secret_expires_at: z3.number().optional()
352
+ client_secret_expires_at: z3.number().optional(),
353
+ issuer: SafeUrlSchema.optional(),
354
+ authorization_server: SafeUrlSchema.optional(),
355
+ token_endpoint: SafeUrlSchema.optional()
350
356
  }).strip();
351
357
  var OAuthClientMetadataSchema = z3.object({
352
358
  redirect_uris: z3.array(SafeUrlSchema),
@@ -449,6 +455,130 @@ var UnauthorizedError = class extends Error {
449
455
  this.name = "UnauthorizedError";
450
456
  }
451
457
  };
458
+ function normalizeUrl(url) {
459
+ return new URL(url).href;
460
+ }
461
+ function validateAuthorizationResponseIssuer({
462
+ callbackIssuer,
463
+ expectedIssuer
464
+ }) {
465
+ if (callbackIssuer != null && callbackIssuer !== expectedIssuer) {
466
+ throw new MCPClientOAuthError({
467
+ message: `OAuth authorization response issuer ${callbackIssuer} does not match expected issuer ${expectedIssuer}`
468
+ });
469
+ }
470
+ }
471
+ function createAuthorizationServerInformation(authorizationServerUrl, metadata) {
472
+ var _a3;
473
+ return {
474
+ issuer: (_a3 = metadata == null ? void 0 : metadata.issuer) != null ? _a3 : String(authorizationServerUrl),
475
+ authorizationServerUrl: normalizeUrl(authorizationServerUrl),
476
+ tokenEndpoint: normalizeUrl(
477
+ (metadata == null ? void 0 : metadata.token_endpoint) ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl)
478
+ )
479
+ };
480
+ }
481
+ function addAuthorizationServerInformationToTokens(tokens, authorizationServerInformation) {
482
+ return {
483
+ ...tokens,
484
+ issuer: authorizationServerInformation.issuer,
485
+ authorization_server: authorizationServerInformation.authorizationServerUrl,
486
+ token_endpoint: authorizationServerInformation.tokenEndpoint
487
+ };
488
+ }
489
+ function addAuthorizationServerInformationToClientInformation(clientInformation, authorizationServerInformation) {
490
+ return {
491
+ ...clientInformation,
492
+ issuer: authorizationServerInformation.issuer,
493
+ authorization_server: authorizationServerInformation.authorizationServerUrl,
494
+ token_endpoint: authorizationServerInformation.tokenEndpoint
495
+ };
496
+ }
497
+ function getAuthorizationServerInformationFromCredentials(credentials) {
498
+ if (!(credentials == null ? void 0 : credentials.authorization_server) || !credentials.token_endpoint) {
499
+ return void 0;
500
+ }
501
+ return {
502
+ issuer: credentials.issuer,
503
+ authorizationServerUrl: normalizeUrl(credentials.authorization_server),
504
+ tokenEndpoint: normalizeUrl(credentials.token_endpoint)
505
+ };
506
+ }
507
+ async function getStoredAuthorizationServerInformation({
508
+ provider,
509
+ clientInformation,
510
+ tokens
511
+ }) {
512
+ var _a3;
513
+ const tokenAuthorizationServerInformation = getAuthorizationServerInformationFromCredentials(tokens);
514
+ if (tokenAuthorizationServerInformation) {
515
+ return tokenAuthorizationServerInformation;
516
+ }
517
+ const providerAuthorizationServerInformation = await ((_a3 = provider.authorizationServerInformation) == null ? void 0 : _a3.call(provider));
518
+ if (providerAuthorizationServerInformation) {
519
+ return {
520
+ issuer: providerAuthorizationServerInformation.issuer,
521
+ authorizationServerUrl: normalizeUrl(
522
+ providerAuthorizationServerInformation.authorizationServerUrl
523
+ ),
524
+ tokenEndpoint: normalizeUrl(
525
+ providerAuthorizationServerInformation.tokenEndpoint
526
+ )
527
+ };
528
+ }
529
+ return getAuthorizationServerInformationFromCredentials(clientInformation);
530
+ }
531
+ async function saveAuthorizationServerInformation({
532
+ provider,
533
+ clientInformation,
534
+ authorizationServerInformation
535
+ }) {
536
+ if (provider.saveAuthorizationServerInformation) {
537
+ await provider.saveAuthorizationServerInformation(
538
+ authorizationServerInformation
539
+ );
540
+ return true;
541
+ }
542
+ if (provider.saveClientInformation) {
543
+ await provider.saveClientInformation(
544
+ addAuthorizationServerInformationToClientInformation(
545
+ clientInformation,
546
+ authorizationServerInformation
547
+ )
548
+ );
549
+ return true;
550
+ }
551
+ const configuredBinding = getAuthorizationServerInformationFromCredentials(clientInformation);
552
+ if (configuredBinding) {
553
+ assertAuthorizationServerInformationMatches({
554
+ storedAuthorizationServerInformation: configuredBinding,
555
+ currentAuthorizationServerInformation: authorizationServerInformation
556
+ });
557
+ return true;
558
+ }
559
+ return false;
560
+ }
561
+ function assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl) {
562
+ if (!resourceMetadataUrl) {
563
+ return;
564
+ }
565
+ const expectedOrigin = new URL(serverUrl).origin;
566
+ if (resourceMetadataUrl.origin !== expectedOrigin) {
567
+ throw new MCPClientOAuthError({
568
+ message: `OAuth protected resource metadata URL ${resourceMetadataUrl.href} must have the same origin as the MCP server URL ${expectedOrigin}`
569
+ });
570
+ }
571
+ }
572
+ function assertAuthorizationServerInformationMatches({
573
+ storedAuthorizationServerInformation,
574
+ currentAuthorizationServerInformation
575
+ }) {
576
+ if (storedAuthorizationServerInformation.issuer != null && currentAuthorizationServerInformation.issuer != null && storedAuthorizationServerInformation.issuer !== currentAuthorizationServerInformation.issuer || storedAuthorizationServerInformation.authorizationServerUrl !== currentAuthorizationServerInformation.authorizationServerUrl || storedAuthorizationServerInformation.tokenEndpoint !== currentAuthorizationServerInformation.tokenEndpoint) {
577
+ throw new MCPClientOAuthError({
578
+ message: "OAuth authorization server metadata does not match the metadata that issued the stored credentials"
579
+ });
580
+ }
581
+ }
452
582
  function extractResourceMetadataUrl(response) {
453
583
  var _a3;
454
584
  const header = (_a3 = response.headers.get("www-authenticate")) != null ? _a3 : response.headers.get("WWW-Authenticate");
@@ -591,15 +721,18 @@ async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn =
591
721
  function buildDiscoveryUrls(authorizationServerUrl) {
592
722
  const url = typeof authorizationServerUrl === "string" ? new URL(authorizationServerUrl) : authorizationServerUrl;
593
723
  const hasPath = url.pathname !== "/";
724
+ const rootIssuer = url.origin;
594
725
  const urlsToTry = [];
595
726
  if (!hasPath) {
596
727
  urlsToTry.push({
597
728
  url: new URL("/.well-known/oauth-authorization-server", url.origin),
598
- type: "oauth"
729
+ type: "oauth",
730
+ expectedIssuer: rootIssuer
599
731
  });
600
732
  urlsToTry.push({
601
733
  url: new URL("/.well-known/openid-configuration", url.origin),
602
- type: "oidc"
734
+ type: "oidc",
735
+ expectedIssuer: rootIssuer
603
736
  });
604
737
  return urlsToTry;
605
738
  }
@@ -607,27 +740,42 @@ function buildDiscoveryUrls(authorizationServerUrl) {
607
740
  if (pathname.endsWith("/")) {
608
741
  pathname = pathname.slice(0, -1);
609
742
  }
743
+ const pathIssuer = `${url.origin}${pathname}`;
610
744
  urlsToTry.push({
611
745
  url: new URL(
612
746
  `/.well-known/oauth-authorization-server${pathname}`,
613
747
  url.origin
614
748
  ),
615
- type: "oauth"
749
+ type: "oauth",
750
+ expectedIssuer: pathIssuer
616
751
  });
617
752
  urlsToTry.push({
618
753
  url: new URL("/.well-known/oauth-authorization-server", url.origin),
619
- type: "oauth"
754
+ type: "oauth",
755
+ expectedIssuer: rootIssuer
620
756
  });
621
757
  urlsToTry.push({
622
758
  url: new URL(`/.well-known/openid-configuration${pathname}`, url.origin),
623
- type: "oidc"
759
+ type: "oidc",
760
+ expectedIssuer: pathIssuer
624
761
  });
762
+ const oidcDiscoveryUrl = new URL(url.origin);
763
+ oidcDiscoveryUrl.pathname = `${pathname}/.well-known/openid-configuration`;
625
764
  urlsToTry.push({
626
- url: new URL(`${pathname}/.well-known/openid-configuration`, url.origin),
627
- type: "oidc"
765
+ url: oidcDiscoveryUrl,
766
+ type: "oidc",
767
+ expectedIssuer: pathIssuer
628
768
  });
629
769
  return urlsToTry;
630
770
  }
771
+ function assertMetadataIssuerMatches(metadata, expectedIssuer) {
772
+ const issuerMatches = metadata.issuer === expectedIssuer || expectedIssuer === new URL(expectedIssuer).origin && metadata.issuer === `${expectedIssuer}/`;
773
+ if (!issuerMatches) {
774
+ throw new MCPClientOAuthError({
775
+ message: `OAuth authorization server metadata issuer ${metadata.issuer} does not match expected issuer ${expectedIssuer}`
776
+ });
777
+ }
778
+ }
631
779
  async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
632
780
  fetchFn = fetch,
633
781
  protocolVersion = LATEST_PROTOCOL_VERSION,
@@ -635,8 +783,14 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
635
783
  } = {}) {
636
784
  var _a3;
637
785
  const headers = { "MCP-Protocol-Version": protocolVersion };
786
+ const authorizationServerOrigin = new URL(authorizationServerUrl).origin;
638
787
  const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
639
- for (const { url: endpointUrl, type } of urlsToTry) {
788
+ if (urlsToTry.some(({ url }) => url.origin !== authorizationServerOrigin)) {
789
+ throw new MCPClientOAuthError({
790
+ message: "OAuth discovery URL changed authorization-server origin"
791
+ });
792
+ }
793
+ for (const { url: endpointUrl, type, expectedIssuer } of urlsToTry) {
640
794
  const response = await fetchWithCorsRetry(
641
795
  endpointUrl,
642
796
  headers,
@@ -655,11 +809,14 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
655
809
  );
656
810
  }
657
811
  if (type === "oauth") {
658
- return OAuthMetadataSchema.parse(await response.json());
812
+ const metadata = OAuthMetadataSchema.parse(await response.json());
813
+ assertMetadataIssuerMatches(metadata, expectedIssuer);
814
+ return metadata;
659
815
  } else {
660
816
  const metadata = OpenIdProviderDiscoveryMetadataSchema.parse(
661
817
  await response.json()
662
818
  );
819
+ assertMetadataIssuerMatches(metadata, expectedIssuer);
663
820
  if (!((_a3 = metadata.code_challenge_methods_supported) == null ? void 0 : _a3.includes("S256"))) {
664
821
  throw new Error(
665
822
  `Incompatible OIDC provider at ${endpointUrl}: does not support S256 code challenge method required by MCP specification`
@@ -668,7 +825,6 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
668
825
  return metadata;
669
826
  }
670
827
  }
671
- return void 0;
672
828
  }
673
829
  async function startAuthorization(authorizationServerUrl, {
674
830
  metadata,
@@ -950,16 +1106,28 @@ async function registerClient(authorizationServerUrl, {
950
1106
  return OAuthClientInformationFullSchema.parse(await response.json());
951
1107
  }
952
1108
  async function auth(provider, options) {
953
- var _a3, _b3;
1109
+ var _a3, _b3, _c;
1110
+ const refreshAttempt = {};
954
1111
  try {
955
- return await authInternal(provider, options);
1112
+ return await authInternal(provider, options, refreshAttempt);
956
1113
  } catch (error) {
957
1114
  if (error instanceof InvalidClientError || error instanceof UnauthorizedClientError) {
958
- await ((_a3 = provider.invalidateCredentials) == null ? void 0 : _a3.call(provider, "all"));
959
- return await authInternal(provider, options);
960
- } else if (error instanceof InvalidGrantError) {
961
- await ((_b3 = provider.invalidateCredentials) == null ? void 0 : _b3.call(provider, "tokens"));
1115
+ if (options.authorizationCode !== void 0 || !await ((_a3 = provider.isClientInformationDynamicallyRegistered) == null ? void 0 : _a3.call(provider))) {
1116
+ throw error;
1117
+ }
1118
+ await ((_b3 = provider.invalidateCredentials) == null ? void 0 : _b3.call(provider, "all"));
962
1119
  return await authInternal(provider, options);
1120
+ } else if (error instanceof InvalidGrantError && refreshAttempt.tokens) {
1121
+ const retryAuthorizationServerInformation = getAuthorizationServerInformationFromCredentials(refreshAttempt.tokens);
1122
+ await ((_c = provider.invalidateCredentials) == null ? void 0 : _c.call(provider, "tokens", {
1123
+ tokens: refreshAttempt.tokens
1124
+ }));
1125
+ return await authInternal(
1126
+ provider,
1127
+ options,
1128
+ void 0,
1129
+ retryAuthorizationServerInformation
1130
+ );
963
1131
  }
964
1132
  throw error;
965
1133
  }
@@ -988,12 +1156,18 @@ async function selectResourceURL(serverUrl, provider, resourceMetadata) {
988
1156
  async function authInternal(provider, {
989
1157
  serverUrl,
990
1158
  authorizationCode,
1159
+ callbackState,
1160
+ callbackIssuer,
991
1161
  scope,
992
1162
  resourceMetadataUrl,
993
1163
  fetchFn
994
- }) {
1164
+ }, refreshAttempt, retryAuthorizationServerInformation) {
1165
+ var _a3, _b3, _c, _d, _e, _f;
995
1166
  let resourceMetadata;
996
1167
  let authorizationServerUrl;
1168
+ let clientInformation;
1169
+ let callbackAuthorizationServerInformation;
1170
+ assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl);
997
1171
  try {
998
1172
  resourceMetadata = await discoverOAuthProtectedResourceMetadata(
999
1173
  serverUrl,
@@ -1005,8 +1179,17 @@ async function authInternal(provider, {
1005
1179
  }
1006
1180
  } catch (e) {
1007
1181
  }
1182
+ if (authorizationCode !== void 0) {
1183
+ clientInformation = await Promise.resolve(provider.clientInformation());
1184
+ if (clientInformation) {
1185
+ callbackAuthorizationServerInformation = await getStoredAuthorizationServerInformation({
1186
+ provider,
1187
+ clientInformation
1188
+ });
1189
+ }
1190
+ }
1008
1191
  if (!authorizationServerUrl) {
1009
- authorizationServerUrl = serverUrl;
1192
+ authorizationServerUrl = (_a3 = callbackAuthorizationServerInformation == null ? void 0 : callbackAuthorizationServerInformation.authorizationServerUrl) != null ? _a3 : serverUrl;
1010
1193
  }
1011
1194
  const parsedServerUrl = new URL(serverUrl);
1012
1195
  const parsedAuthorizationServerUrl = new URL(authorizationServerUrl);
@@ -1021,6 +1204,11 @@ async function authInternal(provider, {
1021
1204
  provider,
1022
1205
  resourceMetadata
1023
1206
  );
1207
+ await ((_b3 = provider.validateAuthorizationServerURL) == null ? void 0 : _b3.call(
1208
+ provider,
1209
+ serverUrl,
1210
+ authorizationServerUrl
1211
+ ));
1024
1212
  const metadata = await discoverAuthorizationServerMetadata(
1025
1213
  authorizationServerUrl,
1026
1214
  {
@@ -1028,7 +1216,59 @@ async function authInternal(provider, {
1028
1216
  trustedOrigin: trustedAuthorizationServerOrigin
1029
1217
  }
1030
1218
  );
1031
- let clientInformation = await Promise.resolve(provider.clientInformation());
1219
+ const currentAuthorizationServerInformation = createAuthorizationServerInformation(authorizationServerUrl, metadata);
1220
+ if (retryAuthorizationServerInformation) {
1221
+ assertAuthorizationServerInformationMatches({
1222
+ storedAuthorizationServerInformation: retryAuthorizationServerInformation,
1223
+ currentAuthorizationServerInformation
1224
+ });
1225
+ }
1226
+ const clientMetadata = provider.clientMetadata;
1227
+ if (authorizationCode === void 0) {
1228
+ clientInformation = await Promise.resolve(provider.clientInformation());
1229
+ }
1230
+ let tokens = authorizationCode === void 0 ? await provider.tokens() : void 0;
1231
+ if (clientInformation) {
1232
+ if (clientInformation.issuer != null && clientInformation.issuer !== currentAuthorizationServerInformation.issuer) {
1233
+ throw new MCPClientOAuthError({
1234
+ message: "OAuth authorization server does not match the issuer of the client credentials"
1235
+ });
1236
+ }
1237
+ const clientAuthorizationServerInformation = getAuthorizationServerInformationFromCredentials(clientInformation);
1238
+ if (clientAuthorizationServerInformation) {
1239
+ assertAuthorizationServerInformationMatches({
1240
+ storedAuthorizationServerInformation: clientAuthorizationServerInformation,
1241
+ currentAuthorizationServerInformation
1242
+ });
1243
+ }
1244
+ const storedAuthorizationServerInformation = (_c = callbackAuthorizationServerInformation != null ? callbackAuthorizationServerInformation : await getStoredAuthorizationServerInformation({
1245
+ provider,
1246
+ clientInformation,
1247
+ tokens
1248
+ })) != null ? _c : retryAuthorizationServerInformation;
1249
+ if (storedAuthorizationServerInformation) {
1250
+ assertAuthorizationServerInformationMatches({
1251
+ storedAuthorizationServerInformation,
1252
+ currentAuthorizationServerInformation
1253
+ });
1254
+ }
1255
+ if ((clientInformation.client_secret != null || provider.addClientAuthentication) && clientInformation.issuer == null && !storedAuthorizationServerInformation) {
1256
+ if (authorizationCode === void 0 && await ((_d = provider.isClientInformationDynamicallyRegistered) == null ? void 0 : _d.call(provider))) {
1257
+ if (!provider.invalidateCredentials) {
1258
+ throw new MCPClientOAuthError({
1259
+ message: "invalidateCredentials is required to discard unbound legacy OAuth credentials; clear stored credentials before authorizing again"
1260
+ });
1261
+ }
1262
+ await provider.invalidateCredentials("all");
1263
+ clientInformation = void 0;
1264
+ tokens = void 0;
1265
+ } else {
1266
+ throw new MCPClientOAuthError({
1267
+ message: "OAuth client credentials must be bound to an authorization server: configure their issuer or store authorization server information"
1268
+ });
1269
+ }
1270
+ }
1271
+ }
1032
1272
  if (!clientInformation) {
1033
1273
  if (authorizationCode !== void 0) {
1034
1274
  throw new Error(
@@ -1042,13 +1282,41 @@ async function authInternal(provider, {
1042
1282
  }
1043
1283
  const fullInformation = await registerClient(authorizationServerUrl, {
1044
1284
  metadata,
1045
- clientMetadata: provider.clientMetadata,
1285
+ clientMetadata,
1046
1286
  fetchFn
1047
1287
  });
1048
- await provider.saveClientInformation(fullInformation);
1049
- clientInformation = fullInformation;
1288
+ clientInformation = addAuthorizationServerInformationToClientInformation(
1289
+ fullInformation,
1290
+ currentAuthorizationServerInformation
1291
+ );
1292
+ await provider.saveClientInformation(clientInformation);
1050
1293
  }
1051
1294
  if (authorizationCode !== void 0) {
1295
+ if (provider.storedState) {
1296
+ const expectedState = await provider.storedState();
1297
+ if (expectedState !== void 0 && expectedState !== callbackState) {
1298
+ throw new Error(
1299
+ "OAuth state parameter mismatch - possible CSRF attack"
1300
+ );
1301
+ }
1302
+ }
1303
+ const storedAuthorizationServerInformation = callbackAuthorizationServerInformation != null ? callbackAuthorizationServerInformation : await getStoredAuthorizationServerInformation({
1304
+ provider,
1305
+ clientInformation
1306
+ });
1307
+ if (!storedAuthorizationServerInformation) {
1308
+ throw new MCPClientOAuthError({
1309
+ message: "Stored OAuth authorization server metadata is required when exchanging an authorization code"
1310
+ });
1311
+ }
1312
+ validateAuthorizationResponseIssuer({
1313
+ callbackIssuer,
1314
+ expectedIssuer: (_f = (_e = storedAuthorizationServerInformation.issuer) != null ? _e : metadata == null ? void 0 : metadata.issuer) != null ? _f : String(authorizationServerUrl)
1315
+ });
1316
+ assertAuthorizationServerInformationMatches({
1317
+ storedAuthorizationServerInformation,
1318
+ currentAuthorizationServerInformation
1319
+ });
1052
1320
  const codeVerifier2 = await provider.codeVerifier();
1053
1321
  const tokens2 = await exchangeAuthorization(authorizationServerUrl, {
1054
1322
  metadata,
@@ -1060,22 +1328,56 @@ async function authInternal(provider, {
1060
1328
  addClientAuthentication: provider.addClientAuthentication,
1061
1329
  fetchFn
1062
1330
  });
1063
- await provider.saveTokens(tokens2);
1331
+ await provider.saveTokens(
1332
+ addAuthorizationServerInformationToTokens(
1333
+ tokens2,
1334
+ currentAuthorizationServerInformation
1335
+ )
1336
+ );
1064
1337
  return "AUTHORIZED";
1065
1338
  }
1066
- const tokens = await provider.tokens();
1067
1339
  if (tokens == null ? void 0 : tokens.refresh_token) {
1068
- try {
1069
- const newTokens = await refreshAuthorization(authorizationServerUrl, {
1070
- metadata,
1071
- clientInformation,
1072
- refreshToken: tokens.refresh_token,
1073
- resource,
1074
- addClientAuthentication: provider.addClientAuthentication,
1075
- fetchFn
1340
+ const storedAuthorizationServerInformation = await getStoredAuthorizationServerInformation({
1341
+ provider,
1342
+ clientInformation,
1343
+ tokens
1344
+ });
1345
+ if (storedAuthorizationServerInformation) {
1346
+ assertAuthorizationServerInformationMatches({
1347
+ storedAuthorizationServerInformation,
1348
+ currentAuthorizationServerInformation
1076
1349
  });
1077
- await provider.saveTokens(newTokens);
1078
- return "AUTHORIZED";
1350
+ } else {
1351
+ if (!provider.invalidateCredentials) {
1352
+ throw new MCPClientOAuthError({
1353
+ message: "invalidateCredentials is required to discard unbound legacy OAuth tokens; clear stored tokens before authorizing again"
1354
+ });
1355
+ }
1356
+ await provider.invalidateCredentials("tokens", {
1357
+ tokens: { ...tokens }
1358
+ });
1359
+ }
1360
+ try {
1361
+ if (storedAuthorizationServerInformation) {
1362
+ if (refreshAttempt) {
1363
+ refreshAttempt.tokens = { ...tokens };
1364
+ }
1365
+ const newTokens = await refreshAuthorization(authorizationServerUrl, {
1366
+ metadata,
1367
+ clientInformation,
1368
+ refreshToken: tokens.refresh_token,
1369
+ resource,
1370
+ addClientAuthentication: provider.addClientAuthentication,
1371
+ fetchFn
1372
+ });
1373
+ await provider.saveTokens(
1374
+ addAuthorizationServerInformationToTokens(
1375
+ newTokens,
1376
+ currentAuthorizationServerInformation
1377
+ )
1378
+ );
1379
+ return "AUTHORIZED";
1380
+ }
1079
1381
  } catch (error) {
1080
1382
  if (
1081
1383
  // 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.
@@ -1087,6 +1389,9 @@ async function authInternal(provider, {
1087
1389
  }
1088
1390
  }
1089
1391
  const state = provider.state ? await provider.state() : void 0;
1392
+ if (state && provider.saveState) {
1393
+ await provider.saveState(state);
1394
+ }
1090
1395
  const { authorizationUrl, codeVerifier } = await startAuthorization(
1091
1396
  authorizationServerUrl,
1092
1397
  {
@@ -1094,10 +1399,20 @@ async function authInternal(provider, {
1094
1399
  clientInformation,
1095
1400
  state,
1096
1401
  redirectUrl: provider.redirectUrl,
1097
- scope: scope || provider.clientMetadata.scope,
1402
+ scope: scope || clientMetadata.scope,
1098
1403
  resource
1099
1404
  }
1100
1405
  );
1406
+ const savedAuthorizationServerInformation = await saveAuthorizationServerInformation({
1407
+ provider,
1408
+ clientInformation,
1409
+ authorizationServerInformation: currentAuthorizationServerInformation
1410
+ });
1411
+ if (!savedAuthorizationServerInformation) {
1412
+ throw new MCPClientOAuthError({
1413
+ message: "OAuth authorization server metadata must be saveable before starting authorization"
1414
+ });
1415
+ }
1101
1416
  await provider.saveCodeVerifier(codeVerifier);
1102
1417
  await provider.redirectToAuthorization(authorizationUrl);
1103
1418
  return "REDIRECT";