@adcp/sdk 14.4.0 → 15.0.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 +6 -1
- package/dist/lib/auth/oauth/ClientCredentialsFlow.js +4 -1
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +4 -1
- package/dist/lib/auth/oauth/MCPOAuthProvider.d.mts +19 -6
- package/dist/lib/auth/oauth/MCPOAuthProvider.d.ts +19 -6
- package/dist/lib/auth/oauth/MCPOAuthProvider.js +95 -30
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +96 -30
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.d.mts +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.d.ts +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.js +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +1 -1
- package/dist/lib/auth/oauth/client-credentials-schema.d.mts +17 -0
- package/dist/lib/auth/oauth/client-credentials-schema.d.ts +17 -0
- package/dist/lib/auth/oauth/client-credentials-schema.js +37 -0
- package/dist/lib/auth/oauth/client-credentials-schema.mjs +13 -0
- package/dist/lib/auth/oauth/file-storage.d.mts +3 -3
- package/dist/lib/auth/oauth/file-storage.d.ts +3 -3
- package/dist/lib/auth/oauth/file-storage.js +12 -5
- package/dist/lib/auth/oauth/file-storage.mjs +12 -5
- package/dist/lib/auth/oauth/index.d.mts +10 -6
- package/dist/lib/auth/oauth/index.d.ts +10 -6
- package/dist/lib/auth/oauth/index.js +9 -3
- package/dist/lib/auth/oauth/index.mjs +8 -3
- package/dist/lib/auth/oauth/types.d.mts +9 -2
- package/dist/lib/auth/oauth/types.d.ts +9 -2
- package/dist/lib/auth/oauth/types.js +3 -2
- package/dist/lib/auth/oauth/types.mjs +3 -2
- package/dist/lib/auth/oauth/web-flow.d.mts +2 -0
- package/dist/lib/auth/oauth/web-flow.d.ts +2 -0
- package/dist/lib/auth/oauth/web-flow.js +42 -9
- package/dist/lib/auth/oauth/web-flow.mjs +42 -9
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.mjs +3 -1
- package/dist/lib/schemas/index.d.mts +2 -0
- package/dist/lib/schemas/index.d.ts +2 -0
- package/dist/lib/schemas/index.js +3 -0
- package/dist/lib/schemas/index.mjs +2 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/testing/client.js +8 -3
- package/dist/lib/testing/client.mjs +8 -3
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/types.d.mts +10 -1
- package/dist/lib/testing/types.d.ts +10 -1
- 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 +1 -1
- package/docs/ZOD-SCHEMAS.md +19 -0
- package/docs/guides/OAUTH-ISSUER-BINDING.md +46 -0
- package/docs/guides/WEB-OAUTH.md +58 -163
- package/docs/guides/account-resolution.md +1 -1
- package/docs/llms.txt +1 -1
- package/docs/migration-14.0-to-14.1.md +1 -1
- package/docs/migration-14.x-rc-worksheet.md +7 -7
- package/docs/migration-oauth-issuer-bindings.md +14 -79
- package/package.json +1 -1
|
@@ -33,7 +33,7 @@ class NonInteractiveFlowHandler {
|
|
|
33
33
|
return this._redirectUrl;
|
|
34
34
|
}
|
|
35
35
|
async redirectToAuthorization() {
|
|
36
|
-
const target = this.agentHint ? `adcp
|
|
36
|
+
const target = this.agentHint ? `adcp ${this.agentHint} --oauth` : "adcp --save-auth <alias> <url> --oauth";
|
|
37
37
|
throw new import_types.OAuthError(
|
|
38
38
|
`OAuth authorization required but this context is non-interactive. Run \`${target}\` from a terminal with a browser, then retry.`,
|
|
39
39
|
"interactive_required"
|
|
@@ -10,7 +10,7 @@ class NonInteractiveFlowHandler {
|
|
|
10
10
|
return this._redirectUrl;
|
|
11
11
|
}
|
|
12
12
|
async redirectToAuthorization() {
|
|
13
|
-
const target = this.agentHint ? `adcp
|
|
13
|
+
const target = this.agentHint ? `adcp ${this.agentHint} --oauth` : "adcp --save-auth <alias> <url> --oauth";
|
|
14
14
|
throw new OAuthError(
|
|
15
15
|
`OAuth authorization required but this context is non-interactive. Run \`${target}\` from a terminal with a browser, then retry.`,
|
|
16
16
|
"interactive_required"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* OAuth client credentials configuration shape. Secret references remain
|
|
4
|
+
* unresolved; endpoint transport and SSRF checks run during token exchange.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AgentOAuthClientCredentialsSchema: z.ZodObject<{
|
|
7
|
+
token_endpoint: z.ZodString;
|
|
8
|
+
client_id: z.ZodString;
|
|
9
|
+
client_secret: z.ZodString;
|
|
10
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
11
|
+
resource: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
12
|
+
audience: z.ZodOptional<z.ZodString>;
|
|
13
|
+
auth_method: z.ZodOptional<z.ZodEnum<{
|
|
14
|
+
basic: "basic";
|
|
15
|
+
body: "body";
|
|
16
|
+
}>>;
|
|
17
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* OAuth client credentials configuration shape. Secret references remain
|
|
4
|
+
* unresolved; endpoint transport and SSRF checks run during token exchange.
|
|
5
|
+
*/
|
|
6
|
+
export declare const AgentOAuthClientCredentialsSchema: z.ZodObject<{
|
|
7
|
+
token_endpoint: z.ZodString;
|
|
8
|
+
client_id: z.ZodString;
|
|
9
|
+
client_secret: z.ZodString;
|
|
10
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
11
|
+
resource: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
12
|
+
audience: z.ZodOptional<z.ZodString>;
|
|
13
|
+
auth_method: z.ZodOptional<z.ZodEnum<{
|
|
14
|
+
basic: "basic";
|
|
15
|
+
body: "body";
|
|
16
|
+
}>>;
|
|
17
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var client_credentials_schema_exports = {};
|
|
20
|
+
__export(client_credentials_schema_exports, {
|
|
21
|
+
AgentOAuthClientCredentialsSchema: () => AgentOAuthClientCredentialsSchema
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(client_credentials_schema_exports);
|
|
24
|
+
var import_zod = require("zod");
|
|
25
|
+
const AgentOAuthClientCredentialsSchema = import_zod.z.object({
|
|
26
|
+
token_endpoint: import_zod.z.string().url(),
|
|
27
|
+
client_id: import_zod.z.string().min(1),
|
|
28
|
+
client_secret: import_zod.z.string().min(1),
|
|
29
|
+
scope: import_zod.z.string().optional(),
|
|
30
|
+
resource: import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.string())]).optional(),
|
|
31
|
+
audience: import_zod.z.string().optional(),
|
|
32
|
+
auth_method: import_zod.z.enum(["basic", "body"]).optional()
|
|
33
|
+
});
|
|
34
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
35
|
+
0 && (module.exports = {
|
|
36
|
+
AgentOAuthClientCredentialsSchema
|
|
37
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const AgentOAuthClientCredentialsSchema = z.object({
|
|
3
|
+
token_endpoint: z.string().url(),
|
|
4
|
+
client_id: z.string().min(1),
|
|
5
|
+
client_secret: z.string().min(1),
|
|
6
|
+
scope: z.string().optional(),
|
|
7
|
+
resource: z.union([z.string(), z.array(z.string())]).optional(),
|
|
8
|
+
audience: z.string().optional(),
|
|
9
|
+
auth_method: z.enum(["basic", "body"]).optional()
|
|
10
|
+
});
|
|
11
|
+
export {
|
|
12
|
+
AgentOAuthClientCredentialsSchema
|
|
13
|
+
};
|
|
@@ -29,9 +29,9 @@ export interface FileOAuthStorageOptions {
|
|
|
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
31
|
* `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* An own property explicitly set to undefined clears tokens, client, verifier
|
|
33
|
+
* or discovery state. Omitted properties preserve stored values for partial
|
|
34
|
+
* callers. Other fields keep their existing merge semantics.
|
|
35
35
|
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```ts
|
|
@@ -29,9 +29,9 @@ export interface FileOAuthStorageOptions {
|
|
|
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
31
|
* `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* An own property explicitly set to undefined clears tokens, client, verifier
|
|
33
|
+
* or discovery state. Omitted properties preserve stored values for partial
|
|
34
|
+
* callers. Other fields keep their existing merge semantics.
|
|
35
35
|
*
|
|
36
36
|
* @example
|
|
37
37
|
* ```ts
|
|
@@ -94,11 +94,18 @@ function createFileOAuthStorage(options) {
|
|
|
94
94
|
...agent.oauth_client_credentials !== void 0 ? { oauth_client_credentials: agent.oauth_client_credentials } : {},
|
|
95
95
|
...agent.oauth_code_verifier !== void 0 ? { oauth_code_verifier: agent.oauth_code_verifier } : {}
|
|
96
96
|
};
|
|
97
|
-
if (agent.oauth_tokens === void 0)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (agent.
|
|
101
|
-
|
|
97
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_tokens") && agent.oauth_tokens === void 0) {
|
|
98
|
+
delete next.oauth_tokens;
|
|
99
|
+
}
|
|
100
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_client") && agent.oauth_client === void 0) {
|
|
101
|
+
delete next.oauth_client;
|
|
102
|
+
}
|
|
103
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_code_verifier") && agent.oauth_code_verifier === void 0) {
|
|
104
|
+
delete next.oauth_code_verifier;
|
|
105
|
+
}
|
|
106
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_discovery_state") && agent.oauth_discovery_state === void 0) {
|
|
107
|
+
delete next.oauth_discovery_state;
|
|
108
|
+
} else if (agent.oauth_discovery_state !== void 0) next.oauth_discovery_state = agent.oauth_discovery_state;
|
|
102
109
|
if (agent.oauth_resource === void 0) delete next.oauth_resource;
|
|
103
110
|
config.agents[key] = next;
|
|
104
111
|
await writeConfig(config);
|
|
@@ -61,11 +61,18 @@ function createFileOAuthStorage(options) {
|
|
|
61
61
|
...agent.oauth_client_credentials !== void 0 ? { oauth_client_credentials: agent.oauth_client_credentials } : {},
|
|
62
62
|
...agent.oauth_code_verifier !== void 0 ? { oauth_code_verifier: agent.oauth_code_verifier } : {}
|
|
63
63
|
};
|
|
64
|
-
if (agent.oauth_tokens === void 0)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (agent.
|
|
68
|
-
|
|
64
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_tokens") && agent.oauth_tokens === void 0) {
|
|
65
|
+
delete next.oauth_tokens;
|
|
66
|
+
}
|
|
67
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_client") && agent.oauth_client === void 0) {
|
|
68
|
+
delete next.oauth_client;
|
|
69
|
+
}
|
|
70
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_code_verifier") && agent.oauth_code_verifier === void 0) {
|
|
71
|
+
delete next.oauth_code_verifier;
|
|
72
|
+
}
|
|
73
|
+
if (Object.prototype.hasOwnProperty.call(agent, "oauth_discovery_state") && agent.oauth_discovery_state === void 0) {
|
|
74
|
+
delete next.oauth_discovery_state;
|
|
75
|
+
} else if (agent.oauth_discovery_state !== void 0) next.oauth_discovery_state = agent.oauth_discovery_state;
|
|
69
76
|
if (agent.oauth_resource === void 0) delete next.oauth_resource;
|
|
70
77
|
config.agents[key] = next;
|
|
71
78
|
await writeConfig(config);
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```typescript
|
|
8
|
-
* import { MCPOAuthProvider, CLIFlowHandler } from '@adcp/sdk';
|
|
8
|
+
* import { MCPOAuthProvider, CLIFlowHandler, DEFAULT_CLIENT_METADATA } from '@adcp/sdk/auth';
|
|
9
|
+
* import type { AgentConfig } from '@adcp/sdk';
|
|
9
10
|
*
|
|
10
11
|
* // Agent config - tokens will be stored here
|
|
11
12
|
* const agent: AgentConfig = {
|
|
@@ -13,14 +14,15 @@
|
|
|
13
14
|
* name: 'My Agent',
|
|
14
15
|
* agent_uri: 'https://agent.example.com/mcp',
|
|
15
16
|
* protocol: 'mcp',
|
|
16
|
-
* //
|
|
17
|
-
* // oauth_tokens: { access_token: '...', refresh_token: '...' }
|
|
17
|
+
* // A fresh interactive flow saves issuer-bound tokens here.
|
|
18
18
|
* };
|
|
19
19
|
*
|
|
20
20
|
* // Create provider with CLI flow handler
|
|
21
21
|
* const provider = new MCPOAuthProvider({
|
|
22
|
+
* allowInteractiveAuthorization: true,
|
|
22
23
|
* agent,
|
|
23
24
|
* flowHandler: new CLIFlowHandler(),
|
|
25
|
+
* clientMetadata: DEFAULT_CLIENT_METADATA,
|
|
24
26
|
* storage: myConfigStorage // Optional: persists tokens to file/db
|
|
25
27
|
* });
|
|
26
28
|
*
|
|
@@ -99,9 +101,10 @@ export declare function createCLIOAuthProvider(agent: AgentConfig, options?: {
|
|
|
99
101
|
* runs, scheduled jobs, and other non-interactive contexts where you've
|
|
100
102
|
* already saved tokens via `createCLIOAuthProvider`.
|
|
101
103
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
104
|
+
* A failed grant can throw `OAuthError` with `owner_reauthorization_required`;
|
|
105
|
+
* missing or mismatched issuer bindings throw `oauth_issuer_required` or `oauth_issuer_mismatch`.
|
|
106
|
+
* The owner can run `adcp <alias> --clear-oauth`, then
|
|
107
|
+
* `adcp <alias> --oauth` to start a fresh browser sign-in.
|
|
105
108
|
*/
|
|
106
109
|
export declare function createNonInteractiveOAuthProvider(agent: AgentConfig, options?: {
|
|
107
110
|
/** Hint shown in error messages if an interactive flow is attempted. */
|
|
@@ -143,5 +146,6 @@ export { createFileOAuthStorage, type FileOAuthStorageOptions } from './file-sto
|
|
|
143
146
|
export { exchangeClientCredentials, ensureClientCredentialsTokens, ClientCredentialsExchangeError, type ExchangeClientCredentialsOptions, type EnsureClientCredentialsOptions, } from './ClientCredentialsFlow.mjs';
|
|
144
147
|
export { resolveSecret, isEnvSecretReference, toEnvSecretReference, extractEnvSecretName, MissingEnvSecretError, } from './secret-resolver.mjs';
|
|
145
148
|
export type { AgentOAuthClientCredentials } from '../../types/adcp.mjs';
|
|
149
|
+
export { AgentOAuthClientCredentialsSchema } from './client-credentials-schema.mjs';
|
|
146
150
|
export { bindAgentStorage, getAgentStorage, unbindAgentStorage } from './storage-registry.mjs';
|
|
147
151
|
export { InvalidTokenError, InsufficientScopeError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @example
|
|
7
7
|
* ```typescript
|
|
8
|
-
* import { MCPOAuthProvider, CLIFlowHandler } from '@adcp/sdk';
|
|
8
|
+
* import { MCPOAuthProvider, CLIFlowHandler, DEFAULT_CLIENT_METADATA } from '@adcp/sdk/auth';
|
|
9
|
+
* import type { AgentConfig } from '@adcp/sdk';
|
|
9
10
|
*
|
|
10
11
|
* // Agent config - tokens will be stored here
|
|
11
12
|
* const agent: AgentConfig = {
|
|
@@ -13,14 +14,15 @@
|
|
|
13
14
|
* name: 'My Agent',
|
|
14
15
|
* agent_uri: 'https://agent.example.com/mcp',
|
|
15
16
|
* protocol: 'mcp',
|
|
16
|
-
* //
|
|
17
|
-
* // oauth_tokens: { access_token: '...', refresh_token: '...' }
|
|
17
|
+
* // A fresh interactive flow saves issuer-bound tokens here.
|
|
18
18
|
* };
|
|
19
19
|
*
|
|
20
20
|
* // Create provider with CLI flow handler
|
|
21
21
|
* const provider = new MCPOAuthProvider({
|
|
22
|
+
* allowInteractiveAuthorization: true,
|
|
22
23
|
* agent,
|
|
23
24
|
* flowHandler: new CLIFlowHandler(),
|
|
25
|
+
* clientMetadata: DEFAULT_CLIENT_METADATA,
|
|
24
26
|
* storage: myConfigStorage // Optional: persists tokens to file/db
|
|
25
27
|
* });
|
|
26
28
|
*
|
|
@@ -99,9 +101,10 @@ export declare function createCLIOAuthProvider(agent: AgentConfig, options?: {
|
|
|
99
101
|
* runs, scheduled jobs, and other non-interactive contexts where you've
|
|
100
102
|
* already saved tokens via `createCLIOAuthProvider`.
|
|
101
103
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
104
|
+
* A failed grant can throw `OAuthError` with `owner_reauthorization_required`;
|
|
105
|
+
* missing or mismatched issuer bindings throw `oauth_issuer_required` or `oauth_issuer_mismatch`.
|
|
106
|
+
* The owner can run `adcp <alias> --clear-oauth`, then
|
|
107
|
+
* `adcp <alias> --oauth` to start a fresh browser sign-in.
|
|
105
108
|
*/
|
|
106
109
|
export declare function createNonInteractiveOAuthProvider(agent: AgentConfig, options?: {
|
|
107
110
|
/** Hint shown in error messages if an interactive flow is attempted. */
|
|
@@ -143,5 +146,6 @@ export { createFileOAuthStorage, type FileOAuthStorageOptions } from './file-sto
|
|
|
143
146
|
export { exchangeClientCredentials, ensureClientCredentialsTokens, ClientCredentialsExchangeError, type ExchangeClientCredentialsOptions, type EnsureClientCredentialsOptions, } from './ClientCredentialsFlow';
|
|
144
147
|
export { resolveSecret, isEnvSecretReference, toEnvSecretReference, extractEnvSecretName, MissingEnvSecretError, } from './secret-resolver';
|
|
145
148
|
export type { AgentOAuthClientCredentials } from '../../types/adcp';
|
|
149
|
+
export { AgentOAuthClientCredentialsSchema } from './client-credentials-schema';
|
|
146
150
|
export { bindAgentStorage, getAgentStorage, unbindAgentStorage } from './storage-registry';
|
|
147
151
|
export { InvalidTokenError, InsufficientScopeError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
@@ -20,6 +20,7 @@ var oauth_exports = {};
|
|
|
20
20
|
__export(oauth_exports, {
|
|
21
21
|
AUTH_DIAGNOSIS_SCHEMA_VERSION: () => import_diagnose.AUTH_DIAGNOSIS_SCHEMA_VERSION,
|
|
22
22
|
AgentChangedDuringFlowError: () => import_web_flow.AgentChangedDuringFlowError,
|
|
23
|
+
AgentOAuthClientCredentialsSchema: () => import_client_credentials_schema.AgentOAuthClientCredentialsSchema,
|
|
23
24
|
AgentVanishedDuringFlowError: () => import_web_flow.AgentVanishedDuringFlowError,
|
|
24
25
|
BrowserBindingRequiredError: () => import_web_flow.BrowserBindingRequiredError,
|
|
25
26
|
CLIFlowHandler: () => import_CLIFlowHandler.CLIFlowHandler,
|
|
@@ -92,6 +93,7 @@ var import_authorization_required = require('./authorization-required.js');
|
|
|
92
93
|
var import_file_storage = require('./file-storage.js');
|
|
93
94
|
var import_ClientCredentialsFlow = require('./ClientCredentialsFlow.js');
|
|
94
95
|
var import_secret_resolver = require('./secret-resolver.js');
|
|
96
|
+
var import_client_credentials_schema = require('./client-credentials-schema.js');
|
|
95
97
|
var import_storage_registry = require('./storage-registry.js');
|
|
96
98
|
var import_errors = require("@modelcontextprotocol/sdk/server/auth/errors.js");
|
|
97
99
|
function createCLIOAuthProvider(agent, options) {
|
|
@@ -107,6 +109,7 @@ function createCLIOAuthProvider(agent, options) {
|
|
|
107
109
|
...options?.clientMetadata
|
|
108
110
|
};
|
|
109
111
|
return new import_MCPOAuthProvider2.MCPOAuthProvider({
|
|
112
|
+
allowInteractiveAuthorization: true,
|
|
110
113
|
agent,
|
|
111
114
|
flowHandler,
|
|
112
115
|
storage: options?.storage,
|
|
@@ -123,6 +126,7 @@ function createNonInteractiveOAuthProvider(agent, options) {
|
|
|
123
126
|
...options?.clientMetadata
|
|
124
127
|
};
|
|
125
128
|
return new import_MCPOAuthProvider2.MCPOAuthProvider({
|
|
129
|
+
allowInteractiveAuthorization: false,
|
|
126
130
|
agent,
|
|
127
131
|
flowHandler,
|
|
128
132
|
storage: options?.storage,
|
|
@@ -153,9 +157,10 @@ function hasValidOAuthTokens(agent) {
|
|
|
153
157
|
return true;
|
|
154
158
|
}
|
|
155
159
|
function clearOAuthTokens(agent) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
agent.oauth_tokens = void 0;
|
|
161
|
+
agent.oauth_client = void 0;
|
|
162
|
+
agent.oauth_code_verifier = void 0;
|
|
163
|
+
agent.oauth_discovery_state = void 0;
|
|
159
164
|
}
|
|
160
165
|
function getEffectiveAuthToken(agent) {
|
|
161
166
|
if (hasValidOAuthTokens(agent)) {
|
|
@@ -167,6 +172,7 @@ function getEffectiveAuthToken(agent) {
|
|
|
167
172
|
0 && (module.exports = {
|
|
168
173
|
AUTH_DIAGNOSIS_SCHEMA_VERSION,
|
|
169
174
|
AgentChangedDuringFlowError,
|
|
175
|
+
AgentOAuthClientCredentialsSchema,
|
|
170
176
|
AgentVanishedDuringFlowError,
|
|
171
177
|
BrowserBindingRequiredError,
|
|
172
178
|
CLIFlowHandler,
|
|
@@ -43,6 +43,7 @@ function createCLIOAuthProvider(agent, options) {
|
|
|
43
43
|
...options?.clientMetadata
|
|
44
44
|
};
|
|
45
45
|
return new MCPOAuthProvider2({
|
|
46
|
+
allowInteractiveAuthorization: true,
|
|
46
47
|
agent,
|
|
47
48
|
flowHandler,
|
|
48
49
|
storage: options?.storage,
|
|
@@ -59,6 +60,7 @@ function createNonInteractiveOAuthProvider(agent, options) {
|
|
|
59
60
|
...options?.clientMetadata
|
|
60
61
|
};
|
|
61
62
|
return new MCPOAuthProvider2({
|
|
63
|
+
allowInteractiveAuthorization: false,
|
|
62
64
|
agent,
|
|
63
65
|
flowHandler,
|
|
64
66
|
storage: options?.storage,
|
|
@@ -89,9 +91,10 @@ function hasValidOAuthTokens(agent) {
|
|
|
89
91
|
return true;
|
|
90
92
|
}
|
|
91
93
|
function clearOAuthTokens(agent) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
agent.oauth_tokens = void 0;
|
|
95
|
+
agent.oauth_client = void 0;
|
|
96
|
+
agent.oauth_code_verifier = void 0;
|
|
97
|
+
agent.oauth_discovery_state = void 0;
|
|
95
98
|
}
|
|
96
99
|
function getEffectiveAuthToken(agent) {
|
|
97
100
|
if (hasValidOAuthTokens(agent)) {
|
|
@@ -132,11 +135,13 @@ import {
|
|
|
132
135
|
extractEnvSecretName,
|
|
133
136
|
MissingEnvSecretError
|
|
134
137
|
} from "./secret-resolver.mjs";
|
|
138
|
+
import { AgentOAuthClientCredentialsSchema } from "./client-credentials-schema.mjs";
|
|
135
139
|
import { bindAgentStorage, getAgentStorage, unbindAgentStorage } from "./storage-registry.mjs";
|
|
136
140
|
import { InvalidTokenError, InsufficientScopeError } from "@modelcontextprotocol/sdk/server/auth/errors.js";
|
|
137
141
|
export {
|
|
138
142
|
AUTH_DIAGNOSIS_SCHEMA_VERSION,
|
|
139
143
|
AgentChangedDuringFlowError,
|
|
144
|
+
AgentOAuthClientCredentialsSchema,
|
|
140
145
|
AgentVanishedDuringFlowError,
|
|
141
146
|
BrowserBindingRequiredError,
|
|
142
147
|
CLIFlowHandler,
|
|
@@ -13,8 +13,10 @@ 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
|
-
*
|
|
16
|
+
* Round-trip pending `oauth_code_verifier` and `oauth_discovery_state` verbatim.
|
|
17
|
+
* For oauth_tokens, oauth_client, oauth_code_verifier and oauth_discovery_state,
|
|
18
|
+
* an own undefined property clears; omission preserves the saved field.
|
|
19
|
+
* Resource overrides and other fields retain the storage backend's semantics.
|
|
18
20
|
* This allows different storage backends (file, database, memory)
|
|
19
21
|
* while keeping tokens in the agent config structure.
|
|
20
22
|
*/
|
|
@@ -76,6 +78,11 @@ export interface OAuthProviderConfig {
|
|
|
76
78
|
storage?: OAuthConfigStorage;
|
|
77
79
|
/** Authorization flow handler */
|
|
78
80
|
flowHandler: OAuthFlowHandler;
|
|
81
|
+
/**
|
|
82
|
+
* Explicitly allow fresh registration, PKCE work, redirects and automatic
|
|
83
|
+
* credential invalidation. Defaults to false; CLI factories opt in.
|
|
84
|
+
*/
|
|
85
|
+
allowInteractiveAuthorization?: boolean;
|
|
79
86
|
/** OAuth client metadata (required - use DEFAULT_CLIENT_METADATA as base) */
|
|
80
87
|
clientMetadata: OAuthClientMetadata;
|
|
81
88
|
/**
|
|
@@ -13,8 +13,10 @@ 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
|
-
*
|
|
16
|
+
* Round-trip pending `oauth_code_verifier` and `oauth_discovery_state` verbatim.
|
|
17
|
+
* For oauth_tokens, oauth_client, oauth_code_verifier and oauth_discovery_state,
|
|
18
|
+
* an own undefined property clears; omission preserves the saved field.
|
|
19
|
+
* Resource overrides and other fields retain the storage backend's semantics.
|
|
18
20
|
* This allows different storage backends (file, database, memory)
|
|
19
21
|
* while keeping tokens in the agent config structure.
|
|
20
22
|
*/
|
|
@@ -76,6 +78,11 @@ export interface OAuthProviderConfig {
|
|
|
76
78
|
storage?: OAuthConfigStorage;
|
|
77
79
|
/** Authorization flow handler */
|
|
78
80
|
flowHandler: OAuthFlowHandler;
|
|
81
|
+
/**
|
|
82
|
+
* Explicitly allow fresh registration, PKCE work, redirects and automatic
|
|
83
|
+
* credential invalidation. Defaults to false; CLI factories opt in.
|
|
84
|
+
*/
|
|
85
|
+
allowInteractiveAuthorization?: boolean;
|
|
79
86
|
/** OAuth client metadata (required - use DEFAULT_CLIENT_METADATA as base) */
|
|
80
87
|
clientMetadata: OAuthClientMetadata;
|
|
81
88
|
/**
|
|
@@ -103,10 +103,11 @@ function fromMCPClientInfo(info) {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
function isOAuthIssuer(value) {
|
|
106
|
-
if (typeof value !== "string" || value
|
|
106
|
+
if (typeof value !== "string" || !value || /[\u0000-\u0020\u007f\\]/.test(value)) return false;
|
|
107
107
|
try {
|
|
108
108
|
const url = new URL(value);
|
|
109
|
-
|
|
109
|
+
const rawAuthority = value.match(/^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i)?.[1];
|
|
110
|
+
return (url.protocol === "https:" || url.protocol === "http:") && !rawAuthority?.includes("@") && !url.username && !url.password && !url.search && !url.hash;
|
|
110
111
|
} catch {
|
|
111
112
|
return false;
|
|
112
113
|
}
|
|
@@ -71,10 +71,11 @@ function fromMCPClientInfo(info) {
|
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
function isOAuthIssuer(value) {
|
|
74
|
-
if (typeof value !== "string" || value
|
|
74
|
+
if (typeof value !== "string" || !value || /[\u0000-\u0020\u007f\\]/.test(value)) return false;
|
|
75
75
|
try {
|
|
76
76
|
const url = new URL(value);
|
|
77
|
-
|
|
77
|
+
const rawAuthority = value.match(/^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i)?.[1];
|
|
78
|
+
return (url.protocol === "https:" || url.protocol === "http:") && !rawAuthority?.includes("@") && !url.username && !url.password && !url.search && !url.hash;
|
|
78
79
|
} catch {
|
|
79
80
|
return false;
|
|
80
81
|
}
|
|
@@ -66,6 +66,8 @@ export interface PendingWebFlow {
|
|
|
66
66
|
resourceOverrideSnapshot?: string | null;
|
|
67
67
|
scope?: string;
|
|
68
68
|
authorizationServerUrl: string;
|
|
69
|
+
/** Validated AS issuer frozen at start. Legacy rows without it cannot spend credentials. */
|
|
70
|
+
authorizationServerIssuer?: string;
|
|
69
71
|
/** Persist as JSON (e.g. Postgres `jsonb`); contents are MCP SDK-typed. */
|
|
70
72
|
clientInformation: OAuthClientInformation;
|
|
71
73
|
createdAt: Date;
|
|
@@ -66,6 +66,8 @@ export interface PendingWebFlow {
|
|
|
66
66
|
resourceOverrideSnapshot?: string | null;
|
|
67
67
|
scope?: string;
|
|
68
68
|
authorizationServerUrl: string;
|
|
69
|
+
/** Validated AS issuer frozen at start. Legacy rows without it cannot spend credentials. */
|
|
70
|
+
authorizationServerIssuer?: string;
|
|
69
71
|
/** Persist as JSON (e.g. Postgres `jsonb`); contents are MCP SDK-typed. */
|
|
70
72
|
clientInformation: OAuthClientInformation;
|
|
71
73
|
createdAt: Date;
|
|
@@ -145,6 +145,8 @@ async function startWebOAuthFlow(opts) {
|
|
|
145
145
|
throw new import_types.OAuthError(`No OAuth metadata at ${asUrl.toString()}`, "no_authorization_server_metadata", agent.id);
|
|
146
146
|
}
|
|
147
147
|
(0, import_types.assertOAuthServerIssuer)(asMetadata, asUrl.toString());
|
|
148
|
+
const authorizationServerIssuer = asUrl.toString();
|
|
149
|
+
assertSavedCredentialIssuers(agent, authorizationServerIssuer);
|
|
148
150
|
const resource = resourceOverride ? new URL(resourceOverride) : prm?.resource ? new URL(prm.resource) : (0, import_auth_utils.resourceUrlFromServerUrl)(new URL(agent.agent_uri));
|
|
149
151
|
const baseClientMetadata = {
|
|
150
152
|
...import_types.DEFAULT_CLIENT_METADATA,
|
|
@@ -159,7 +161,8 @@ async function startWebOAuthFlow(opts) {
|
|
|
159
161
|
asMetadata,
|
|
160
162
|
clientMetadata: baseClientMetadata,
|
|
161
163
|
fetchFn: guardedFetch,
|
|
162
|
-
allowConfidentialClient
|
|
164
|
+
allowConfidentialClient,
|
|
165
|
+
issuer: authorizationServerIssuer
|
|
163
166
|
});
|
|
164
167
|
const state = (generateState ?? defaultGenerateState)();
|
|
165
168
|
const { authorizationUrl, codeVerifier } = await (0, import_auth.startAuthorization)(asUrl.toString(), {
|
|
@@ -187,6 +190,7 @@ async function startWebOAuthFlow(opts) {
|
|
|
187
190
|
resourceOverrideSnapshot: agent.oauth_resource ?? null,
|
|
188
191
|
scope,
|
|
189
192
|
authorizationServerUrl: asUrl.toString(),
|
|
193
|
+
authorizationServerIssuer,
|
|
190
194
|
clientInformation,
|
|
191
195
|
createdAt: now,
|
|
192
196
|
expiresAt,
|
|
@@ -232,11 +236,16 @@ async function completeWebOAuthFlow(opts) {
|
|
|
232
236
|
fetchFn: guardedFetch
|
|
233
237
|
});
|
|
234
238
|
(0, import_types.assertOAuthServerIssuer)(asMetadata, flow.authorizationServerUrl);
|
|
239
|
+
assertFrozenIssuer(flow);
|
|
235
240
|
(0, import_types.assertOAuthCredentialIssuer)(
|
|
236
241
|
flow.clientInformation,
|
|
237
|
-
flow.
|
|
242
|
+
flow.authorizationServerIssuer,
|
|
238
243
|
!!flow.clientInformation.client_secret
|
|
239
244
|
);
|
|
245
|
+
if (agentStorage) {
|
|
246
|
+
agentForPersistence = await agentStorage.loadAgent(flow.agentId);
|
|
247
|
+
assertAgentUnchangedForFlow(agentForPersistence, flow);
|
|
248
|
+
}
|
|
240
249
|
let tokens;
|
|
241
250
|
try {
|
|
242
251
|
tokens = await (0, import_auth.exchangeAuthorization)(flow.authorizationServerUrl, {
|
|
@@ -248,7 +257,7 @@ async function completeWebOAuthFlow(opts) {
|
|
|
248
257
|
resource: flow.resource ? new URL(flow.resource) : void 0,
|
|
249
258
|
fetchFn: guardedFetch
|
|
250
259
|
});
|
|
251
|
-
tokens = { ...tokens, issuer: flow.
|
|
260
|
+
tokens = { ...tokens, issuer: flow.authorizationServerIssuer };
|
|
252
261
|
} catch (err) {
|
|
253
262
|
throw wrapTokenExchangeError(err);
|
|
254
263
|
}
|
|
@@ -262,7 +271,8 @@ async function completeWebOAuthFlow(opts) {
|
|
|
262
271
|
} else if (flow.resourceOverrideAction === "clear") {
|
|
263
272
|
delete agentForPersistence.oauth_resource;
|
|
264
273
|
}
|
|
265
|
-
|
|
274
|
+
agentForPersistence.oauth_code_verifier = void 0;
|
|
275
|
+
agentForPersistence.oauth_discovery_state = void 0;
|
|
266
276
|
await agentStorage.saveAgent(agentForPersistence);
|
|
267
277
|
persisted = true;
|
|
268
278
|
}
|
|
@@ -274,6 +284,20 @@ async function completeWebOAuthFlow(opts) {
|
|
|
274
284
|
persisted
|
|
275
285
|
};
|
|
276
286
|
}
|
|
287
|
+
function assertSavedCredentialIssuers(agent, issuer) {
|
|
288
|
+
if (agent.oauth_tokens) (0, import_types.assertOAuthCredentialIssuer)(agent.oauth_tokens, issuer, !!agent.oauth_tokens.refresh_token);
|
|
289
|
+
if (agent.oauth_client) (0, import_types.assertOAuthCredentialIssuer)(agent.oauth_client, issuer, !!agent.oauth_client.client_secret);
|
|
290
|
+
}
|
|
291
|
+
function assertFrozenIssuer(flow) {
|
|
292
|
+
if (!flow.authorizationServerIssuer) {
|
|
293
|
+
throw new import_types.OAuthError(
|
|
294
|
+
"Pending OAuth flow lacks a validated authorization-server issuer. Start a new owner sign-in.",
|
|
295
|
+
"oauth_issuer_required",
|
|
296
|
+
flow.agentId
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
(0, import_types.assertOAuthServerIssuer)({ issuer: flow.authorizationServerIssuer }, flow.authorizationServerUrl);
|
|
300
|
+
}
|
|
277
301
|
function assertAgentUnchangedForFlow(agent, flow) {
|
|
278
302
|
if (!agent) {
|
|
279
303
|
throw new AgentVanishedDuringFlowError(flow.agentId);
|
|
@@ -281,6 +305,13 @@ function assertAgentUnchangedForFlow(agent, flow) {
|
|
|
281
305
|
if (agent.agent_uri !== flow.agentUrl) {
|
|
282
306
|
throw new AgentChangedDuringFlowError(flow.agentId);
|
|
283
307
|
}
|
|
308
|
+
if (!agent.oauth_client) throw new AgentChangedDuringFlowError(flow.agentId);
|
|
309
|
+
const currentClient = (0, import_types.toMCPClientInfo)(agent.oauth_client);
|
|
310
|
+
for (const key of ["client_id", "client_secret", "client_secret_expires_at", "issuer"]) {
|
|
311
|
+
if (currentClient[key] !== flow.clientInformation[key]) throw new AgentChangedDuringFlowError(flow.agentId);
|
|
312
|
+
}
|
|
313
|
+
assertFrozenIssuer(flow);
|
|
314
|
+
assertSavedCredentialIssuers(agent, flow.authorizationServerIssuer);
|
|
284
315
|
if (Object.prototype.hasOwnProperty.call(flow, "resourceOverrideSnapshot") && (agent.oauth_resource ?? null) !== flow.resourceOverrideSnapshot) {
|
|
285
316
|
throw new AgentChangedDuringFlowError(flow.agentId);
|
|
286
317
|
}
|
|
@@ -416,9 +447,8 @@ function createSsrfSafeOAuthFetch(trustedFetchFn, allowHttp) {
|
|
|
416
447
|
});
|
|
417
448
|
}
|
|
418
449
|
async function resolveClientInformation(args) {
|
|
419
|
-
const { agent, agentStorage, asUrl, asMetadata, clientMetadata, fetchFn, allowConfidentialClient } = args;
|
|
450
|
+
const { agent, agentStorage, asUrl, asMetadata, clientMetadata, fetchFn, allowConfidentialClient, issuer } = args;
|
|
420
451
|
if (agent.oauth_client) {
|
|
421
|
-
(0, import_types.assertOAuthCredentialIssuer)(agent.oauth_client, asUrl.toString(), !!agent.oauth_client.client_secret);
|
|
422
452
|
return (0, import_types.toMCPClientInfo)(agent.oauth_client);
|
|
423
453
|
}
|
|
424
454
|
if (!asMetadata.registration_endpoint) {
|
|
@@ -428,17 +458,20 @@ async function resolveClientInformation(args) {
|
|
|
428
458
|
agent.id
|
|
429
459
|
);
|
|
430
460
|
}
|
|
431
|
-
const
|
|
461
|
+
const response = await (0, import_auth.registerClient)(asUrl.toString(), {
|
|
432
462
|
metadata: asMetadata,
|
|
433
463
|
clientMetadata,
|
|
434
464
|
fetchFn
|
|
435
465
|
});
|
|
436
|
-
const registered = { ...
|
|
466
|
+
const registered = { ...response, issuer };
|
|
437
467
|
if (registered.client_secret && !allowConfidentialClient) {
|
|
438
468
|
throw new ConfidentialClientNotAllowedError(agent.id);
|
|
439
469
|
}
|
|
440
470
|
if (agentStorage) {
|
|
441
|
-
const fresh = await agentStorage.loadAgent(agent.id)
|
|
471
|
+
const fresh = await agentStorage.loadAgent(agent.id);
|
|
472
|
+
if (!fresh) throw new AgentVanishedDuringFlowError(agent.id);
|
|
473
|
+
if (fresh.agent_uri !== agent.agent_uri || fresh.oauth_client) throw new AgentChangedDuringFlowError(agent.id);
|
|
474
|
+
assertSavedCredentialIssuers(fresh, issuer);
|
|
442
475
|
fresh.oauth_client = (0, import_types.fromMCPClientInfo)(registered);
|
|
443
476
|
await agentStorage.saveAgent(fresh);
|
|
444
477
|
}
|