@ai-sdk/mcp 1.0.90 → 1.0.92
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/CHANGELOG.md +14 -0
- package/dist/index.d.mts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +22 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/tool/oauth.ts +34 -5
package/package.json
CHANGED
package/src/tool/oauth.ts
CHANGED
|
@@ -80,9 +80,15 @@ export interface OAuthClientProvider {
|
|
|
80
80
|
* If implemented, provides a way for the client to invalidate (e.g. delete) the specified
|
|
81
81
|
* credentials, in the case where the server has indicated that they are no longer valid.
|
|
82
82
|
* This avoids requiring the user to intervene manually.
|
|
83
|
+
*
|
|
84
|
+
* For token invalidation, context identifies the token generation being invalidated
|
|
85
|
+
* when available. Providers sharing storage must atomically compare and delete
|
|
86
|
+
* that generation so a concurrent refresh's tokens are preserved. Omitting or
|
|
87
|
+
* ignoring context retains unconditional invalidation.
|
|
83
88
|
*/
|
|
84
89
|
invalidateCredentials?(
|
|
85
90
|
scope: 'all' | 'client' | 'tokens' | 'verifier',
|
|
91
|
+
context?: { tokens: OAuthTokens },
|
|
86
92
|
): void | Promise<void>;
|
|
87
93
|
get redirectUrl(): string | URL;
|
|
88
94
|
get clientMetadata(): OAuthClientMetadata;
|
|
@@ -625,8 +631,12 @@ export function buildDiscoveryUrls(
|
|
|
625
631
|
expectedIssuer: pathIssuer,
|
|
626
632
|
});
|
|
627
633
|
|
|
634
|
+
// Assign the path rather than resolving it as a URL reference: a pathname
|
|
635
|
+
// beginning with // would otherwise replace the authorization server host.
|
|
636
|
+
const oidcDiscoveryUrl = new URL(url.origin);
|
|
637
|
+
oidcDiscoveryUrl.pathname = `${pathname}/.well-known/openid-configuration`;
|
|
628
638
|
urlsToTry.push({
|
|
629
|
-
url:
|
|
639
|
+
url: oidcDiscoveryUrl,
|
|
630
640
|
type: 'oidc',
|
|
631
641
|
expectedIssuer: pathIssuer,
|
|
632
642
|
});
|
|
@@ -664,8 +674,17 @@ export async function discoverAuthorizationServerMetadata(
|
|
|
664
674
|
): Promise<AuthorizationServerMetadata | undefined> {
|
|
665
675
|
const headers = { 'MCP-Protocol-Version': protocolVersion };
|
|
666
676
|
|
|
677
|
+
const authorizationServerOrigin = new URL(authorizationServerUrl).origin;
|
|
667
678
|
const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
|
|
668
679
|
|
|
680
|
+
// Check all candidates before any request so discovery cannot bypass an
|
|
681
|
+
// application's authorization server validation by changing the origin.
|
|
682
|
+
if (urlsToTry.some(({ url }) => url.origin !== authorizationServerOrigin)) {
|
|
683
|
+
throw new MCPClientOAuthError({
|
|
684
|
+
message: 'OAuth discovery URL changed authorization-server origin',
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
669
688
|
for (const { url: endpointUrl, type, expectedIssuer } of urlsToTry) {
|
|
670
689
|
const response = await fetchWithCorsRetry(
|
|
671
690
|
endpointUrl,
|
|
@@ -1214,8 +1233,9 @@ export async function auth(
|
|
|
1214
1233
|
fetchFn?: FetchFunction;
|
|
1215
1234
|
},
|
|
1216
1235
|
): Promise<AuthResult> {
|
|
1236
|
+
const refreshAttempt: { tokens?: OAuthTokens } = {};
|
|
1217
1237
|
try {
|
|
1218
|
-
return await authInternal(provider, options);
|
|
1238
|
+
return await authInternal(provider, options, refreshAttempt);
|
|
1219
1239
|
} catch (error) {
|
|
1220
1240
|
if (
|
|
1221
1241
|
error instanceof InvalidClientError ||
|
|
@@ -1230,8 +1250,11 @@ export async function auth(
|
|
|
1230
1250
|
|
|
1231
1251
|
await provider.invalidateCredentials?.('all');
|
|
1232
1252
|
return await authInternal(provider, options);
|
|
1233
|
-
} else if (error instanceof InvalidGrantError) {
|
|
1234
|
-
|
|
1253
|
+
} else if (error instanceof InvalidGrantError && refreshAttempt.tokens) {
|
|
1254
|
+
// Only invalidate the tokens used by a failed refresh.
|
|
1255
|
+
await provider.invalidateCredentials?.('tokens', {
|
|
1256
|
+
tokens: refreshAttempt.tokens,
|
|
1257
|
+
});
|
|
1235
1258
|
return await authInternal(provider, options);
|
|
1236
1259
|
}
|
|
1237
1260
|
|
|
@@ -1287,6 +1310,7 @@ async function authInternal(
|
|
|
1287
1310
|
resourceMetadataUrl?: URL;
|
|
1288
1311
|
fetchFn?: FetchFunction;
|
|
1289
1312
|
},
|
|
1313
|
+
refreshAttempt?: { tokens?: OAuthTokens },
|
|
1290
1314
|
): Promise<AuthResult> {
|
|
1291
1315
|
let resourceMetadata: OAuthProtectedResourceMetadata | undefined;
|
|
1292
1316
|
let authorizationServerUrl: string | URL | undefined;
|
|
@@ -1483,11 +1507,16 @@ async function authInternal(
|
|
|
1483
1507
|
currentAuthorizationServerInformation,
|
|
1484
1508
|
});
|
|
1485
1509
|
} else {
|
|
1486
|
-
await provider.invalidateCredentials?.('tokens'
|
|
1510
|
+
await provider.invalidateCredentials?.('tokens', {
|
|
1511
|
+
tokens: { ...tokens },
|
|
1512
|
+
});
|
|
1487
1513
|
}
|
|
1488
1514
|
|
|
1489
1515
|
try {
|
|
1490
1516
|
if (storedAuthorizationServerInformation) {
|
|
1517
|
+
if (refreshAttempt) {
|
|
1518
|
+
refreshAttempt.tokens = { ...tokens };
|
|
1519
|
+
}
|
|
1491
1520
|
// Attempt to refresh the token
|
|
1492
1521
|
const newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
1493
1522
|
metadata,
|