@adcp/sdk 14.2.0 → 14.4.0
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/bin/adcp.js +2 -4
- package/dist/lib/auth/oauth/MCPOAuthProvider.d.mts +6 -2
- package/dist/lib/auth/oauth/MCPOAuthProvider.d.ts +6 -2
- package/dist/lib/auth/oauth/MCPOAuthProvider.js +63 -3
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +75 -3
- package/dist/lib/auth/oauth/diagnose.js +14 -0
- package/dist/lib/auth/oauth/diagnose.mjs +14 -0
- package/dist/lib/auth/oauth/file-storage.d.mts +4 -2
- package/dist/lib/auth/oauth/file-storage.d.ts +4 -2
- package/dist/lib/auth/oauth/file-storage.js +7 -1
- package/dist/lib/auth/oauth/file-storage.mjs +7 -1
- package/dist/lib/auth/oauth/types.d.mts +10 -0
- package/dist/lib/auth/oauth/types.d.ts +10 -0
- package/dist/lib/auth/oauth/types.js +39 -0
- package/dist/lib/auth/oauth/types.mjs +37 -0
- package/dist/lib/auth/oauth/web-flow.js +11 -1
- package/dist/lib/auth/oauth/web-flow.mjs +20 -2
- package/dist/lib/protocols/mcp.js +2 -0
- package/dist/lib/protocols/mcp.mjs +2 -0
- package/dist/lib/reporting/inspection.d.mts +3 -3
- package/dist/lib/reporting/inspection.d.ts +3 -3
- package/dist/lib/reporting/inspection.js +229 -84
- package/dist/lib/reporting/inspection.mjs +229 -84
- package/dist/lib/reporting/ledger/status-ingest.js +3 -19
- package/dist/lib/reporting/ledger/status-ingest.mjs +2 -18
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.js +5 -2
- package/dist/lib/server/create-adcp-server.mjs +5 -2
- package/dist/lib/signing/canonicalize.d.mts +2 -0
- package/dist/lib/signing/canonicalize.d.ts +2 -0
- package/dist/lib/signing/canonicalize.js +1 -1
- package/dist/lib/signing/canonicalize.mjs +1 -1
- package/dist/lib/signing/verifier.js +1 -1
- package/dist/lib/signing/verifier.mjs +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.js +13 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +13 -1
- package/dist/lib/testing/storyboard/request-signing/grader.js +1 -0
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +1 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/adcp.d.mts +6 -0
- package/dist/lib/types/adcp.d.ts +6 -0
- package/dist/lib/utils/pointer-utils.d.mts +2 -0
- package/dist/lib/utils/pointer-utils.d.ts +2 -0
- package/dist/lib/utils/pointer-utils.js +43 -0
- package/dist/lib/utils/pointer-utils.mjs +19 -0
- package/dist/lib/validation/diagnostic-limits.d.mts +4 -0
- package/dist/lib/validation/diagnostic-limits.d.ts +4 -0
- package/dist/lib/validation/diagnostic-limits.js +57 -0
- package/dist/lib/validation/diagnostic-limits.mjs +32 -0
- package/dist/lib/validation/schema-errors.d.mts +3 -1
- package/dist/lib/validation/schema-errors.d.ts +3 -1
- package/dist/lib/validation/schema-errors.js +18 -3
- package/dist/lib/validation/schema-errors.mjs +18 -3
- package/dist/lib/validation/schema-validator.js +56 -4
- package/dist/lib/validation/schema-validator.mjs +56 -4
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/llms.txt +2 -2
- package/docs/migration-14.x-rc-worksheet.md +5 -5
- package/docs/migration-oauth-issuer-bindings.md +82 -0
- package/package.json +6 -6
- package/skills/call-adcp-agent/SKILL.md +4 -1
package/bin/adcp.js
CHANGED
|
@@ -1386,9 +1386,7 @@ async function ensureOAuthTokensForAlias(alias, url, { quiet = false, allowHttp
|
|
|
1386
1386
|
return existing;
|
|
1387
1387
|
}
|
|
1388
1388
|
|
|
1389
|
-
//
|
|
1390
|
-
// flow the verifier lives on the saved record, and without it the MCP SDK
|
|
1391
|
-
// throws "No PKCE code verifier found" when the AS redirects back.
|
|
1389
|
+
// A fresh login starts a new PKCE leg and rediscovers the authorization server.
|
|
1392
1390
|
const tempAgent = {
|
|
1393
1391
|
id: alias,
|
|
1394
1392
|
name: alias,
|
|
@@ -1396,7 +1394,6 @@ async function ensureOAuthTokensForAlias(alias, url, { quiet = false, allowHttp
|
|
|
1396
1394
|
protocol: 'mcp',
|
|
1397
1395
|
...(existing?.oauth_client && { oauth_client: existing.oauth_client }),
|
|
1398
1396
|
...(existing?.oauth_tokens && { oauth_tokens: existing.oauth_tokens }),
|
|
1399
|
-
...(existing?.oauth_code_verifier && { oauth_code_verifier: existing.oauth_code_verifier }),
|
|
1400
1397
|
};
|
|
1401
1398
|
|
|
1402
1399
|
const { Client: MCPClient } = require('@modelcontextprotocol/sdk/client/index.js');
|
|
@@ -6671,6 +6668,7 @@ credential material — never sync or commit.
|
|
|
6671
6668
|
delete agentConfig.oauth_tokens;
|
|
6672
6669
|
delete agentConfig.oauth_client;
|
|
6673
6670
|
delete agentConfig.oauth_code_verifier;
|
|
6671
|
+
delete agentConfig.oauth_discovery_state;
|
|
6674
6672
|
saveAgent(alias, agentConfig);
|
|
6675
6673
|
|
|
6676
6674
|
console.log(`\n✅ Cleared OAuth tokens for '${alias}'`);
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Implements the MCP SDK's OAuthClientProvider interface
|
|
5
5
|
* using AgentConfig for token storage.
|
|
6
6
|
*/
|
|
7
|
-
import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
7
|
+
import type { OAuthClientProvider, OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
8
8
|
import type { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens, OAuthFlowHandler, OAuthProviderConfig, OAuthConfigStorage, AgentConfig } from './types.mjs';
|
|
9
|
+
export declare function assertLegacyOAuthSdk(): void;
|
|
9
10
|
/**
|
|
10
11
|
* MCP OAuth Client Provider
|
|
11
12
|
*
|
|
@@ -90,6 +91,9 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
90
91
|
* Redirect user to authorization URL
|
|
91
92
|
*/
|
|
92
93
|
redirectToAuthorization(authorizationUrl: URL): Promise<void>;
|
|
94
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
95
|
+
saveDiscoveryState(state: OAuthDiscoveryState): Promise<void>;
|
|
96
|
+
discoveryState(): Promise<OAuthDiscoveryState | undefined>;
|
|
93
97
|
/**
|
|
94
98
|
* Save PKCE code verifier
|
|
95
99
|
*/
|
|
@@ -103,7 +107,7 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
103
107
|
/**
|
|
104
108
|
* Invalidate credentials when server indicates they're invalid
|
|
105
109
|
*/
|
|
106
|
-
invalidateCredentials(scope: '
|
|
110
|
+
invalidateCredentials(scope: Parameters<NonNullable<OAuthClientProvider['invalidateCredentials']>>[0]): Promise<void>;
|
|
107
111
|
/**
|
|
108
112
|
* Persist agent config to storage if configured
|
|
109
113
|
*/
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Implements the MCP SDK's OAuthClientProvider interface
|
|
5
5
|
* using AgentConfig for token storage.
|
|
6
6
|
*/
|
|
7
|
-
import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
7
|
+
import type { OAuthClientProvider, OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
8
8
|
import type { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens, OAuthFlowHandler, OAuthProviderConfig, OAuthConfigStorage, AgentConfig } from './types';
|
|
9
|
+
export declare function assertLegacyOAuthSdk(): void;
|
|
9
10
|
/**
|
|
10
11
|
* MCP OAuth Client Provider
|
|
11
12
|
*
|
|
@@ -90,6 +91,9 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
90
91
|
* Redirect user to authorization URL
|
|
91
92
|
*/
|
|
92
93
|
redirectToAuthorization(authorizationUrl: URL): Promise<void>;
|
|
94
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
95
|
+
saveDiscoveryState(state: OAuthDiscoveryState): Promise<void>;
|
|
96
|
+
discoveryState(): Promise<OAuthDiscoveryState | undefined>;
|
|
93
97
|
/**
|
|
94
98
|
* Save PKCE code verifier
|
|
95
99
|
*/
|
|
@@ -103,7 +107,7 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
103
107
|
/**
|
|
104
108
|
* Invalidate credentials when server indicates they're invalid
|
|
105
109
|
*/
|
|
106
|
-
invalidateCredentials(scope: '
|
|
110
|
+
invalidateCredentials(scope: Parameters<NonNullable<OAuthClientProvider['invalidateCredentials']>>[0]): Promise<void>;
|
|
107
111
|
/**
|
|
108
112
|
* Persist agent config to storage if configured
|
|
109
113
|
*/
|
|
@@ -18,12 +18,40 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var MCPOAuthProvider_exports = {};
|
|
20
20
|
__export(MCPOAuthProvider_exports, {
|
|
21
|
-
MCPOAuthProvider: () => MCPOAuthProvider
|
|
21
|
+
MCPOAuthProvider: () => MCPOAuthProvider,
|
|
22
|
+
assertLegacyOAuthSdk: () => assertLegacyOAuthSdk
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(MCPOAuthProvider_exports);
|
|
24
25
|
var import_types = require('./types.js');
|
|
25
26
|
var import_crypto = require("crypto");
|
|
27
|
+
var import_node_fs = require("node:fs");
|
|
28
|
+
var import_node_path = require("node:path");
|
|
29
|
+
var import_node_module = require("node:module");
|
|
26
30
|
var import_resource_url = require('./resource-url.js');
|
|
31
|
+
let legacyOAuthSdkChecked = false;
|
|
32
|
+
function assertLegacyOAuthSdk() {
|
|
33
|
+
if (legacyOAuthSdkChecked) return;
|
|
34
|
+
try {
|
|
35
|
+
const clientPath = (0, import_node_module.createRequire)((0, import_node_path.resolve)(__dirname, "MCPOAuthProvider.js")).resolve(
|
|
36
|
+
"@modelcontextprotocol/sdk/client/index.js"
|
|
37
|
+
);
|
|
38
|
+
const { version } = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.resolve)((0, import_node_path.dirname)(clientPath), "../../../package.json"), "utf8"));
|
|
39
|
+
const match = typeof version === "string" && /^1\.(\d+)\.\d+$/.exec(version);
|
|
40
|
+
if (!match || Number(match[1]) < 31) {
|
|
41
|
+
throw new import_types.OAuthError(
|
|
42
|
+
"Legacy MCP OAuth requires @modelcontextprotocol/sdk >=1.31.0 within major 1. Upgrade the installed peer dependency.",
|
|
43
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error instanceof import_types.OAuthError) throw error;
|
|
48
|
+
throw new import_types.OAuthError(
|
|
49
|
+
"Cannot verify a patched legacy MCP SDK. Install @modelcontextprotocol/sdk ^1.31.0 and keep it external when bundling.",
|
|
50
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
legacyOAuthSdkChecked = true;
|
|
54
|
+
}
|
|
27
55
|
class MCPOAuthProvider {
|
|
28
56
|
agent;
|
|
29
57
|
storage;
|
|
@@ -97,6 +125,8 @@ class MCPOAuthProvider {
|
|
|
97
125
|
*/
|
|
98
126
|
async clientInformation() {
|
|
99
127
|
if (this.agent.oauth_client) {
|
|
128
|
+
if (this.agent.oauth_client.client_secret) assertLegacyOAuthSdk();
|
|
129
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_client, void 0, !!this.agent.oauth_client.client_secret);
|
|
100
130
|
return (0, import_types.toMCPClientInfo)(this.agent.oauth_client);
|
|
101
131
|
}
|
|
102
132
|
return void 0;
|
|
@@ -113,6 +143,8 @@ class MCPOAuthProvider {
|
|
|
113
143
|
*/
|
|
114
144
|
async tokens() {
|
|
115
145
|
if (this.agent.oauth_tokens) {
|
|
146
|
+
if (this.agent.oauth_tokens.refresh_token) assertLegacyOAuthSdk();
|
|
147
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_tokens, void 0, !!this.agent.oauth_tokens.refresh_token);
|
|
116
148
|
return (0, import_types.toMCPTokens)(this.agent.oauth_tokens);
|
|
117
149
|
}
|
|
118
150
|
return void 0;
|
|
@@ -124,13 +156,34 @@ class MCPOAuthProvider {
|
|
|
124
156
|
async saveTokens(tokens) {
|
|
125
157
|
this.agent.oauth_tokens = (0, import_types.fromMCPTokens)(tokens);
|
|
126
158
|
delete this.agent.oauth_code_verifier;
|
|
159
|
+
delete this.agent.oauth_discovery_state;
|
|
127
160
|
await this.persistAgent();
|
|
128
161
|
}
|
|
129
162
|
/**
|
|
130
163
|
* Redirect user to authorization URL
|
|
131
164
|
*/
|
|
132
165
|
async redirectToAuthorization(authorizationUrl) {
|
|
133
|
-
|
|
166
|
+
try {
|
|
167
|
+
await this.flowHandler.redirectToAuthorization(authorizationUrl);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
await this.invalidateCredentials("verifier");
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
174
|
+
async saveDiscoveryState(state) {
|
|
175
|
+
const issuer = String(state.authorizationServerUrl);
|
|
176
|
+
if (this.agent.oauth_tokens?.refresh_token) {
|
|
177
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_tokens, issuer, true);
|
|
178
|
+
}
|
|
179
|
+
if (this.agent.oauth_client?.client_secret) {
|
|
180
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_client, issuer, true);
|
|
181
|
+
}
|
|
182
|
+
this.agent.oauth_discovery_state = state;
|
|
183
|
+
await this.persistAgent();
|
|
184
|
+
}
|
|
185
|
+
async discoveryState() {
|
|
186
|
+
return this.agent.oauth_code_verifier ? this.agent.oauth_discovery_state : void 0;
|
|
134
187
|
}
|
|
135
188
|
/**
|
|
136
189
|
* Save PKCE code verifier
|
|
@@ -158,6 +211,7 @@ class MCPOAuthProvider {
|
|
|
158
211
|
async invalidateCredentials(scope) {
|
|
159
212
|
switch (scope) {
|
|
160
213
|
case "all":
|
|
214
|
+
delete this.agent.oauth_discovery_state;
|
|
161
215
|
delete this.agent.oauth_tokens;
|
|
162
216
|
delete this.agent.oauth_client;
|
|
163
217
|
delete this.agent.oauth_code_verifier;
|
|
@@ -166,9 +220,14 @@ class MCPOAuthProvider {
|
|
|
166
220
|
delete this.agent.oauth_tokens;
|
|
167
221
|
break;
|
|
168
222
|
case "client":
|
|
223
|
+
delete this.agent.oauth_discovery_state;
|
|
169
224
|
delete this.agent.oauth_client;
|
|
170
225
|
break;
|
|
226
|
+
case "discovery":
|
|
227
|
+
delete this.agent.oauth_discovery_state;
|
|
228
|
+
break;
|
|
171
229
|
case "verifier":
|
|
230
|
+
delete this.agent.oauth_discovery_state;
|
|
172
231
|
delete this.agent.oauth_code_verifier;
|
|
173
232
|
break;
|
|
174
233
|
}
|
|
@@ -243,5 +302,6 @@ class MCPOAuthProvider {
|
|
|
243
302
|
}
|
|
244
303
|
// Annotate the CommonJS export names for ESM import in node:
|
|
245
304
|
0 && (module.exports = {
|
|
246
|
-
MCPOAuthProvider
|
|
305
|
+
MCPOAuthProvider,
|
|
306
|
+
assertLegacyOAuthSdk
|
|
247
307
|
});
|
|
@@ -1,6 +1,46 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fileURLToPath as __adcpFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __adcpDirname } from "node:path";
|
|
3
|
+
import { createRequire as __adcpCreateRequire } from "node:module";
|
|
4
|
+
const __dirname = __adcpDirname(__adcpFileURLToPath(import.meta.url));
|
|
5
|
+
const require2 = __adcpCreateRequire(import.meta.url);
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_CLIENT_METADATA,
|
|
8
|
+
toMCPTokens,
|
|
9
|
+
fromMCPTokens,
|
|
10
|
+
toMCPClientInfo,
|
|
11
|
+
fromMCPClientInfo,
|
|
12
|
+
assertOAuthCredentialIssuer,
|
|
13
|
+
OAuthError
|
|
14
|
+
} from "./types.mjs";
|
|
2
15
|
import { randomBytes } from "crypto";
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { dirname, resolve } from "node:path";
|
|
18
|
+
import { createRequire } from "node:module";
|
|
3
19
|
import { validateOAuthResourceUrl } from "./resource-url.mjs";
|
|
20
|
+
let legacyOAuthSdkChecked = false;
|
|
21
|
+
function assertLegacyOAuthSdk() {
|
|
22
|
+
if (legacyOAuthSdkChecked) return;
|
|
23
|
+
try {
|
|
24
|
+
const clientPath = createRequire(resolve(__dirname, "MCPOAuthProvider.js")).resolve(
|
|
25
|
+
"@modelcontextprotocol/sdk/client/index.js"
|
|
26
|
+
);
|
|
27
|
+
const { version } = JSON.parse(readFileSync(resolve(dirname(clientPath), "../../../package.json"), "utf8"));
|
|
28
|
+
const match = typeof version === "string" && /^1\.(\d+)\.\d+$/.exec(version);
|
|
29
|
+
if (!match || Number(match[1]) < 31) {
|
|
30
|
+
throw new OAuthError(
|
|
31
|
+
"Legacy MCP OAuth requires @modelcontextprotocol/sdk >=1.31.0 within major 1. Upgrade the installed peer dependency.",
|
|
32
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof OAuthError) throw error;
|
|
37
|
+
throw new OAuthError(
|
|
38
|
+
"Cannot verify a patched legacy MCP SDK. Install @modelcontextprotocol/sdk ^1.31.0 and keep it external when bundling.",
|
|
39
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
legacyOAuthSdkChecked = true;
|
|
43
|
+
}
|
|
4
44
|
class MCPOAuthProvider {
|
|
5
45
|
agent;
|
|
6
46
|
storage;
|
|
@@ -74,6 +114,8 @@ class MCPOAuthProvider {
|
|
|
74
114
|
*/
|
|
75
115
|
async clientInformation() {
|
|
76
116
|
if (this.agent.oauth_client) {
|
|
117
|
+
if (this.agent.oauth_client.client_secret) assertLegacyOAuthSdk();
|
|
118
|
+
assertOAuthCredentialIssuer(this.agent.oauth_client, void 0, !!this.agent.oauth_client.client_secret);
|
|
77
119
|
return toMCPClientInfo(this.agent.oauth_client);
|
|
78
120
|
}
|
|
79
121
|
return void 0;
|
|
@@ -90,6 +132,8 @@ class MCPOAuthProvider {
|
|
|
90
132
|
*/
|
|
91
133
|
async tokens() {
|
|
92
134
|
if (this.agent.oauth_tokens) {
|
|
135
|
+
if (this.agent.oauth_tokens.refresh_token) assertLegacyOAuthSdk();
|
|
136
|
+
assertOAuthCredentialIssuer(this.agent.oauth_tokens, void 0, !!this.agent.oauth_tokens.refresh_token);
|
|
93
137
|
return toMCPTokens(this.agent.oauth_tokens);
|
|
94
138
|
}
|
|
95
139
|
return void 0;
|
|
@@ -101,13 +145,34 @@ class MCPOAuthProvider {
|
|
|
101
145
|
async saveTokens(tokens) {
|
|
102
146
|
this.agent.oauth_tokens = fromMCPTokens(tokens);
|
|
103
147
|
delete this.agent.oauth_code_verifier;
|
|
148
|
+
delete this.agent.oauth_discovery_state;
|
|
104
149
|
await this.persistAgent();
|
|
105
150
|
}
|
|
106
151
|
/**
|
|
107
152
|
* Redirect user to authorization URL
|
|
108
153
|
*/
|
|
109
154
|
async redirectToAuthorization(authorizationUrl) {
|
|
110
|
-
|
|
155
|
+
try {
|
|
156
|
+
await this.flowHandler.redirectToAuthorization(authorizationUrl);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
await this.invalidateCredentials("verifier");
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
163
|
+
async saveDiscoveryState(state) {
|
|
164
|
+
const issuer = String(state.authorizationServerUrl);
|
|
165
|
+
if (this.agent.oauth_tokens?.refresh_token) {
|
|
166
|
+
assertOAuthCredentialIssuer(this.agent.oauth_tokens, issuer, true);
|
|
167
|
+
}
|
|
168
|
+
if (this.agent.oauth_client?.client_secret) {
|
|
169
|
+
assertOAuthCredentialIssuer(this.agent.oauth_client, issuer, true);
|
|
170
|
+
}
|
|
171
|
+
this.agent.oauth_discovery_state = state;
|
|
172
|
+
await this.persistAgent();
|
|
173
|
+
}
|
|
174
|
+
async discoveryState() {
|
|
175
|
+
return this.agent.oauth_code_verifier ? this.agent.oauth_discovery_state : void 0;
|
|
111
176
|
}
|
|
112
177
|
/**
|
|
113
178
|
* Save PKCE code verifier
|
|
@@ -135,6 +200,7 @@ class MCPOAuthProvider {
|
|
|
135
200
|
async invalidateCredentials(scope) {
|
|
136
201
|
switch (scope) {
|
|
137
202
|
case "all":
|
|
203
|
+
delete this.agent.oauth_discovery_state;
|
|
138
204
|
delete this.agent.oauth_tokens;
|
|
139
205
|
delete this.agent.oauth_client;
|
|
140
206
|
delete this.agent.oauth_code_verifier;
|
|
@@ -143,9 +209,14 @@ class MCPOAuthProvider {
|
|
|
143
209
|
delete this.agent.oauth_tokens;
|
|
144
210
|
break;
|
|
145
211
|
case "client":
|
|
212
|
+
delete this.agent.oauth_discovery_state;
|
|
146
213
|
delete this.agent.oauth_client;
|
|
147
214
|
break;
|
|
215
|
+
case "discovery":
|
|
216
|
+
delete this.agent.oauth_discovery_state;
|
|
217
|
+
break;
|
|
148
218
|
case "verifier":
|
|
219
|
+
delete this.agent.oauth_discovery_state;
|
|
149
220
|
delete this.agent.oauth_code_verifier;
|
|
150
221
|
break;
|
|
151
222
|
}
|
|
@@ -219,5 +290,6 @@ class MCPOAuthProvider {
|
|
|
219
290
|
}
|
|
220
291
|
}
|
|
221
292
|
export {
|
|
222
|
-
MCPOAuthProvider
|
|
293
|
+
MCPOAuthProvider,
|
|
294
|
+
assertLegacyOAuthSdk
|
|
223
295
|
};
|
|
@@ -23,6 +23,7 @@ __export(diagnose_exports, {
|
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(diagnose_exports);
|
|
25
25
|
var import_net = require('../../net/index.js');
|
|
26
|
+
var import_types = require('./types.js');
|
|
26
27
|
var import_diagnostics = require('./diagnostics.js');
|
|
27
28
|
const AUTH_DIAGNOSIS_SCHEMA_VERSION = 1;
|
|
28
29
|
async function runAuthDiagnosis(agent, options = {}) {
|
|
@@ -48,6 +49,17 @@ async function runAuthDiagnosis(agent, options = {}) {
|
|
|
48
49
|
if (!options.skipRefresh && agent.oauth_tokens?.refresh_token) {
|
|
49
50
|
const tokenEndpoint = extractTokenEndpoint(asCapture);
|
|
50
51
|
const clientId = agent.oauth_client?.client_id;
|
|
52
|
+
let issuerError;
|
|
53
|
+
if (tokenEndpoint && clientId) {
|
|
54
|
+
try {
|
|
55
|
+
const issuer = new URL(extractIssuer(prmCapture) ?? "").toString();
|
|
56
|
+
(0, import_types.assertOAuthServerIssuer)(asCapture.body, issuer);
|
|
57
|
+
(0, import_types.assertOAuthCredentialIssuer)(agent.oauth_tokens, issuer, true);
|
|
58
|
+
(0, import_types.assertOAuthCredentialIssuer)(agent.oauth_client, issuer, !!agent.oauth_client?.client_secret);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
issuerError = error instanceof import_types.OAuthError ? error.message : "Authorization-server issuer could not be validated";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
51
63
|
const resource = agentUrl;
|
|
52
64
|
if (!tokenEndpoint) {
|
|
53
65
|
steps.push({
|
|
@@ -59,6 +71,8 @@ async function runAuthDiagnosis(agent, options = {}) {
|
|
|
59
71
|
name: "token_refresh_attempt",
|
|
60
72
|
error: "No saved oauth_client.client_id \u2014 cannot attempt refresh without it"
|
|
61
73
|
});
|
|
74
|
+
} else if (issuerError) {
|
|
75
|
+
steps.push({ name: "token_refresh_attempt", error: issuerError });
|
|
62
76
|
} else {
|
|
63
77
|
const refreshCapture = await attemptTokenRefresh({
|
|
64
78
|
tokenEndpoint,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ssrfSafeFetch, decodeBodyAsJsonOrText, SsrfRefusedError } from "../../net/index.mjs";
|
|
2
|
+
import { assertOAuthCredentialIssuer, assertOAuthServerIssuer, OAuthError } from "./types.mjs";
|
|
2
3
|
import { decodeAccessTokenClaims, parseWWWAuthenticate, validateTokenAudience } from "./diagnostics.mjs";
|
|
3
4
|
const AUTH_DIAGNOSIS_SCHEMA_VERSION = 1;
|
|
4
5
|
async function runAuthDiagnosis(agent, options = {}) {
|
|
@@ -24,6 +25,17 @@ async function runAuthDiagnosis(agent, options = {}) {
|
|
|
24
25
|
if (!options.skipRefresh && agent.oauth_tokens?.refresh_token) {
|
|
25
26
|
const tokenEndpoint = extractTokenEndpoint(asCapture);
|
|
26
27
|
const clientId = agent.oauth_client?.client_id;
|
|
28
|
+
let issuerError;
|
|
29
|
+
if (tokenEndpoint && clientId) {
|
|
30
|
+
try {
|
|
31
|
+
const issuer = new URL(extractIssuer(prmCapture) ?? "").toString();
|
|
32
|
+
assertOAuthServerIssuer(asCapture.body, issuer);
|
|
33
|
+
assertOAuthCredentialIssuer(agent.oauth_tokens, issuer, true);
|
|
34
|
+
assertOAuthCredentialIssuer(agent.oauth_client, issuer, !!agent.oauth_client?.client_secret);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
issuerError = error instanceof OAuthError ? error.message : "Authorization-server issuer could not be validated";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
27
39
|
const resource = agentUrl;
|
|
28
40
|
if (!tokenEndpoint) {
|
|
29
41
|
steps.push({
|
|
@@ -35,6 +47,8 @@ async function runAuthDiagnosis(agent, options = {}) {
|
|
|
35
47
|
name: "token_refresh_attempt",
|
|
36
48
|
error: "No saved oauth_client.client_id \u2014 cannot attempt refresh without it"
|
|
37
49
|
});
|
|
50
|
+
} else if (issuerError) {
|
|
51
|
+
steps.push({ name: "token_refresh_attempt", error: issuerError });
|
|
38
52
|
} else {
|
|
39
53
|
const refreshCapture = await attemptTokenRefresh({
|
|
40
54
|
tokenEndpoint,
|
|
@@ -28,8 +28,10 @@ export interface FileOAuthStorageOptions {
|
|
|
28
28
|
*
|
|
29
29
|
* Does NOT touch fields outside the OAuth envelope (`auth_token`, custom
|
|
30
30
|
* fields) when saving — the MCP provider only mutates `oauth_tokens`,
|
|
31
|
-
* `oauth_client`, `oauth_resource`,
|
|
32
|
-
*
|
|
31
|
+
* `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
|
|
32
|
+
* Those fields are authoritative: an absent value deletes the stored value.
|
|
33
|
+
* Load the complete agent before constructing a provider; partial snapshots
|
|
34
|
+
* cannot preserve omitted OAuth fields. Other fields keep merge semantics.
|
|
33
35
|
*
|
|
34
36
|
* @example
|
|
35
37
|
* ```ts
|
|
@@ -28,8 +28,10 @@ export interface FileOAuthStorageOptions {
|
|
|
28
28
|
*
|
|
29
29
|
* Does NOT touch fields outside the OAuth envelope (`auth_token`, custom
|
|
30
30
|
* fields) when saving — the MCP provider only mutates `oauth_tokens`,
|
|
31
|
-
* `oauth_client`, `oauth_resource`,
|
|
32
|
-
*
|
|
31
|
+
* `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
|
|
32
|
+
* Those fields are authoritative: an absent value deletes the stored value.
|
|
33
|
+
* Load the complete agent before constructing a provider; partial snapshots
|
|
34
|
+
* cannot preserve omitted OAuth fields. Other fields keep merge semantics.
|
|
33
35
|
*
|
|
34
36
|
* @example
|
|
35
37
|
* ```ts
|
|
@@ -75,7 +75,8 @@ function createFileOAuthStorage(options) {
|
|
|
75
75
|
oauth_client: stored.oauth_client,
|
|
76
76
|
oauth_resource: stored.oauth_resource,
|
|
77
77
|
oauth_client_credentials: stored.oauth_client_credentials,
|
|
78
|
-
oauth_code_verifier: stored.oauth_code_verifier
|
|
78
|
+
oauth_code_verifier: stored.oauth_code_verifier,
|
|
79
|
+
oauth_discovery_state: stored.oauth_discovery_state
|
|
79
80
|
};
|
|
80
81
|
},
|
|
81
82
|
async saveAgent(agent) {
|
|
@@ -93,6 +94,11 @@ function createFileOAuthStorage(options) {
|
|
|
93
94
|
...agent.oauth_client_credentials !== void 0 ? { oauth_client_credentials: agent.oauth_client_credentials } : {},
|
|
94
95
|
...agent.oauth_code_verifier !== void 0 ? { oauth_code_verifier: agent.oauth_code_verifier } : {}
|
|
95
96
|
};
|
|
97
|
+
if (agent.oauth_tokens === void 0) delete next.oauth_tokens;
|
|
98
|
+
if (agent.oauth_client === void 0) delete next.oauth_client;
|
|
99
|
+
if (agent.oauth_code_verifier === void 0) delete next.oauth_code_verifier;
|
|
100
|
+
if (agent.oauth_discovery_state === void 0) delete next.oauth_discovery_state;
|
|
101
|
+
else next.oauth_discovery_state = agent.oauth_discovery_state;
|
|
96
102
|
if (agent.oauth_resource === void 0) delete next.oauth_resource;
|
|
97
103
|
config.agents[key] = next;
|
|
98
104
|
await writeConfig(config);
|
|
@@ -42,7 +42,8 @@ function createFileOAuthStorage(options) {
|
|
|
42
42
|
oauth_client: stored.oauth_client,
|
|
43
43
|
oauth_resource: stored.oauth_resource,
|
|
44
44
|
oauth_client_credentials: stored.oauth_client_credentials,
|
|
45
|
-
oauth_code_verifier: stored.oauth_code_verifier
|
|
45
|
+
oauth_code_verifier: stored.oauth_code_verifier,
|
|
46
|
+
oauth_discovery_state: stored.oauth_discovery_state
|
|
46
47
|
};
|
|
47
48
|
},
|
|
48
49
|
async saveAgent(agent) {
|
|
@@ -60,6 +61,11 @@ function createFileOAuthStorage(options) {
|
|
|
60
61
|
...agent.oauth_client_credentials !== void 0 ? { oauth_client_credentials: agent.oauth_client_credentials } : {},
|
|
61
62
|
...agent.oauth_code_verifier !== void 0 ? { oauth_code_verifier: agent.oauth_code_verifier } : {}
|
|
62
63
|
};
|
|
64
|
+
if (agent.oauth_tokens === void 0) delete next.oauth_tokens;
|
|
65
|
+
if (agent.oauth_client === void 0) delete next.oauth_client;
|
|
66
|
+
if (agent.oauth_code_verifier === void 0) delete next.oauth_code_verifier;
|
|
67
|
+
if (agent.oauth_discovery_state === void 0) delete next.oauth_discovery_state;
|
|
68
|
+
else next.oauth_discovery_state = agent.oauth_discovery_state;
|
|
63
69
|
if (agent.oauth_resource === void 0) delete next.oauth_resource;
|
|
64
70
|
config.agents[key] = next;
|
|
65
71
|
await writeConfig(config);
|
|
@@ -13,6 +13,8 @@ export type { AgentConfig, AgentOAuthTokens, AgentOAuthClient };
|
|
|
13
13
|
* Agent config storage interface
|
|
14
14
|
*
|
|
15
15
|
* Implement this to persist OAuth tokens back to agent configuration.
|
|
16
|
+
* Round-trip pending `oauth_code_verifier` and `oauth_discovery_state` verbatim,
|
|
17
|
+
* and remove OAuth fields absent from the authoritative agent snapshot.
|
|
16
18
|
* This allows different storage backends (file, database, memory)
|
|
17
19
|
* while keeping tokens in the agent config structure.
|
|
18
20
|
*/
|
|
@@ -132,3 +134,11 @@ export declare function toMCPClientInfo(client: AgentOAuthClient): OAuthClientIn
|
|
|
132
134
|
* Convert MCP SDK OAuthClientInformationFull to AgentOAuthClient format
|
|
133
135
|
*/
|
|
134
136
|
export declare function fromMCPClientInfo(info: OAuthClientInformationFull): AgentOAuthClient;
|
|
137
|
+
/** Internal credential-binding guard; old secrets must be migrated from trusted configuration. */
|
|
138
|
+
export declare function assertOAuthCredentialIssuer(credential: {
|
|
139
|
+
issuer?: unknown;
|
|
140
|
+
}, expectedIssuer?: string, requireIssuer?: boolean): void;
|
|
141
|
+
/** Internal metadata guard, matching the modern MCP SDK's asymmetric issuer-echo rule. */
|
|
142
|
+
export declare function assertOAuthServerIssuer(metadata: {
|
|
143
|
+
issuer?: unknown;
|
|
144
|
+
} | undefined, authorizationServerUrl: string): void;
|
|
@@ -13,6 +13,8 @@ export type { AgentConfig, AgentOAuthTokens, AgentOAuthClient };
|
|
|
13
13
|
* Agent config storage interface
|
|
14
14
|
*
|
|
15
15
|
* Implement this to persist OAuth tokens back to agent configuration.
|
|
16
|
+
* Round-trip pending `oauth_code_verifier` and `oauth_discovery_state` verbatim,
|
|
17
|
+
* and remove OAuth fields absent from the authoritative agent snapshot.
|
|
16
18
|
* This allows different storage backends (file, database, memory)
|
|
17
19
|
* while keeping tokens in the agent config structure.
|
|
18
20
|
*/
|
|
@@ -132,3 +134,11 @@ export declare function toMCPClientInfo(client: AgentOAuthClient): OAuthClientIn
|
|
|
132
134
|
* Convert MCP SDK OAuthClientInformationFull to AgentOAuthClient format
|
|
133
135
|
*/
|
|
134
136
|
export declare function fromMCPClientInfo(info: OAuthClientInformationFull): AgentOAuthClient;
|
|
137
|
+
/** Internal credential-binding guard; old secrets must be migrated from trusted configuration. */
|
|
138
|
+
export declare function assertOAuthCredentialIssuer(credential: {
|
|
139
|
+
issuer?: unknown;
|
|
140
|
+
}, expectedIssuer?: string, requireIssuer?: boolean): void;
|
|
141
|
+
/** Internal metadata guard, matching the modern MCP SDK's asymmetric issuer-echo rule. */
|
|
142
|
+
export declare function assertOAuthServerIssuer(metadata: {
|
|
143
|
+
issuer?: unknown;
|
|
144
|
+
} | undefined, authorizationServerUrl: string): void;
|
|
@@ -22,6 +22,8 @@ __export(types_exports, {
|
|
|
22
22
|
OAuthCancelledError: () => OAuthCancelledError,
|
|
23
23
|
OAuthError: () => OAuthError,
|
|
24
24
|
OAuthTimeoutError: () => OAuthTimeoutError,
|
|
25
|
+
assertOAuthCredentialIssuer: () => assertOAuthCredentialIssuer,
|
|
26
|
+
assertOAuthServerIssuer: () => assertOAuthServerIssuer,
|
|
25
27
|
fromMCPClientInfo: () => fromMCPClientInfo,
|
|
26
28
|
fromMCPTokens: () => fromMCPTokens,
|
|
27
29
|
toMCPClientInfo: () => toMCPClientInfo,
|
|
@@ -60,6 +62,7 @@ class OAuthTimeoutError extends OAuthError {
|
|
|
60
62
|
}
|
|
61
63
|
function toMCPTokens(tokens) {
|
|
62
64
|
return {
|
|
65
|
+
...tokens.issuer !== void 0 && tokens.issuer !== null && { issuer: tokens.issuer },
|
|
63
66
|
access_token: tokens.access_token,
|
|
64
67
|
refresh_token: tokens.refresh_token,
|
|
65
68
|
token_type: tokens.token_type || "Bearer",
|
|
@@ -69,6 +72,7 @@ function toMCPTokens(tokens) {
|
|
|
69
72
|
}
|
|
70
73
|
function fromMCPTokens(tokens) {
|
|
71
74
|
const result = {
|
|
75
|
+
...tokens.issuer !== void 0 && tokens.issuer !== null && { issuer: tokens.issuer },
|
|
72
76
|
access_token: tokens.access_token,
|
|
73
77
|
token_type: tokens.token_type,
|
|
74
78
|
scope: tokens.scope
|
|
@@ -84,6 +88,7 @@ function fromMCPTokens(tokens) {
|
|
|
84
88
|
}
|
|
85
89
|
function toMCPClientInfo(client) {
|
|
86
90
|
return {
|
|
91
|
+
...client.issuer !== void 0 && client.issuer !== null && { issuer: client.issuer },
|
|
87
92
|
client_id: client.client_id,
|
|
88
93
|
client_secret: client.client_secret,
|
|
89
94
|
client_secret_expires_at: client.client_secret_expires_at
|
|
@@ -91,17 +96,51 @@ function toMCPClientInfo(client) {
|
|
|
91
96
|
}
|
|
92
97
|
function fromMCPClientInfo(info) {
|
|
93
98
|
return {
|
|
99
|
+
...info.issuer !== void 0 && info.issuer !== null && { issuer: info.issuer },
|
|
94
100
|
client_id: info.client_id,
|
|
95
101
|
client_secret: info.client_secret,
|
|
96
102
|
client_secret_expires_at: info.client_secret_expires_at
|
|
97
103
|
};
|
|
98
104
|
}
|
|
105
|
+
function isOAuthIssuer(value) {
|
|
106
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
107
|
+
try {
|
|
108
|
+
const url = new URL(value);
|
|
109
|
+
return (url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password && !url.search && !url.hash;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function assertOAuthCredentialIssuer(credential, expectedIssuer, requireIssuer = false) {
|
|
115
|
+
const issuer = credential.issuer;
|
|
116
|
+
if ((issuer === void 0 || issuer === null) && !requireIssuer) return;
|
|
117
|
+
if (!isOAuthIssuer(issuer)) {
|
|
118
|
+
throw new OAuthError(
|
|
119
|
+
"Saved OAuth credentials need a valid issuer. Clear them and sign in again, or set their issuer from trusted configuration.",
|
|
120
|
+
"oauth_issuer_required"
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (expectedIssuer !== void 0 && issuer !== expectedIssuer && !(issuer.endsWith("/") && issuer.slice(0, -1) === expectedIssuer) && !(expectedIssuer.endsWith("/") && expectedIssuer.slice(0, -1) === issuer)) {
|
|
124
|
+
throw new OAuthError("OAuth credentials belong to a different authorization server.", "oauth_issuer_mismatch");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function assertOAuthServerIssuer(metadata, authorizationServerUrl) {
|
|
128
|
+
const issuer = metadata?.issuer;
|
|
129
|
+
if (!isOAuthIssuer(issuer) || issuer !== authorizationServerUrl && !(authorizationServerUrl.endsWith("/") && issuer === authorizationServerUrl.slice(0, -1))) {
|
|
130
|
+
throw new OAuthError(
|
|
131
|
+
"Authorization-server metadata does not match the discovered issuer.",
|
|
132
|
+
"oauth_issuer_mismatch"
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
99
136
|
// Annotate the CommonJS export names for ESM import in node:
|
|
100
137
|
0 && (module.exports = {
|
|
101
138
|
DEFAULT_CLIENT_METADATA,
|
|
102
139
|
OAuthCancelledError,
|
|
103
140
|
OAuthError,
|
|
104
141
|
OAuthTimeoutError,
|
|
142
|
+
assertOAuthCredentialIssuer,
|
|
143
|
+
assertOAuthServerIssuer,
|
|
105
144
|
fromMCPClientInfo,
|
|
106
145
|
fromMCPTokens,
|
|
107
146
|
toMCPClientInfo,
|