@adcp/sdk 15.0.0 → 15.2.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-grade.js +46 -2
- package/bin/adcp.js +7 -2
- package/dist/lib/canonical-references/index.d.mts +19 -1
- package/dist/lib/canonical-references/index.d.ts +19 -1
- package/dist/lib/canonical-references/index.js +81 -9
- package/dist/lib/canonical-references/index.mjs +80 -9
- package/dist/lib/client/core.d.mts +1 -0
- package/dist/lib/client/core.d.ts +1 -0
- package/dist/lib/core/AgentClient.d.mts +9 -1
- package/dist/lib/core/AgentClient.d.ts +9 -1
- package/dist/lib/core/AgentClient.js +10 -0
- package/dist/lib/core/AgentClient.mjs +10 -0
- package/dist/lib/core/SingleAgentClient.d.mts +13 -2
- package/dist/lib/core/SingleAgentClient.d.ts +13 -2
- package/dist/lib/core/SingleAgentClient.js +23 -5
- package/dist/lib/core/SingleAgentClient.mjs +23 -5
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/reporting/gcs/adapter.d.mts +31 -0
- package/dist/lib/reporting/gcs/adapter.d.ts +31 -0
- package/dist/lib/reporting/gcs/adapter.js +342 -0
- package/dist/lib/reporting/gcs/adapter.mjs +325 -0
- package/dist/lib/reporting/gcs/bucket-policy.d.mts +3 -0
- package/dist/lib/reporting/gcs/bucket-policy.d.ts +3 -0
- package/dist/lib/reporting/gcs/bucket-policy.js +31 -0
- package/dist/lib/reporting/gcs/bucket-policy.mjs +7 -0
- package/dist/lib/reporting/gcs/errors.d.mts +5 -0
- package/dist/lib/reporting/gcs/errors.d.ts +5 -0
- package/dist/lib/reporting/gcs/errors.js +41 -0
- package/dist/lib/reporting/gcs/errors.mjs +16 -0
- package/dist/lib/reporting/gcs/fence.d.mts +36 -0
- package/dist/lib/reporting/gcs/fence.d.ts +36 -0
- package/dist/lib/reporting/gcs/fence.js +250 -0
- package/dist/lib/reporting/gcs/fence.mjs +230 -0
- package/dist/lib/reporting/gcs/index.d.mts +7 -0
- package/dist/lib/reporting/gcs/index.d.ts +7 -0
- package/dist/lib/reporting/gcs/index.js +41 -0
- package/dist/lib/reporting/gcs/index.mjs +12 -0
- package/dist/lib/reporting/gcs/io.d.mts +7 -0
- package/dist/lib/reporting/gcs/io.d.ts +7 -0
- package/dist/lib/reporting/gcs/io.js +95 -0
- package/dist/lib/reporting/gcs/io.mjs +69 -0
- package/dist/lib/reporting/gcs/reader.d.mts +43 -0
- package/dist/lib/reporting/gcs/reader.d.ts +43 -0
- package/dist/lib/reporting/gcs/reader.js +193 -0
- package/dist/lib/reporting/gcs/reader.mjs +169 -0
- package/dist/lib/reporting/gcs/validation.d.mts +5 -0
- package/dist/lib/reporting/gcs/validation.d.ts +5 -0
- package/dist/lib/reporting/gcs/validation.js +49 -0
- package/dist/lib/reporting/gcs/validation.mjs +25 -0
- package/dist/lib/reporting/ledger/handler.d.mts +1 -0
- package/dist/lib/reporting/ledger/handler.d.ts +1 -0
- package/dist/lib/reporting/ledger/index.d.mts +1 -0
- package/dist/lib/reporting/ledger/index.d.ts +1 -0
- package/dist/lib/reporting/ledger/index.js +2 -0
- package/dist/lib/reporting/ledger/index.mjs +1 -0
- package/dist/lib/reporting/ledger/managed-postgres.d.mts +16 -1
- package/dist/lib/reporting/ledger/managed-postgres.d.ts +16 -1
- package/dist/lib/reporting/ledger/managed-postgres.js +243 -2
- package/dist/lib/reporting/ledger/managed-postgres.mjs +248 -2
- package/dist/lib/reporting/ledger/object-writes.d.mts +69 -0
- package/dist/lib/reporting/ledger/object-writes.d.ts +69 -0
- package/dist/lib/reporting/ledger/object-writes.js +147 -0
- package/dist/lib/reporting/ledger/object-writes.mjs +114 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.d.mts +50 -1
- package/dist/lib/server/a2a-adapter.d.ts +50 -1
- package/dist/lib/server/a2a-adapter.js +230 -7
- package/dist/lib/server/a2a-adapter.mjs +237 -7
- package/dist/lib/server/auth-signature.d.mts +84 -46
- package/dist/lib/server/auth-signature.d.ts +84 -46
- package/dist/lib/server/auth-signature.js +48 -14
- package/dist/lib/server/auth-signature.mjs +58 -15
- package/dist/lib/server/create-adcp-server.js +5 -16
- package/dist/lib/server/create-adcp-server.mjs +5 -16
- package/dist/lib/server/index.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +3 -1
- package/dist/lib/signing/agent-fetch.js +3 -33
- package/dist/lib/signing/agent-fetch.mjs +3 -33
- package/dist/lib/signing/middleware.d.mts +9 -1
- package/dist/lib/signing/middleware.d.ts +9 -1
- package/dist/lib/signing/operation-resolution.d.mts +37 -0
- package/dist/lib/signing/operation-resolution.d.ts +37 -0
- package/dist/lib/signing/operation-resolution.js +154 -0
- package/dist/lib/signing/operation-resolution.mjs +125 -0
- package/dist/lib/signing/postgres-replay-store.d.mts +1 -1
- package/dist/lib/signing/postgres-replay-store.d.ts +1 -1
- package/dist/lib/signing/redis-replay-store.d.mts +1 -1
- package/dist/lib/signing/redis-replay-store.d.ts +1 -1
- package/dist/lib/signing/server.d.mts +1 -0
- package/dist/lib/signing/server.d.ts +1 -0
- package/dist/lib/signing/server.js +7 -0
- package/dist/lib/signing/server.mjs +8 -0
- package/dist/lib/signing/verifier.d.mts +36 -1
- package/dist/lib/signing/verifier.d.ts +36 -1
- package/dist/lib/signing/verifier.js +66 -34
- package/dist/lib/signing/verifier.mjs +61 -34
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.mts +12 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.d.ts +12 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.js +29 -2
- package/dist/lib/testing/storyboard/request-signing/a2a-dispatch.mjs +27 -1
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.mts +61 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.d.ts +61 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.js +298 -0
- package/dist/lib/testing/storyboard/request-signing/a2a-operation-resolution.mjs +276 -0
- package/dist/lib/testing/storyboard/request-signing/grader.d.mts +31 -1
- package/dist/lib/testing/storyboard/request-signing/grader.d.ts +31 -1
- package/dist/lib/testing/storyboard/request-signing/grader.js +11 -0
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +11 -0
- package/dist/lib/testing/storyboard/request-signing/index.d.mts +5 -4
- package/dist/lib/testing/storyboard/request-signing/index.d.ts +5 -4
- package/dist/lib/testing/storyboard/request-signing/index.js +13 -0
- package/dist/lib/testing/storyboard/request-signing/index.mjs +18 -2
- package/dist/lib/testing/storyboard/request-signing/types.d.mts +43 -0
- package/dist/lib/testing/storyboard/request-signing/types.d.ts +43 -0
- package/dist/lib/testing/storyboard/request-signing/types.js +3 -0
- package/dist/lib/testing/storyboard/request-signing/types.mjs +2 -0
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.mts +42 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.d.ts +42 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.js +100 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +104 -3
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -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 +2 -2
- package/docs/guides/BUILD-AN-AGENT.md +9 -4
- package/docs/guides/CREATIVE-DELIVERY.md +34 -0
- package/docs/guides/REPORTING-GCS-FENCE.md +52 -0
- package/docs/guides/REPORTING-GCS-MANAGED.md +56 -0
- package/docs/guides/REPORTING-OPERATIONS.md +4 -0
- package/docs/guides/SIGNING-GUIDE.md +20 -6
- package/docs/guides/VALIDATE-YOUR-AGENT.md +4 -0
- package/docs/llms.txt +2 -2
- package/docs/migration-14.x-rc-worksheet.md +5 -4
- package/docs/migration-buyer-agent-registry.md +1 -1
- package/package.json +19 -1
- package/skills/build-seller-agent/specialisms/signed-requests.md +2 -0
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* verifyApiKey,
|
|
20
20
|
* anyOf,
|
|
21
21
|
* verifySignatureAsAuthenticator,
|
|
22
|
-
*
|
|
22
|
+
* adcpOperationResolver,
|
|
23
23
|
* } from '@adcp/sdk/server';
|
|
24
24
|
*
|
|
25
25
|
* serve(createAgent, {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* verifySignatureAsAuthenticator({
|
|
29
29
|
* jwks, replayStore, revocationStore,
|
|
30
30
|
* capability: { supported: true, required_for: [], covers_content_digest: 'either' },
|
|
31
|
-
* resolveOperation:
|
|
31
|
+
* resolveOperation: adcpOperationResolver,
|
|
32
32
|
* }),
|
|
33
33
|
* ),
|
|
34
34
|
* });
|
|
@@ -42,6 +42,7 @@ import type { JwksResolver } from '../signing/jwks.mjs';
|
|
|
42
42
|
import { type ReplayStore } from '../signing/replay.mjs';
|
|
43
43
|
import { type RevocationStore } from '../signing/revocation.mjs';
|
|
44
44
|
import type { VerifiedSigner, VerifierCapability } from '../signing/types.mjs';
|
|
45
|
+
import { type OperationResolutionInput, type ResolvedOperation } from '../signing/operation-resolution.mjs';
|
|
45
46
|
import { type AuthPrincipal, type Authenticator } from './auth.mjs';
|
|
46
47
|
export interface VerifySignatureAsAuthenticatorOptions {
|
|
47
48
|
/** Verifier capability block. `required_for` is not enforced here — unsigned
|
|
@@ -82,17 +83,22 @@ export interface VerifySignatureAsAuthenticatorOptions {
|
|
|
82
83
|
* before this runs. Same semantics as
|
|
83
84
|
* `ExpressMiddlewareOptions.resolveOperation` on {@link createExpressVerifier}.
|
|
84
85
|
*
|
|
86
|
+
* Pass {@link adcpOperationResolver}: it resolves MCP `tools/call` and A2A
|
|
87
|
+
* `SendMessage` / `message/send` requests, and reports a body that does not
|
|
88
|
+
* resolve to exactly one operation as unresolvable, which the verifier
|
|
89
|
+
* rejects with `request_body_malformed`.
|
|
90
|
+
*
|
|
85
91
|
* SECURITY: a resolver that always returns `undefined` disables
|
|
86
|
-
* `capability.required_for` enforcement for this authenticator.
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
+
* `capability.required_for` enforcement for this authenticator. This adapter
|
|
93
|
+
* is for composition with other authenticators (see the module docstring),
|
|
94
|
+
* so unsigned requests fall through to the next authenticator regardless;
|
|
95
|
+
* `required_for` enforcement on the unsigned path belongs to
|
|
96
|
+
* {@link requireSignatureWhenPresent}, which fails closed on an
|
|
97
|
+
* unresolvable body.
|
|
92
98
|
*/
|
|
93
99
|
resolveOperation: (req: IncomingMessage & {
|
|
94
100
|
rawBody?: string;
|
|
95
|
-
}) =>
|
|
101
|
+
}) => ResolvedOperation;
|
|
96
102
|
/**
|
|
97
103
|
* Override how the request's full URL is reconstructed. Use when the
|
|
98
104
|
* server sits behind a TLS-terminating or path-rewriting load balancer
|
|
@@ -202,21 +208,37 @@ export interface RequireSignatureWhenPresentOptions {
|
|
|
202
208
|
*/
|
|
203
209
|
requiredFor?: readonly string[];
|
|
204
210
|
/**
|
|
205
|
-
*
|
|
206
|
-
*
|
|
211
|
+
* JSON-RPC protocol methods (e.g. `CancelTask`, `tasks/cancel`) that MUST be
|
|
212
|
+
* signed, matching `capability.protocol_methods_required_for`. Matched
|
|
213
|
+
* against the JSON-RPC `method` only, never against the resolved operation.
|
|
214
|
+
* Like {@link requiredFor}, a valid fallback credential satisfies it.
|
|
215
|
+
*/
|
|
216
|
+
protocolMethodsRequiredFor?: readonly string[];
|
|
217
|
+
/**
|
|
218
|
+
* Resolve the AdCP operation a request carries. Defaults to
|
|
219
|
+
* {@link adcpOperationResolver}, which understands MCP `tools/call` and A2A
|
|
220
|
+
* `SendMessage` / `SendStreamingMessage` / `message/send` / `message/stream`.
|
|
207
221
|
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
222
|
+
* A resolver may return:
|
|
223
|
+
* - an operation name, matched against {@link requiredFor};
|
|
224
|
+
* - `undefined` for a request that carries no AdCP operation (discovery
|
|
225
|
+
* probes, `tasks/*`) — the request is not subject to {@link requiredFor};
|
|
226
|
+
* - {@link UNRESOLVABLE_OPERATION} for a body that does not resolve to
|
|
227
|
+
* exactly one operation. The gate rejects it with `request_body_malformed`
|
|
228
|
+
* before it looks at any signature header or credential, whether or not
|
|
229
|
+
* the request is signed. An ambiguous body is never treated as "an
|
|
230
|
+
* operation that is not in `requiredFor`".
|
|
211
231
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
232
|
+
* Omitting this option no longer disables the pre-check: a gate with
|
|
233
|
+
* `requiredFor` set and no resolver used to skip enforcement for every
|
|
234
|
+
* request. The default resolver is used instead. `requiredFor` and the
|
|
235
|
+
* resolver are only as good as each other — a resolver that returns
|
|
236
|
+
* `undefined` for A2A messages (such as {@link mcpToolNameResolver}) leaves
|
|
237
|
+
* `requiredFor` unenforced over A2A.
|
|
216
238
|
*/
|
|
217
239
|
resolveOperation?: (req: IncomingMessage & {
|
|
218
240
|
rawBody?: string;
|
|
219
|
-
}) =>
|
|
241
|
+
}) => ResolvedOperation;
|
|
220
242
|
}
|
|
221
243
|
/**
|
|
222
244
|
* Compose a signature authenticator with a fallback under presence-gated
|
|
@@ -252,10 +274,12 @@ export interface RequireAuthenticatedOrSignedOptions {
|
|
|
252
274
|
* declaration in `capabilities.request_signing.required_for`.
|
|
253
275
|
*/
|
|
254
276
|
requiredFor?: readonly string[];
|
|
277
|
+
/** See {@link RequireSignatureWhenPresentOptions.protocolMethodsRequiredFor}. */
|
|
278
|
+
protocolMethodsRequiredFor?: readonly string[];
|
|
255
279
|
/** See {@link RequireSignatureWhenPresentOptions.resolveOperation}. */
|
|
256
280
|
resolveOperation?: (req: IncomingMessage & {
|
|
257
281
|
rawBody?: string;
|
|
258
|
-
}) =>
|
|
282
|
+
}) => ResolvedOperation;
|
|
259
283
|
}
|
|
260
284
|
/**
|
|
261
285
|
* Bundled composition of {@link requireSignatureWhenPresent} with an
|
|
@@ -279,7 +303,7 @@ export interface RequireAuthenticatedOrSignedOptions {
|
|
|
279
303
|
* anyOf,
|
|
280
304
|
* verifySignatureAsAuthenticator,
|
|
281
305
|
* requireAuthenticatedOrSigned,
|
|
282
|
-
*
|
|
306
|
+
* adcpOperationResolver,
|
|
283
307
|
* MUTATING_TASKS,
|
|
284
308
|
* } from '@adcp/sdk/server';
|
|
285
309
|
*
|
|
@@ -287,43 +311,57 @@ export interface RequireAuthenticatedOrSignedOptions {
|
|
|
287
311
|
* authenticate: requireAuthenticatedOrSigned({
|
|
288
312
|
* signature: verifySignatureAsAuthenticator({
|
|
289
313
|
* jwks, replayStore, revocationStore, capability,
|
|
290
|
-
* resolveOperation:
|
|
314
|
+
* resolveOperation: adcpOperationResolver,
|
|
291
315
|
* }),
|
|
292
316
|
* fallback: anyOf(verifyApiKey({ keys }), verifyBearer({ jwksUri, issuer, audience })),
|
|
293
317
|
* requiredFor: [...MUTATING_TASKS],
|
|
294
|
-
* resolveOperation:
|
|
318
|
+
* resolveOperation: adcpOperationResolver,
|
|
295
319
|
* }),
|
|
296
320
|
* });
|
|
297
321
|
* ```
|
|
298
322
|
*/
|
|
299
323
|
export declare function requireAuthenticatedOrSigned(options: RequireAuthenticatedOrSignedOptions): Authenticator;
|
|
300
324
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* uses.
|
|
325
|
+
* Resolve the AdCP operation a request carries, for MCP and A2A alike
|
|
326
|
+
* (adcp#7945, "Operation resolution over A2A"). Pass it as `resolveOperation`
|
|
327
|
+
* on {@link verifySignatureAsAuthenticator}, {@link requireSignatureWhenPresent},
|
|
328
|
+
* {@link requireAuthenticatedOrSigned}, or `createExpressVerifier` from
|
|
329
|
+
* `@adcp/sdk/signing`; it is the default for the composers.
|
|
307
330
|
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
331
|
+
* | Request | Result |
|
|
332
|
+
* |-------------------------------------------------------------------------|--------------------------------|
|
|
333
|
+
* | MCP `tools/call` | `params.name` |
|
|
334
|
+
* | A2A `SendMessage`, `SendStreamingMessage`, `message/send`, `message/stream` | `skill` of the sole DataPart |
|
|
335
|
+
* | any other JSON-RPC method (`tasks/*`, `CancelTask`, `initialize`, …) | `undefined` (no operation) |
|
|
336
|
+
* | non-`POST` request | `undefined` |
|
|
337
|
+
* | anything ambiguous | {@link UNRESOLVABLE_OPERATION} |
|
|
311
338
|
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
|
|
324
|
-
|
|
339
|
+
* Ambiguous means: an empty or non-JSON body on a `POST`, a JSON-RPC batch or
|
|
340
|
+
* non-object body, a missing `method`, duplicate object keys (compared after
|
|
341
|
+
* JSON string decoding), a case variant of a recognized member name (`Skill`,
|
|
342
|
+
* `Parts`), zero or more than one DataPart, a FilePart (`raw`, `url`, `file`),
|
|
343
|
+
* a Part with other than one content member or a `kind` that disagrees with
|
|
344
|
+
* it, `data` that is not an object, or a `skill` / `params.name` that is not a
|
|
345
|
+
* non-empty string. Matching is exact and case-sensitive.
|
|
346
|
+
*
|
|
347
|
+
* The consumers reject {@link UNRESOLVABLE_OPERATION} with
|
|
348
|
+
* `request_body_malformed`, never as "an operation that is not in
|
|
349
|
+
* `required_for`". `req.rawBody` must hold the exact bytes the client sent.
|
|
350
|
+
*/
|
|
351
|
+
export declare function adcpOperationResolver(req: OperationResolutionInput | (IncomingMessage & {
|
|
352
|
+
rawBody?: string | Buffer;
|
|
353
|
+
})): ResolvedOperation;
|
|
354
|
+
/**
|
|
355
|
+
* Resolver for MCP agents. Parses the buffered JSON-RPC body on `req.rawBody`
|
|
356
|
+
* and returns `params.name` when `method === 'tools/call'`; returns
|
|
357
|
+
* `undefined` for every other method, a missing body, or malformed JSON.
|
|
325
358
|
*
|
|
326
|
-
*
|
|
359
|
+
* **MCP only, and lenient.** It does not understand A2A: an A2A
|
|
360
|
+
* `SendMessage` resolves to `undefined`, so a `requiredFor` list guarded by
|
|
361
|
+
* this resolver is NOT enforced over A2A. It also treats an ambiguous body as
|
|
362
|
+
* "no operation". Prefer {@link adcpOperationResolver}, which resolves A2A and
|
|
363
|
+
* reports ambiguity as {@link UNRESOLVABLE_OPERATION}. Kept for callers that
|
|
364
|
+
* pin the previous behavior.
|
|
327
365
|
*/
|
|
328
366
|
export declare function mcpToolNameResolver(req: {
|
|
329
367
|
rawBody?: string;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* verifyApiKey,
|
|
20
20
|
* anyOf,
|
|
21
21
|
* verifySignatureAsAuthenticator,
|
|
22
|
-
*
|
|
22
|
+
* adcpOperationResolver,
|
|
23
23
|
* } from '@adcp/sdk/server';
|
|
24
24
|
*
|
|
25
25
|
* serve(createAgent, {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* verifySignatureAsAuthenticator({
|
|
29
29
|
* jwks, replayStore, revocationStore,
|
|
30
30
|
* capability: { supported: true, required_for: [], covers_content_digest: 'either' },
|
|
31
|
-
* resolveOperation:
|
|
31
|
+
* resolveOperation: adcpOperationResolver,
|
|
32
32
|
* }),
|
|
33
33
|
* ),
|
|
34
34
|
* });
|
|
@@ -42,6 +42,7 @@ import type { JwksResolver } from '../signing/jwks';
|
|
|
42
42
|
import { type ReplayStore } from '../signing/replay';
|
|
43
43
|
import { type RevocationStore } from '../signing/revocation';
|
|
44
44
|
import type { VerifiedSigner, VerifierCapability } from '../signing/types';
|
|
45
|
+
import { type OperationResolutionInput, type ResolvedOperation } from '../signing/operation-resolution';
|
|
45
46
|
import { type AuthPrincipal, type Authenticator } from './auth';
|
|
46
47
|
export interface VerifySignatureAsAuthenticatorOptions {
|
|
47
48
|
/** Verifier capability block. `required_for` is not enforced here — unsigned
|
|
@@ -82,17 +83,22 @@ export interface VerifySignatureAsAuthenticatorOptions {
|
|
|
82
83
|
* before this runs. Same semantics as
|
|
83
84
|
* `ExpressMiddlewareOptions.resolveOperation` on {@link createExpressVerifier}.
|
|
84
85
|
*
|
|
86
|
+
* Pass {@link adcpOperationResolver}: it resolves MCP `tools/call` and A2A
|
|
87
|
+
* `SendMessage` / `message/send` requests, and reports a body that does not
|
|
88
|
+
* resolve to exactly one operation as unresolvable, which the verifier
|
|
89
|
+
* rejects with `request_body_malformed`.
|
|
90
|
+
*
|
|
85
91
|
* SECURITY: a resolver that always returns `undefined` disables
|
|
86
|
-
* `capability.required_for` enforcement for this authenticator.
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
+
* `capability.required_for` enforcement for this authenticator. This adapter
|
|
93
|
+
* is for composition with other authenticators (see the module docstring),
|
|
94
|
+
* so unsigned requests fall through to the next authenticator regardless;
|
|
95
|
+
* `required_for` enforcement on the unsigned path belongs to
|
|
96
|
+
* {@link requireSignatureWhenPresent}, which fails closed on an
|
|
97
|
+
* unresolvable body.
|
|
92
98
|
*/
|
|
93
99
|
resolveOperation: (req: IncomingMessage & {
|
|
94
100
|
rawBody?: string;
|
|
95
|
-
}) =>
|
|
101
|
+
}) => ResolvedOperation;
|
|
96
102
|
/**
|
|
97
103
|
* Override how the request's full URL is reconstructed. Use when the
|
|
98
104
|
* server sits behind a TLS-terminating or path-rewriting load balancer
|
|
@@ -202,21 +208,37 @@ export interface RequireSignatureWhenPresentOptions {
|
|
|
202
208
|
*/
|
|
203
209
|
requiredFor?: readonly string[];
|
|
204
210
|
/**
|
|
205
|
-
*
|
|
206
|
-
*
|
|
211
|
+
* JSON-RPC protocol methods (e.g. `CancelTask`, `tasks/cancel`) that MUST be
|
|
212
|
+
* signed, matching `capability.protocol_methods_required_for`. Matched
|
|
213
|
+
* against the JSON-RPC `method` only, never against the resolved operation.
|
|
214
|
+
* Like {@link requiredFor}, a valid fallback credential satisfies it.
|
|
215
|
+
*/
|
|
216
|
+
protocolMethodsRequiredFor?: readonly string[];
|
|
217
|
+
/**
|
|
218
|
+
* Resolve the AdCP operation a request carries. Defaults to
|
|
219
|
+
* {@link adcpOperationResolver}, which understands MCP `tools/call` and A2A
|
|
220
|
+
* `SendMessage` / `SendStreamingMessage` / `message/send` / `message/stream`.
|
|
207
221
|
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
222
|
+
* A resolver may return:
|
|
223
|
+
* - an operation name, matched against {@link requiredFor};
|
|
224
|
+
* - `undefined` for a request that carries no AdCP operation (discovery
|
|
225
|
+
* probes, `tasks/*`) — the request is not subject to {@link requiredFor};
|
|
226
|
+
* - {@link UNRESOLVABLE_OPERATION} for a body that does not resolve to
|
|
227
|
+
* exactly one operation. The gate rejects it with `request_body_malformed`
|
|
228
|
+
* before it looks at any signature header or credential, whether or not
|
|
229
|
+
* the request is signed. An ambiguous body is never treated as "an
|
|
230
|
+
* operation that is not in `requiredFor`".
|
|
211
231
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
232
|
+
* Omitting this option no longer disables the pre-check: a gate with
|
|
233
|
+
* `requiredFor` set and no resolver used to skip enforcement for every
|
|
234
|
+
* request. The default resolver is used instead. `requiredFor` and the
|
|
235
|
+
* resolver are only as good as each other — a resolver that returns
|
|
236
|
+
* `undefined` for A2A messages (such as {@link mcpToolNameResolver}) leaves
|
|
237
|
+
* `requiredFor` unenforced over A2A.
|
|
216
238
|
*/
|
|
217
239
|
resolveOperation?: (req: IncomingMessage & {
|
|
218
240
|
rawBody?: string;
|
|
219
|
-
}) =>
|
|
241
|
+
}) => ResolvedOperation;
|
|
220
242
|
}
|
|
221
243
|
/**
|
|
222
244
|
* Compose a signature authenticator with a fallback under presence-gated
|
|
@@ -252,10 +274,12 @@ export interface RequireAuthenticatedOrSignedOptions {
|
|
|
252
274
|
* declaration in `capabilities.request_signing.required_for`.
|
|
253
275
|
*/
|
|
254
276
|
requiredFor?: readonly string[];
|
|
277
|
+
/** See {@link RequireSignatureWhenPresentOptions.protocolMethodsRequiredFor}. */
|
|
278
|
+
protocolMethodsRequiredFor?: readonly string[];
|
|
255
279
|
/** See {@link RequireSignatureWhenPresentOptions.resolveOperation}. */
|
|
256
280
|
resolveOperation?: (req: IncomingMessage & {
|
|
257
281
|
rawBody?: string;
|
|
258
|
-
}) =>
|
|
282
|
+
}) => ResolvedOperation;
|
|
259
283
|
}
|
|
260
284
|
/**
|
|
261
285
|
* Bundled composition of {@link requireSignatureWhenPresent} with an
|
|
@@ -279,7 +303,7 @@ export interface RequireAuthenticatedOrSignedOptions {
|
|
|
279
303
|
* anyOf,
|
|
280
304
|
* verifySignatureAsAuthenticator,
|
|
281
305
|
* requireAuthenticatedOrSigned,
|
|
282
|
-
*
|
|
306
|
+
* adcpOperationResolver,
|
|
283
307
|
* MUTATING_TASKS,
|
|
284
308
|
* } from '@adcp/sdk/server';
|
|
285
309
|
*
|
|
@@ -287,43 +311,57 @@ export interface RequireAuthenticatedOrSignedOptions {
|
|
|
287
311
|
* authenticate: requireAuthenticatedOrSigned({
|
|
288
312
|
* signature: verifySignatureAsAuthenticator({
|
|
289
313
|
* jwks, replayStore, revocationStore, capability,
|
|
290
|
-
* resolveOperation:
|
|
314
|
+
* resolveOperation: adcpOperationResolver,
|
|
291
315
|
* }),
|
|
292
316
|
* fallback: anyOf(verifyApiKey({ keys }), verifyBearer({ jwksUri, issuer, audience })),
|
|
293
317
|
* requiredFor: [...MUTATING_TASKS],
|
|
294
|
-
* resolveOperation:
|
|
318
|
+
* resolveOperation: adcpOperationResolver,
|
|
295
319
|
* }),
|
|
296
320
|
* });
|
|
297
321
|
* ```
|
|
298
322
|
*/
|
|
299
323
|
export declare function requireAuthenticatedOrSigned(options: RequireAuthenticatedOrSignedOptions): Authenticator;
|
|
300
324
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* uses.
|
|
325
|
+
* Resolve the AdCP operation a request carries, for MCP and A2A alike
|
|
326
|
+
* (adcp#7945, "Operation resolution over A2A"). Pass it as `resolveOperation`
|
|
327
|
+
* on {@link verifySignatureAsAuthenticator}, {@link requireSignatureWhenPresent},
|
|
328
|
+
* {@link requireAuthenticatedOrSigned}, or `createExpressVerifier` from
|
|
329
|
+
* `@adcp/sdk/signing`; it is the default for the composers.
|
|
307
330
|
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
331
|
+
* | Request | Result |
|
|
332
|
+
* |-------------------------------------------------------------------------|--------------------------------|
|
|
333
|
+
* | MCP `tools/call` | `params.name` |
|
|
334
|
+
* | A2A `SendMessage`, `SendStreamingMessage`, `message/send`, `message/stream` | `skill` of the sole DataPart |
|
|
335
|
+
* | any other JSON-RPC method (`tasks/*`, `CancelTask`, `initialize`, …) | `undefined` (no operation) |
|
|
336
|
+
* | non-`POST` request | `undefined` |
|
|
337
|
+
* | anything ambiguous | {@link UNRESOLVABLE_OPERATION} |
|
|
311
338
|
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
|
|
324
|
-
|
|
339
|
+
* Ambiguous means: an empty or non-JSON body on a `POST`, a JSON-RPC batch or
|
|
340
|
+
* non-object body, a missing `method`, duplicate object keys (compared after
|
|
341
|
+
* JSON string decoding), a case variant of a recognized member name (`Skill`,
|
|
342
|
+
* `Parts`), zero or more than one DataPart, a FilePart (`raw`, `url`, `file`),
|
|
343
|
+
* a Part with other than one content member or a `kind` that disagrees with
|
|
344
|
+
* it, `data` that is not an object, or a `skill` / `params.name` that is not a
|
|
345
|
+
* non-empty string. Matching is exact and case-sensitive.
|
|
346
|
+
*
|
|
347
|
+
* The consumers reject {@link UNRESOLVABLE_OPERATION} with
|
|
348
|
+
* `request_body_malformed`, never as "an operation that is not in
|
|
349
|
+
* `required_for`". `req.rawBody` must hold the exact bytes the client sent.
|
|
350
|
+
*/
|
|
351
|
+
export declare function adcpOperationResolver(req: OperationResolutionInput | (IncomingMessage & {
|
|
352
|
+
rawBody?: string | Buffer;
|
|
353
|
+
})): ResolvedOperation;
|
|
354
|
+
/**
|
|
355
|
+
* Resolver for MCP agents. Parses the buffered JSON-RPC body on `req.rawBody`
|
|
356
|
+
* and returns `params.name` when `method === 'tools/call'`; returns
|
|
357
|
+
* `undefined` for every other method, a missing body, or malformed JSON.
|
|
325
358
|
*
|
|
326
|
-
*
|
|
359
|
+
* **MCP only, and lenient.** It does not understand A2A: an A2A
|
|
360
|
+
* `SendMessage` resolves to `undefined`, so a `requiredFor` list guarded by
|
|
361
|
+
* this resolver is NOT enforced over A2A. It also treats an ambiguous body as
|
|
362
|
+
* "no operation". Prefer {@link adcpOperationResolver}, which resolves A2A and
|
|
363
|
+
* reports ambiguity as {@link UNRESOLVABLE_OPERATION}. Kept for callers that
|
|
364
|
+
* pin the previous behavior.
|
|
327
365
|
*/
|
|
328
366
|
export declare function mcpToolNameResolver(req: {
|
|
329
367
|
rawBody?: string;
|
|
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var auth_signature_exports = {};
|
|
20
20
|
__export(auth_signature_exports, {
|
|
21
|
+
adcpOperationResolver: () => adcpOperationResolver,
|
|
21
22
|
mcpToolNameResolver: () => mcpToolNameResolver,
|
|
22
23
|
principalForVerifiedSigner: () => principalForVerifiedSigner,
|
|
23
24
|
requireAuthenticatedOrSigned: () => requireAuthenticatedOrSigned,
|
|
@@ -28,6 +29,7 @@ module.exports = __toCommonJS(auth_signature_exports);
|
|
|
28
29
|
var import_errors = require('../signing/errors.js');
|
|
29
30
|
var import_replay = require('../signing/replay.js');
|
|
30
31
|
var import_revocation = require('../signing/revocation.js');
|
|
32
|
+
var import_operation_resolution = require('../signing/operation-resolution.js');
|
|
31
33
|
var import_verifier = require('../signing/verifier.js');
|
|
32
34
|
var import_buyer_agent = require('./decisioning/buyer-agent.js');
|
|
33
35
|
var import_auth = require('./auth.js');
|
|
@@ -112,8 +114,18 @@ function principalForVerifiedSigner(signer, makePrincipal) {
|
|
|
112
114
|
}
|
|
113
115
|
function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {}) {
|
|
114
116
|
const requiredFor = new Set(options.requiredFor ?? []);
|
|
115
|
-
const
|
|
117
|
+
const unsignedCapability = {
|
|
118
|
+
required_for: [...requiredFor],
|
|
119
|
+
...options.protocolMethodsRequiredFor ? { protocol_methods_required_for: [...options.protocolMethodsRequiredFor] } : {}
|
|
120
|
+
};
|
|
121
|
+
const resolveOperation = options.resolveOperation ?? (requiredFor.size > 0 || unsignedCapability.protocol_methods_required_for ? adcpOperationResolver : void 0);
|
|
116
122
|
const combined = async (req) => {
|
|
123
|
+
const request = req;
|
|
124
|
+
const operation = resolveOperation?.(request);
|
|
125
|
+
if ((0, import_operation_resolution.isUnresolvableOperation)(operation)) {
|
|
126
|
+
logUnresolvable(request);
|
|
127
|
+
throw signatureAuthError((0, import_verifier.unresolvableOperationError)());
|
|
128
|
+
}
|
|
117
129
|
if (hasSignatureHeader(req)) {
|
|
118
130
|
const result = await signatureAuth(req);
|
|
119
131
|
if (result === null) {
|
|
@@ -121,6 +133,12 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
|
|
|
121
133
|
}
|
|
122
134
|
return result;
|
|
123
135
|
}
|
|
136
|
+
const body = bodyText(request);
|
|
137
|
+
try {
|
|
138
|
+
(0, import_verifier.assertUnsignedWebhookAuthenticationAbsent)(body);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
throw asSignatureAuthError(err);
|
|
141
|
+
}
|
|
124
142
|
let fallbackResult = null;
|
|
125
143
|
let fallbackError;
|
|
126
144
|
let fallbackThrew = false;
|
|
@@ -131,23 +149,16 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
|
|
|
131
149
|
fallbackError = err;
|
|
132
150
|
}
|
|
133
151
|
if (fallbackResult !== null) return fallbackResult;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"request_signature_required",
|
|
140
|
-
0,
|
|
141
|
-
`Operation ${operation} requires an RFC 9421 request signature when no other credentials are presented.`
|
|
142
|
-
)
|
|
143
|
-
});
|
|
144
|
-
}
|
|
152
|
+
try {
|
|
153
|
+
(0, import_verifier.assertUnsignedOperationNotRequired)(operation, unsignedCapability);
|
|
154
|
+
(0, import_verifier.assertUnsignedProtocolMethodNotRequired)(body, unsignedCapability);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
throw asSignatureAuthError(err);
|
|
145
157
|
}
|
|
146
158
|
if (fallbackThrew) throw fallbackError;
|
|
147
159
|
return null;
|
|
148
160
|
};
|
|
149
|
-
|
|
150
|
-
if (anyChildNeedsRawBody || resolveOperation) {
|
|
161
|
+
if (resolveOperation || (0, import_auth.authenticatorNeedsRawBody)(signatureAuth) || (0, import_auth.authenticatorNeedsRawBody)(fallbackAuth)) {
|
|
151
162
|
(0, import_auth.tagAuthenticatorNeedsRawBody)(combined);
|
|
152
163
|
}
|
|
153
164
|
(0, import_auth.tagAuthenticatorPresenceGated)(combined);
|
|
@@ -156,9 +167,13 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
|
|
|
156
167
|
function requireAuthenticatedOrSigned(options) {
|
|
157
168
|
return requireSignatureWhenPresent(options.signature, options.fallback, {
|
|
158
169
|
requiredFor: options.requiredFor,
|
|
170
|
+
protocolMethodsRequiredFor: options.protocolMethodsRequiredFor,
|
|
159
171
|
resolveOperation: options.resolveOperation
|
|
160
172
|
});
|
|
161
173
|
}
|
|
174
|
+
function adcpOperationResolver(req) {
|
|
175
|
+
return (0, import_operation_resolution.resolveRequestOperation)(operationInput(req));
|
|
176
|
+
}
|
|
162
177
|
function mcpToolNameResolver(req) {
|
|
163
178
|
const raw = req.rawBody;
|
|
164
179
|
if (!raw) return void 0;
|
|
@@ -171,6 +186,24 @@ function mcpToolNameResolver(req) {
|
|
|
171
186
|
return void 0;
|
|
172
187
|
}
|
|
173
188
|
}
|
|
189
|
+
function operationInput(req) {
|
|
190
|
+
const candidate = req;
|
|
191
|
+
return { rawBody: candidate.rawBody, method: candidate.method, url: candidate.url };
|
|
192
|
+
}
|
|
193
|
+
function bodyText(req) {
|
|
194
|
+
const raw = req.rawBody;
|
|
195
|
+
return typeof raw === "string" ? raw : raw?.toString("utf8");
|
|
196
|
+
}
|
|
197
|
+
function signatureAuthError(err) {
|
|
198
|
+
return new import_auth.AuthError(`Signature rejected (${err.code}).`, { cause: err });
|
|
199
|
+
}
|
|
200
|
+
function asSignatureAuthError(err) {
|
|
201
|
+
return err instanceof import_errors.RequestSignatureError ? signatureAuthError(err) : err;
|
|
202
|
+
}
|
|
203
|
+
function logUnresolvable(req) {
|
|
204
|
+
const reason = (0, import_operation_resolution.describeUnresolvable)(operationInput(req));
|
|
205
|
+
console.error(`[adcp/auth] request body does not resolve to one operation: ${reason ?? "unknown"}`);
|
|
206
|
+
}
|
|
174
207
|
const SAFE_KEYID = /^[A-Za-z0-9._-]{1,256}$/;
|
|
175
208
|
function hasSignatureHeader(req) {
|
|
176
209
|
return nonEmptyHeader(req.headers["signature-input"]) || nonEmptyHeader(req.headers["signature"]);
|
|
@@ -204,6 +237,7 @@ function firstHeader(value) {
|
|
|
204
237
|
}
|
|
205
238
|
// Annotate the CommonJS export names for ESM import in node:
|
|
206
239
|
0 && (module.exports = {
|
|
240
|
+
adcpOperationResolver,
|
|
207
241
|
mcpToolNameResolver,
|
|
208
242
|
principalForVerifiedSigner,
|
|
209
243
|
requireAuthenticatedOrSigned,
|