@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.js CHANGED
@@ -301,15 +301,6 @@ var import_pkce_challenge = __toESM(require("pkce-challenge"));
301
301
 
302
302
  // src/tool/oauth-types.ts
303
303
  var import_v43 = require("zod/v4");
304
- var OAuthTokensSchema = import_v43.z.object({
305
- access_token: import_v43.z.string(),
306
- id_token: import_v43.z.string().optional(),
307
- // Optional for OAuth 2.1, but necessary in OpenID Connect
308
- token_type: import_v43.z.string(),
309
- expires_in: import_v43.z.number().optional(),
310
- scope: import_v43.z.string().optional(),
311
- refresh_token: import_v43.z.string().optional()
312
- }).strip();
313
304
  var SafeUrlSchema = import_v43.z.string().url().superRefine((val, ctx) => {
314
305
  if (!URL.canParse(val)) {
315
306
  ctx.addIssue({
@@ -326,6 +317,18 @@ var SafeUrlSchema = import_v43.z.string().url().superRefine((val, ctx) => {
326
317
  },
327
318
  { message: "URL cannot use javascript:, data:, or vbscript: scheme" }
328
319
  );
320
+ var OAuthTokensSchema = import_v43.z.object({
321
+ access_token: import_v43.z.string(),
322
+ id_token: import_v43.z.string().optional(),
323
+ // Optional for OAuth 2.1, but necessary in OpenID Connect
324
+ token_type: import_v43.z.string(),
325
+ expires_in: import_v43.z.number().optional(),
326
+ scope: import_v43.z.string().optional(),
327
+ refresh_token: import_v43.z.string().optional(),
328
+ issuer: SafeUrlSchema.optional(),
329
+ authorization_server: SafeUrlSchema.optional(),
330
+ token_endpoint: SafeUrlSchema.optional()
331
+ }).strip();
329
332
  var OAuthProtectedResourceMetadataSchema = import_v43.z.object({
330
333
  resource: import_v43.z.string().url(),
331
334
  authorization_servers: import_v43.z.array(SafeUrlSchema).optional(),
@@ -378,7 +381,10 @@ var OAuthClientInformationSchema = import_v43.z.object({
378
381
  client_id: import_v43.z.string(),
379
382
  client_secret: import_v43.z.string().optional(),
380
383
  client_id_issued_at: import_v43.z.number().optional(),
381
- client_secret_expires_at: import_v43.z.number().optional()
384
+ client_secret_expires_at: import_v43.z.number().optional(),
385
+ issuer: SafeUrlSchema.optional(),
386
+ authorization_server: SafeUrlSchema.optional(),
387
+ token_endpoint: SafeUrlSchema.optional()
382
388
  }).strip();
383
389
  var OAuthClientMetadataSchema = import_v43.z.object({
384
390
  redirect_uris: import_v43.z.array(SafeUrlSchema),
@@ -476,6 +482,130 @@ var UnauthorizedError = class extends Error {
476
482
  this.name = "UnauthorizedError";
477
483
  }
478
484
  };
485
+ function normalizeUrl(url) {
486
+ return new URL(url).href;
487
+ }
488
+ function validateAuthorizationResponseIssuer({
489
+ callbackIssuer,
490
+ expectedIssuer
491
+ }) {
492
+ if (callbackIssuer != null && callbackIssuer !== expectedIssuer) {
493
+ throw new MCPClientOAuthError({
494
+ message: `OAuth authorization response issuer ${callbackIssuer} does not match expected issuer ${expectedIssuer}`
495
+ });
496
+ }
497
+ }
498
+ function createAuthorizationServerInformation(authorizationServerUrl, metadata) {
499
+ var _a3;
500
+ return {
501
+ issuer: (_a3 = metadata == null ? void 0 : metadata.issuer) != null ? _a3 : String(authorizationServerUrl),
502
+ authorizationServerUrl: normalizeUrl(authorizationServerUrl),
503
+ tokenEndpoint: normalizeUrl(
504
+ (metadata == null ? void 0 : metadata.token_endpoint) ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl)
505
+ )
506
+ };
507
+ }
508
+ function addAuthorizationServerInformationToTokens(tokens, authorizationServerInformation) {
509
+ return {
510
+ ...tokens,
511
+ issuer: authorizationServerInformation.issuer,
512
+ authorization_server: authorizationServerInformation.authorizationServerUrl,
513
+ token_endpoint: authorizationServerInformation.tokenEndpoint
514
+ };
515
+ }
516
+ function addAuthorizationServerInformationToClientInformation(clientInformation, authorizationServerInformation) {
517
+ return {
518
+ ...clientInformation,
519
+ issuer: authorizationServerInformation.issuer,
520
+ authorization_server: authorizationServerInformation.authorizationServerUrl,
521
+ token_endpoint: authorizationServerInformation.tokenEndpoint
522
+ };
523
+ }
524
+ function getAuthorizationServerInformationFromCredentials(credentials) {
525
+ if (!(credentials == null ? void 0 : credentials.authorization_server) || !credentials.token_endpoint) {
526
+ return void 0;
527
+ }
528
+ return {
529
+ issuer: credentials.issuer,
530
+ authorizationServerUrl: normalizeUrl(credentials.authorization_server),
531
+ tokenEndpoint: normalizeUrl(credentials.token_endpoint)
532
+ };
533
+ }
534
+ async function getStoredAuthorizationServerInformation({
535
+ provider,
536
+ clientInformation,
537
+ tokens
538
+ }) {
539
+ var _a3;
540
+ const tokenAuthorizationServerInformation = getAuthorizationServerInformationFromCredentials(tokens);
541
+ if (tokenAuthorizationServerInformation) {
542
+ return tokenAuthorizationServerInformation;
543
+ }
544
+ const providerAuthorizationServerInformation = await ((_a3 = provider.authorizationServerInformation) == null ? void 0 : _a3.call(provider));
545
+ if (providerAuthorizationServerInformation) {
546
+ return {
547
+ issuer: providerAuthorizationServerInformation.issuer,
548
+ authorizationServerUrl: normalizeUrl(
549
+ providerAuthorizationServerInformation.authorizationServerUrl
550
+ ),
551
+ tokenEndpoint: normalizeUrl(
552
+ providerAuthorizationServerInformation.tokenEndpoint
553
+ )
554
+ };
555
+ }
556
+ return getAuthorizationServerInformationFromCredentials(clientInformation);
557
+ }
558
+ async function saveAuthorizationServerInformation({
559
+ provider,
560
+ clientInformation,
561
+ authorizationServerInformation
562
+ }) {
563
+ if (provider.saveAuthorizationServerInformation) {
564
+ await provider.saveAuthorizationServerInformation(
565
+ authorizationServerInformation
566
+ );
567
+ return true;
568
+ }
569
+ if (provider.saveClientInformation) {
570
+ await provider.saveClientInformation(
571
+ addAuthorizationServerInformationToClientInformation(
572
+ clientInformation,
573
+ authorizationServerInformation
574
+ )
575
+ );
576
+ return true;
577
+ }
578
+ const configuredBinding = getAuthorizationServerInformationFromCredentials(clientInformation);
579
+ if (configuredBinding) {
580
+ assertAuthorizationServerInformationMatches({
581
+ storedAuthorizationServerInformation: configuredBinding,
582
+ currentAuthorizationServerInformation: authorizationServerInformation
583
+ });
584
+ return true;
585
+ }
586
+ return false;
587
+ }
588
+ function assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl) {
589
+ if (!resourceMetadataUrl) {
590
+ return;
591
+ }
592
+ const expectedOrigin = new URL(serverUrl).origin;
593
+ if (resourceMetadataUrl.origin !== expectedOrigin) {
594
+ throw new MCPClientOAuthError({
595
+ message: `OAuth protected resource metadata URL ${resourceMetadataUrl.href} must have the same origin as the MCP server URL ${expectedOrigin}`
596
+ });
597
+ }
598
+ }
599
+ function assertAuthorizationServerInformationMatches({
600
+ storedAuthorizationServerInformation,
601
+ currentAuthorizationServerInformation
602
+ }) {
603
+ if (storedAuthorizationServerInformation.issuer != null && currentAuthorizationServerInformation.issuer != null && storedAuthorizationServerInformation.issuer !== currentAuthorizationServerInformation.issuer || storedAuthorizationServerInformation.authorizationServerUrl !== currentAuthorizationServerInformation.authorizationServerUrl || storedAuthorizationServerInformation.tokenEndpoint !== currentAuthorizationServerInformation.tokenEndpoint) {
604
+ throw new MCPClientOAuthError({
605
+ message: "OAuth authorization server metadata does not match the metadata that issued the stored credentials"
606
+ });
607
+ }
608
+ }
479
609
  function extractResourceMetadataUrl(response) {
480
610
  var _a3;
481
611
  const header = (_a3 = response.headers.get("www-authenticate")) != null ? _a3 : response.headers.get("WWW-Authenticate");
@@ -618,15 +748,18 @@ async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn =
618
748
  function buildDiscoveryUrls(authorizationServerUrl) {
619
749
  const url = typeof authorizationServerUrl === "string" ? new URL(authorizationServerUrl) : authorizationServerUrl;
620
750
  const hasPath = url.pathname !== "/";
751
+ const rootIssuer = url.origin;
621
752
  const urlsToTry = [];
622
753
  if (!hasPath) {
623
754
  urlsToTry.push({
624
755
  url: new URL("/.well-known/oauth-authorization-server", url.origin),
625
- type: "oauth"
756
+ type: "oauth",
757
+ expectedIssuer: rootIssuer
626
758
  });
627
759
  urlsToTry.push({
628
760
  url: new URL("/.well-known/openid-configuration", url.origin),
629
- type: "oidc"
761
+ type: "oidc",
762
+ expectedIssuer: rootIssuer
630
763
  });
631
764
  return urlsToTry;
632
765
  }
@@ -634,27 +767,42 @@ function buildDiscoveryUrls(authorizationServerUrl) {
634
767
  if (pathname.endsWith("/")) {
635
768
  pathname = pathname.slice(0, -1);
636
769
  }
770
+ const pathIssuer = `${url.origin}${pathname}`;
637
771
  urlsToTry.push({
638
772
  url: new URL(
639
773
  `/.well-known/oauth-authorization-server${pathname}`,
640
774
  url.origin
641
775
  ),
642
- type: "oauth"
776
+ type: "oauth",
777
+ expectedIssuer: pathIssuer
643
778
  });
644
779
  urlsToTry.push({
645
780
  url: new URL("/.well-known/oauth-authorization-server", url.origin),
646
- type: "oauth"
781
+ type: "oauth",
782
+ expectedIssuer: rootIssuer
647
783
  });
648
784
  urlsToTry.push({
649
785
  url: new URL(`/.well-known/openid-configuration${pathname}`, url.origin),
650
- type: "oidc"
786
+ type: "oidc",
787
+ expectedIssuer: pathIssuer
651
788
  });
789
+ const oidcDiscoveryUrl = new URL(url.origin);
790
+ oidcDiscoveryUrl.pathname = `${pathname}/.well-known/openid-configuration`;
652
791
  urlsToTry.push({
653
- url: new URL(`${pathname}/.well-known/openid-configuration`, url.origin),
654
- type: "oidc"
792
+ url: oidcDiscoveryUrl,
793
+ type: "oidc",
794
+ expectedIssuer: pathIssuer
655
795
  });
656
796
  return urlsToTry;
657
797
  }
798
+ function assertMetadataIssuerMatches(metadata, expectedIssuer) {
799
+ const issuerMatches = metadata.issuer === expectedIssuer || expectedIssuer === new URL(expectedIssuer).origin && metadata.issuer === `${expectedIssuer}/`;
800
+ if (!issuerMatches) {
801
+ throw new MCPClientOAuthError({
802
+ message: `OAuth authorization server metadata issuer ${metadata.issuer} does not match expected issuer ${expectedIssuer}`
803
+ });
804
+ }
805
+ }
658
806
  async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
659
807
  fetchFn = fetch,
660
808
  protocolVersion = LATEST_PROTOCOL_VERSION,
@@ -662,8 +810,14 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
662
810
  } = {}) {
663
811
  var _a3;
664
812
  const headers = { "MCP-Protocol-Version": protocolVersion };
813
+ const authorizationServerOrigin = new URL(authorizationServerUrl).origin;
665
814
  const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
666
- for (const { url: endpointUrl, type } of urlsToTry) {
815
+ if (urlsToTry.some(({ url }) => url.origin !== authorizationServerOrigin)) {
816
+ throw new MCPClientOAuthError({
817
+ message: "OAuth discovery URL changed authorization-server origin"
818
+ });
819
+ }
820
+ for (const { url: endpointUrl, type, expectedIssuer } of urlsToTry) {
667
821
  const response = await fetchWithCorsRetry(
668
822
  endpointUrl,
669
823
  headers,
@@ -682,11 +836,14 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
682
836
  );
683
837
  }
684
838
  if (type === "oauth") {
685
- return OAuthMetadataSchema.parse(await response.json());
839
+ const metadata = OAuthMetadataSchema.parse(await response.json());
840
+ assertMetadataIssuerMatches(metadata, expectedIssuer);
841
+ return metadata;
686
842
  } else {
687
843
  const metadata = OpenIdProviderDiscoveryMetadataSchema.parse(
688
844
  await response.json()
689
845
  );
846
+ assertMetadataIssuerMatches(metadata, expectedIssuer);
690
847
  if (!((_a3 = metadata.code_challenge_methods_supported) == null ? void 0 : _a3.includes("S256"))) {
691
848
  throw new Error(
692
849
  `Incompatible OIDC provider at ${endpointUrl}: does not support S256 code challenge method required by MCP specification`
@@ -695,7 +852,6 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, {
695
852
  return metadata;
696
853
  }
697
854
  }
698
- return void 0;
699
855
  }
700
856
  async function startAuthorization(authorizationServerUrl, {
701
857
  metadata,
@@ -977,16 +1133,28 @@ async function registerClient(authorizationServerUrl, {
977
1133
  return OAuthClientInformationFullSchema.parse(await response.json());
978
1134
  }
979
1135
  async function auth(provider, options) {
980
- var _a3, _b3;
1136
+ var _a3, _b3, _c;
1137
+ const refreshAttempt = {};
981
1138
  try {
982
- return await authInternal(provider, options);
1139
+ return await authInternal(provider, options, refreshAttempt);
983
1140
  } catch (error) {
984
1141
  if (error instanceof InvalidClientError || error instanceof UnauthorizedClientError) {
985
- await ((_a3 = provider.invalidateCredentials) == null ? void 0 : _a3.call(provider, "all"));
986
- return await authInternal(provider, options);
987
- } else if (error instanceof InvalidGrantError) {
988
- await ((_b3 = provider.invalidateCredentials) == null ? void 0 : _b3.call(provider, "tokens"));
1142
+ if (options.authorizationCode !== void 0 || !await ((_a3 = provider.isClientInformationDynamicallyRegistered) == null ? void 0 : _a3.call(provider))) {
1143
+ throw error;
1144
+ }
1145
+ await ((_b3 = provider.invalidateCredentials) == null ? void 0 : _b3.call(provider, "all"));
989
1146
  return await authInternal(provider, options);
1147
+ } else if (error instanceof InvalidGrantError && refreshAttempt.tokens) {
1148
+ const retryAuthorizationServerInformation = getAuthorizationServerInformationFromCredentials(refreshAttempt.tokens);
1149
+ await ((_c = provider.invalidateCredentials) == null ? void 0 : _c.call(provider, "tokens", {
1150
+ tokens: refreshAttempt.tokens
1151
+ }));
1152
+ return await authInternal(
1153
+ provider,
1154
+ options,
1155
+ void 0,
1156
+ retryAuthorizationServerInformation
1157
+ );
990
1158
  }
991
1159
  throw error;
992
1160
  }
@@ -1015,12 +1183,18 @@ async function selectResourceURL(serverUrl, provider, resourceMetadata) {
1015
1183
  async function authInternal(provider, {
1016
1184
  serverUrl,
1017
1185
  authorizationCode,
1186
+ callbackState,
1187
+ callbackIssuer,
1018
1188
  scope,
1019
1189
  resourceMetadataUrl,
1020
1190
  fetchFn
1021
- }) {
1191
+ }, refreshAttempt, retryAuthorizationServerInformation) {
1192
+ var _a3, _b3, _c, _d, _e, _f;
1022
1193
  let resourceMetadata;
1023
1194
  let authorizationServerUrl;
1195
+ let clientInformation;
1196
+ let callbackAuthorizationServerInformation;
1197
+ assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl);
1024
1198
  try {
1025
1199
  resourceMetadata = await discoverOAuthProtectedResourceMetadata(
1026
1200
  serverUrl,
@@ -1032,8 +1206,17 @@ async function authInternal(provider, {
1032
1206
  }
1033
1207
  } catch (e) {
1034
1208
  }
1209
+ if (authorizationCode !== void 0) {
1210
+ clientInformation = await Promise.resolve(provider.clientInformation());
1211
+ if (clientInformation) {
1212
+ callbackAuthorizationServerInformation = await getStoredAuthorizationServerInformation({
1213
+ provider,
1214
+ clientInformation
1215
+ });
1216
+ }
1217
+ }
1035
1218
  if (!authorizationServerUrl) {
1036
- authorizationServerUrl = serverUrl;
1219
+ authorizationServerUrl = (_a3 = callbackAuthorizationServerInformation == null ? void 0 : callbackAuthorizationServerInformation.authorizationServerUrl) != null ? _a3 : serverUrl;
1037
1220
  }
1038
1221
  const parsedServerUrl = new URL(serverUrl);
1039
1222
  const parsedAuthorizationServerUrl = new URL(authorizationServerUrl);
@@ -1048,6 +1231,11 @@ async function authInternal(provider, {
1048
1231
  provider,
1049
1232
  resourceMetadata
1050
1233
  );
1234
+ await ((_b3 = provider.validateAuthorizationServerURL) == null ? void 0 : _b3.call(
1235
+ provider,
1236
+ serverUrl,
1237
+ authorizationServerUrl
1238
+ ));
1051
1239
  const metadata = await discoverAuthorizationServerMetadata(
1052
1240
  authorizationServerUrl,
1053
1241
  {
@@ -1055,7 +1243,59 @@ async function authInternal(provider, {
1055
1243
  trustedOrigin: trustedAuthorizationServerOrigin
1056
1244
  }
1057
1245
  );
1058
- let clientInformation = await Promise.resolve(provider.clientInformation());
1246
+ const currentAuthorizationServerInformation = createAuthorizationServerInformation(authorizationServerUrl, metadata);
1247
+ if (retryAuthorizationServerInformation) {
1248
+ assertAuthorizationServerInformationMatches({
1249
+ storedAuthorizationServerInformation: retryAuthorizationServerInformation,
1250
+ currentAuthorizationServerInformation
1251
+ });
1252
+ }
1253
+ const clientMetadata = provider.clientMetadata;
1254
+ if (authorizationCode === void 0) {
1255
+ clientInformation = await Promise.resolve(provider.clientInformation());
1256
+ }
1257
+ let tokens = authorizationCode === void 0 ? await provider.tokens() : void 0;
1258
+ if (clientInformation) {
1259
+ if (clientInformation.issuer != null && clientInformation.issuer !== currentAuthorizationServerInformation.issuer) {
1260
+ throw new MCPClientOAuthError({
1261
+ message: "OAuth authorization server does not match the issuer of the client credentials"
1262
+ });
1263
+ }
1264
+ const clientAuthorizationServerInformation = getAuthorizationServerInformationFromCredentials(clientInformation);
1265
+ if (clientAuthorizationServerInformation) {
1266
+ assertAuthorizationServerInformationMatches({
1267
+ storedAuthorizationServerInformation: clientAuthorizationServerInformation,
1268
+ currentAuthorizationServerInformation
1269
+ });
1270
+ }
1271
+ const storedAuthorizationServerInformation = (_c = callbackAuthorizationServerInformation != null ? callbackAuthorizationServerInformation : await getStoredAuthorizationServerInformation({
1272
+ provider,
1273
+ clientInformation,
1274
+ tokens
1275
+ })) != null ? _c : retryAuthorizationServerInformation;
1276
+ if (storedAuthorizationServerInformation) {
1277
+ assertAuthorizationServerInformationMatches({
1278
+ storedAuthorizationServerInformation,
1279
+ currentAuthorizationServerInformation
1280
+ });
1281
+ }
1282
+ if ((clientInformation.client_secret != null || provider.addClientAuthentication) && clientInformation.issuer == null && !storedAuthorizationServerInformation) {
1283
+ if (authorizationCode === void 0 && await ((_d = provider.isClientInformationDynamicallyRegistered) == null ? void 0 : _d.call(provider))) {
1284
+ if (!provider.invalidateCredentials) {
1285
+ throw new MCPClientOAuthError({
1286
+ message: "invalidateCredentials is required to discard unbound legacy OAuth credentials; clear stored credentials before authorizing again"
1287
+ });
1288
+ }
1289
+ await provider.invalidateCredentials("all");
1290
+ clientInformation = void 0;
1291
+ tokens = void 0;
1292
+ } else {
1293
+ throw new MCPClientOAuthError({
1294
+ message: "OAuth client credentials must be bound to an authorization server: configure their issuer or store authorization server information"
1295
+ });
1296
+ }
1297
+ }
1298
+ }
1059
1299
  if (!clientInformation) {
1060
1300
  if (authorizationCode !== void 0) {
1061
1301
  throw new Error(
@@ -1069,13 +1309,41 @@ async function authInternal(provider, {
1069
1309
  }
1070
1310
  const fullInformation = await registerClient(authorizationServerUrl, {
1071
1311
  metadata,
1072
- clientMetadata: provider.clientMetadata,
1312
+ clientMetadata,
1073
1313
  fetchFn
1074
1314
  });
1075
- await provider.saveClientInformation(fullInformation);
1076
- clientInformation = fullInformation;
1315
+ clientInformation = addAuthorizationServerInformationToClientInformation(
1316
+ fullInformation,
1317
+ currentAuthorizationServerInformation
1318
+ );
1319
+ await provider.saveClientInformation(clientInformation);
1077
1320
  }
1078
1321
  if (authorizationCode !== void 0) {
1322
+ if (provider.storedState) {
1323
+ const expectedState = await provider.storedState();
1324
+ if (expectedState !== void 0 && expectedState !== callbackState) {
1325
+ throw new Error(
1326
+ "OAuth state parameter mismatch - possible CSRF attack"
1327
+ );
1328
+ }
1329
+ }
1330
+ const storedAuthorizationServerInformation = callbackAuthorizationServerInformation != null ? callbackAuthorizationServerInformation : await getStoredAuthorizationServerInformation({
1331
+ provider,
1332
+ clientInformation
1333
+ });
1334
+ if (!storedAuthorizationServerInformation) {
1335
+ throw new MCPClientOAuthError({
1336
+ message: "Stored OAuth authorization server metadata is required when exchanging an authorization code"
1337
+ });
1338
+ }
1339
+ validateAuthorizationResponseIssuer({
1340
+ callbackIssuer,
1341
+ expectedIssuer: (_f = (_e = storedAuthorizationServerInformation.issuer) != null ? _e : metadata == null ? void 0 : metadata.issuer) != null ? _f : String(authorizationServerUrl)
1342
+ });
1343
+ assertAuthorizationServerInformationMatches({
1344
+ storedAuthorizationServerInformation,
1345
+ currentAuthorizationServerInformation
1346
+ });
1079
1347
  const codeVerifier2 = await provider.codeVerifier();
1080
1348
  const tokens2 = await exchangeAuthorization(authorizationServerUrl, {
1081
1349
  metadata,
@@ -1087,22 +1355,56 @@ async function authInternal(provider, {
1087
1355
  addClientAuthentication: provider.addClientAuthentication,
1088
1356
  fetchFn
1089
1357
  });
1090
- await provider.saveTokens(tokens2);
1358
+ await provider.saveTokens(
1359
+ addAuthorizationServerInformationToTokens(
1360
+ tokens2,
1361
+ currentAuthorizationServerInformation
1362
+ )
1363
+ );
1091
1364
  return "AUTHORIZED";
1092
1365
  }
1093
- const tokens = await provider.tokens();
1094
1366
  if (tokens == null ? void 0 : tokens.refresh_token) {
1095
- try {
1096
- const newTokens = await refreshAuthorization(authorizationServerUrl, {
1097
- metadata,
1098
- clientInformation,
1099
- refreshToken: tokens.refresh_token,
1100
- resource,
1101
- addClientAuthentication: provider.addClientAuthentication,
1102
- fetchFn
1367
+ const storedAuthorizationServerInformation = await getStoredAuthorizationServerInformation({
1368
+ provider,
1369
+ clientInformation,
1370
+ tokens
1371
+ });
1372
+ if (storedAuthorizationServerInformation) {
1373
+ assertAuthorizationServerInformationMatches({
1374
+ storedAuthorizationServerInformation,
1375
+ currentAuthorizationServerInformation
1103
1376
  });
1104
- await provider.saveTokens(newTokens);
1105
- return "AUTHORIZED";
1377
+ } else {
1378
+ if (!provider.invalidateCredentials) {
1379
+ throw new MCPClientOAuthError({
1380
+ message: "invalidateCredentials is required to discard unbound legacy OAuth tokens; clear stored tokens before authorizing again"
1381
+ });
1382
+ }
1383
+ await provider.invalidateCredentials("tokens", {
1384
+ tokens: { ...tokens }
1385
+ });
1386
+ }
1387
+ try {
1388
+ if (storedAuthorizationServerInformation) {
1389
+ if (refreshAttempt) {
1390
+ refreshAttempt.tokens = { ...tokens };
1391
+ }
1392
+ const newTokens = await refreshAuthorization(authorizationServerUrl, {
1393
+ metadata,
1394
+ clientInformation,
1395
+ refreshToken: tokens.refresh_token,
1396
+ resource,
1397
+ addClientAuthentication: provider.addClientAuthentication,
1398
+ fetchFn
1399
+ });
1400
+ await provider.saveTokens(
1401
+ addAuthorizationServerInformationToTokens(
1402
+ newTokens,
1403
+ currentAuthorizationServerInformation
1404
+ )
1405
+ );
1406
+ return "AUTHORIZED";
1407
+ }
1106
1408
  } catch (error) {
1107
1409
  if (
1108
1410
  // 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.
@@ -1114,6 +1416,9 @@ async function authInternal(provider, {
1114
1416
  }
1115
1417
  }
1116
1418
  const state = provider.state ? await provider.state() : void 0;
1419
+ if (state && provider.saveState) {
1420
+ await provider.saveState(state);
1421
+ }
1117
1422
  const { authorizationUrl, codeVerifier } = await startAuthorization(
1118
1423
  authorizationServerUrl,
1119
1424
  {
@@ -1121,10 +1426,20 @@ async function authInternal(provider, {
1121
1426
  clientInformation,
1122
1427
  state,
1123
1428
  redirectUrl: provider.redirectUrl,
1124
- scope: scope || provider.clientMetadata.scope,
1429
+ scope: scope || clientMetadata.scope,
1125
1430
  resource
1126
1431
  }
1127
1432
  );
1433
+ const savedAuthorizationServerInformation = await saveAuthorizationServerInformation({
1434
+ provider,
1435
+ clientInformation,
1436
+ authorizationServerInformation: currentAuthorizationServerInformation
1437
+ });
1438
+ if (!savedAuthorizationServerInformation) {
1439
+ throw new MCPClientOAuthError({
1440
+ message: "OAuth authorization server metadata must be saveable before starting authorization"
1441
+ });
1442
+ }
1128
1443
  await provider.saveCodeVerifier(codeVerifier);
1129
1444
  await provider.redirectToAuthorization(authorizationUrl);
1130
1445
  return "REDIRECT";