@adcp/sdk 14.0.0-beta.14 → 14.0.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/auth/oauth/authorization-required.d.mts +25 -11
- package/dist/lib/auth/oauth/authorization-required.d.ts +25 -11
- package/dist/lib/auth/oauth/authorization-required.js +18 -4
- package/dist/lib/auth/oauth/authorization-required.mjs +17 -4
- package/dist/lib/auth/oauth/index.d.mts +1 -1
- package/dist/lib/auth/oauth/index.d.ts +1 -1
- package/dist/lib/auth/oauth/index.js +2 -0
- package/dist/lib/auth/oauth/index.mjs +2 -0
- package/dist/lib/auth/oauth/resource-url.d.mts +8 -0
- package/dist/lib/auth/oauth/resource-url.d.ts +8 -0
- package/dist/lib/auth/oauth/resource-url.js +42 -0
- package/dist/lib/auth/oauth/resource-url.mjs +40 -0
- package/dist/lib/core/ConversationTypes.d.mts +10 -0
- package/dist/lib/core/ConversationTypes.d.ts +10 -0
- package/dist/lib/core/SingleAgentClient.d.mts +5 -2
- package/dist/lib/core/SingleAgentClient.d.ts +5 -2
- package/dist/lib/core/SingleAgentClient.js +133 -28
- package/dist/lib/core/SingleAgentClient.mjs +138 -29
- package/dist/lib/core/TaskExecutor.d.mts +1 -0
- package/dist/lib/core/TaskExecutor.d.ts +1 -0
- package/dist/lib/core/TaskExecutor.js +7 -1
- package/dist/lib/core/TaskExecutor.mjs +7 -1
- package/dist/lib/core/webhook-registration.d.mts +8 -1
- package/dist/lib/core/webhook-registration.d.ts +8 -1
- package/dist/lib/core/webhook-registration.js +65 -5
- package/dist/lib/core/webhook-registration.mjs +61 -4
- package/dist/lib/errors/index.d.mts +9 -0
- package/dist/lib/errors/index.d.ts +9 -0
- package/dist/lib/errors/index.js +33 -0
- package/dist/lib/errors/index.mjs +32 -0
- package/dist/lib/index.d.mts +3 -2
- package/dist/lib/index.d.ts +3 -2
- package/dist/lib/index.js +4 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/media-buy/compatibility.js +14 -2
- package/dist/lib/media-buy/compatibility.mjs +14 -2
- package/dist/lib/protocols/index.js +23 -7
- package/dist/lib/protocols/index.mjs +24 -8
- package/dist/lib/protocols/mcp.js +8 -6
- package/dist/lib/protocols/mcp.mjs +9 -7
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/decisioning/async-outcome.d.mts +11 -0
- package/dist/lib/server/decisioning/async-outcome.d.ts +11 -0
- package/dist/lib/server/decisioning/context.d.mts +11 -1
- package/dist/lib/server/decisioning/context.d.ts +11 -1
- package/dist/lib/server/decisioning/index.d.mts +3 -2
- package/dist/lib/server/decisioning/index.d.ts +3 -2
- package/dist/lib/server/decisioning/index.js +9 -0
- package/dist/lib/server/decisioning/index.mjs +10 -0
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +5 -2
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +5 -2
- package/dist/lib/server/decisioning/runtime/from-platform.js +40 -8
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +40 -8
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.mts +8 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts +8 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +10 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +9 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.d.mts +79 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.d.ts +79 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.js +391 -0
- package/dist/lib/server/decisioning/runtime/postgres-task-settlement.mjs +369 -0
- package/dist/lib/server/decisioning/runtime/task-registry.d.mts +2 -0
- package/dist/lib/server/decisioning/runtime/task-registry.d.ts +2 -0
- package/dist/lib/server/decisioning/runtime/task-registry.js +2 -1
- package/dist/lib/server/decisioning/runtime/task-registry.mjs +2 -1
- package/dist/lib/server/decisioning/runtime/to-context.js +15 -11
- package/dist/lib/server/decisioning/runtime/to-context.mjs +15 -11
- package/dist/lib/server/index.d.mts +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +4 -0
- package/dist/lib/server/index.mjs +4 -0
- package/dist/lib/server/webhook-delivery/index.d.mts +2 -2
- package/dist/lib/server/webhook-delivery/index.d.ts +2 -2
- package/dist/lib/server/webhook-delivery/index.js +4 -0
- package/dist/lib/server/webhook-delivery/index.mjs +5 -1
- package/dist/lib/server/webhook-delivery/pg.d.mts +3 -1
- package/dist/lib/server/webhook-delivery/pg.d.ts +3 -1
- package/dist/lib/server/webhook-delivery/pg.js +25 -2
- package/dist/lib/server/webhook-delivery/pg.mjs +25 -2
- package/dist/lib/server/webhook-delivery/recovery.d.mts +44 -0
- package/dist/lib/server/webhook-delivery/recovery.d.ts +44 -0
- package/dist/lib/server/webhook-delivery/recovery.js +146 -43
- package/dist/lib/server/webhook-delivery/recovery.mjs +144 -43
- package/dist/lib/signing/agent-resolver/index.d.mts +1 -1
- package/dist/lib/signing/agent-resolver/index.d.ts +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +9 -0
- package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +9 -0
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +4 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +4 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +17 -4
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +17 -4
- package/dist/lib/signing/jwks.d.mts +7 -0
- package/dist/lib/signing/jwks.d.ts +7 -0
- package/dist/lib/signing/server.d.mts +2 -2
- package/dist/lib/signing/server.d.ts +2 -2
- package/dist/lib/signing/webhook-verifier.d.mts +6 -0
- package/dist/lib/signing/webhook-verifier.d.ts +6 -0
- package/dist/lib/signing/webhook-verifier.js +38 -4
- package/dist/lib/signing/webhook-verifier.mjs +38 -4
- package/dist/lib/testing/client.d.mts +1 -0
- package/dist/lib/testing/client.d.ts +1 -0
- package/dist/lib/testing/client.js +6 -2
- package/dist/lib/testing/client.mjs +6 -2
- package/dist/lib/testing/compliance/comply.d.mts +2 -2
- package/dist/lib/testing/compliance/comply.d.ts +2 -2
- package/dist/lib/testing/compliance/comply.js +41 -31
- package/dist/lib/testing/compliance/comply.mjs +44 -21
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.mts +1 -7
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts +1 -7
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js +6 -42
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs +10 -40
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/utils/adcp-version-config.d.mts +2 -0
- package/dist/lib/utils/adcp-version-config.d.ts +2 -0
- package/dist/lib/utils/adcp-version-config.js +5 -0
- package/dist/lib/utils/adcp-version-config.mjs +4 -0
- 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/llms.txt +3 -1
- package/docs/migration-12-to-14.md +6 -5
- package/docs/migration-13-to-14.md +48 -7
- package/docs/migration-task-registry-scoping.md +164 -11
- package/package.json +1 -1
- package/skills/build-decisioning-platform/advanced/REFERENCE.md +1 -1
|
@@ -7,6 +7,12 @@ import {
|
|
|
7
7
|
assertWebhookDisposition,
|
|
8
8
|
isWebhookDeliveryTerminalError
|
|
9
9
|
} from "./common.mjs";
|
|
10
|
+
class WebhookAuthenticationProtectionError extends Error {
|
|
11
|
+
name = "WebhookAuthenticationProtectionError";
|
|
12
|
+
}
|
|
13
|
+
class WebhookAuthenticationResolutionError extends Error {
|
|
14
|
+
name = "WebhookAuthenticationResolutionError";
|
|
15
|
+
}
|
|
10
16
|
function memoryWebhookDeliveryRecoveryBackend(options = {}) {
|
|
11
17
|
const records = /* @__PURE__ */ new Map();
|
|
12
18
|
const now = options.now ?? Date.now;
|
|
@@ -116,13 +122,25 @@ function createWebhookDeliveryRecovery(options) {
|
|
|
116
122
|
return {
|
|
117
123
|
durability: "durable",
|
|
118
124
|
probe: () => options.backend.probe(),
|
|
125
|
+
async prepare(key, snapshot, prepareOptions) {
|
|
126
|
+
assertDeliveryKey(key);
|
|
127
|
+
const stored = await protectSnapshot(
|
|
128
|
+
snapshot,
|
|
129
|
+
key,
|
|
130
|
+
options.authenticationAdapter,
|
|
131
|
+
prepareOptions?.protectPayloadToken === true
|
|
132
|
+
);
|
|
133
|
+
assertStoredSnapshotSize(stored, maxSnapshotBytes);
|
|
134
|
+
return {
|
|
135
|
+
snapshot: stored,
|
|
136
|
+
snapshotFingerprint: snapshotFingerprint(stored),
|
|
137
|
+
storageFingerprint: canonicalJsonSha256(stored)
|
|
138
|
+
};
|
|
139
|
+
},
|
|
119
140
|
async checkpoint(key, snapshot) {
|
|
120
141
|
assertDeliveryKey(key);
|
|
121
|
-
const stored = await protectSnapshot(snapshot, key, options.authenticationAdapter);
|
|
122
|
-
|
|
123
|
-
if (storedBytes > maxSnapshotBytes) {
|
|
124
|
-
throw new TypeError(`Webhook recovery snapshot exceeds maxSnapshotBytes (${maxSnapshotBytes})`);
|
|
125
|
-
}
|
|
142
|
+
const stored = await protectSnapshot(snapshot, key, options.authenticationAdapter, false);
|
|
143
|
+
assertStoredSnapshotSize(stored, maxSnapshotBytes);
|
|
126
144
|
const fingerprint = snapshotFingerprint(stored);
|
|
127
145
|
const storageFingerprint = canonicalJsonSha256(stored);
|
|
128
146
|
const ownerToken = randomUUID();
|
|
@@ -294,59 +312,101 @@ function assertPollOutcome(outcome) {
|
|
|
294
312
|
throw new TypeError("Webhook recovery deliver() returned an invalid disposition");
|
|
295
313
|
}
|
|
296
314
|
}
|
|
297
|
-
async function protectSnapshot(snapshot, key, adapter) {
|
|
315
|
+
async function protectSnapshot(snapshot, key, adapter, protectPayloadToken) {
|
|
316
|
+
const payload = structuredClone(snapshot.payload);
|
|
317
|
+
const cleartextPayloadToken = protectPayloadToken && typeof payload.token === "string" ? payload.token : void 0;
|
|
318
|
+
if (cleartextPayloadToken !== void 0) delete payload.token;
|
|
298
319
|
const base = {
|
|
299
320
|
url: snapshot.url,
|
|
300
|
-
payload
|
|
321
|
+
payload,
|
|
301
322
|
retries: { ...snapshot.retries }
|
|
302
323
|
};
|
|
303
324
|
canonicalJsonSha256(base);
|
|
304
|
-
if (snapshot.authentication
|
|
305
|
-
if (!adapter) {
|
|
325
|
+
if ((snapshot.authentication !== null || cleartextPayloadToken !== void 0) && !adapter) {
|
|
306
326
|
throw new TypeError(
|
|
307
|
-
"Durable webhook recovery requires authenticationAdapter when bearer or
|
|
327
|
+
"Durable webhook recovery requires authenticationAdapter when bearer/HMAC authentication or a payload token is present"
|
|
308
328
|
);
|
|
309
329
|
}
|
|
310
|
-
const
|
|
311
|
-
const protectedAuth = await adapter.protect(structuredClone(snapshot.authentication), context);
|
|
312
|
-
if (!protectedAuth || typeof protectedAuth.fingerprint !== "string" || protectedAuth.fingerprint.length < 16) {
|
|
313
|
-
throw new TypeError(
|
|
314
|
-
"WebhookAuthenticationAdapter.protect() must return a stable fingerprint of at least 16 characters"
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
canonicalJsonSha256(protectedAuth.protectedValue);
|
|
318
|
-
return {
|
|
330
|
+
const stored = {
|
|
319
331
|
...base,
|
|
320
|
-
authentication: {
|
|
321
|
-
kind: "protected",
|
|
322
|
-
protectedValue: structuredClone(protectedAuth.protectedValue),
|
|
323
|
-
fingerprint: protectedAuth.fingerprint
|
|
324
|
-
}
|
|
332
|
+
authentication: { kind: "none" }
|
|
325
333
|
};
|
|
334
|
+
if (snapshot.authentication !== null) {
|
|
335
|
+
const protectedAuth = await protectAuthentication(
|
|
336
|
+
adapter,
|
|
337
|
+
structuredClone(snapshot.authentication),
|
|
338
|
+
authenticationContext(key, base)
|
|
339
|
+
);
|
|
340
|
+
stored.authentication = { kind: "protected", ...protectedAuth };
|
|
341
|
+
}
|
|
342
|
+
if (cleartextPayloadToken !== void 0) {
|
|
343
|
+
const protectedToken = await protectAuthentication(
|
|
344
|
+
adapter,
|
|
345
|
+
{ type: "bearer", token: cleartextPayloadToken },
|
|
346
|
+
authenticationContext(key, base, "payload_token")
|
|
347
|
+
);
|
|
348
|
+
stored.payloadToken = { kind: "protected", ...protectedToken };
|
|
349
|
+
}
|
|
350
|
+
return stored;
|
|
326
351
|
}
|
|
327
352
|
async function resolveSnapshot(snapshot, key, adapter) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
353
|
+
try {
|
|
354
|
+
let authentication = null;
|
|
355
|
+
if (snapshot.authentication.kind === "protected") {
|
|
356
|
+
if (!adapter) throw new Error("Cannot recover protected webhook authentication without authenticationAdapter");
|
|
357
|
+
authentication = await adapter.resolve(
|
|
358
|
+
structuredClone(snapshot.authentication.protectedValue),
|
|
359
|
+
authenticationContext(key, snapshot)
|
|
360
|
+
);
|
|
361
|
+
if (authentication === null)
|
|
362
|
+
throw new Error("WebhookAuthenticationAdapter.resolve() returned null for protected state");
|
|
363
|
+
}
|
|
364
|
+
const payload = structuredClone(snapshot.payload);
|
|
365
|
+
if (snapshot.payloadToken) {
|
|
366
|
+
if (!adapter) throw new Error("Cannot recover protected webhook payload token without authenticationAdapter");
|
|
367
|
+
const resolved = await adapter.resolve(
|
|
368
|
+
structuredClone(snapshot.payloadToken.protectedValue),
|
|
369
|
+
authenticationContext(key, snapshot, "payload_token")
|
|
370
|
+
);
|
|
371
|
+
if (resolved?.type !== "bearer" || typeof resolved.token !== "string") {
|
|
372
|
+
throw new Error("WebhookAuthenticationAdapter.resolve() returned an invalid protected payload token");
|
|
373
|
+
}
|
|
374
|
+
payload.token = resolved.token;
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
url: snapshot.url,
|
|
378
|
+
payload,
|
|
379
|
+
authentication,
|
|
380
|
+
retries: { ...snapshot.retries }
|
|
381
|
+
};
|
|
382
|
+
} catch (cause) {
|
|
383
|
+
if (cause instanceof WebhookAuthenticationResolutionError) throw cause;
|
|
384
|
+
throw new WebhookAuthenticationResolutionError("Webhook authentication resolution failed", { cause });
|
|
337
385
|
}
|
|
338
|
-
return {
|
|
339
|
-
url: snapshot.url,
|
|
340
|
-
payload: structuredClone(snapshot.payload),
|
|
341
|
-
authentication,
|
|
342
|
-
retries: { ...snapshot.retries }
|
|
343
|
-
};
|
|
344
386
|
}
|
|
345
|
-
function authenticationContext(key, snapshot) {
|
|
346
|
-
|
|
387
|
+
function authenticationContext(key, snapshot, purpose) {
|
|
388
|
+
const base = {
|
|
347
389
|
key: { ...key },
|
|
348
|
-
url: snapshot.url
|
|
390
|
+
url: snapshot.url
|
|
391
|
+
};
|
|
392
|
+
if (purpose === void 0) {
|
|
393
|
+
return {
|
|
394
|
+
...base,
|
|
395
|
+
// This is the exact pre-payload-token context hash. Do not change it:
|
|
396
|
+
// applications may bind KMS AAD to this value for pending snapshots.
|
|
397
|
+
snapshotContextFingerprint: canonicalJsonSha256({
|
|
398
|
+
key,
|
|
399
|
+
url: snapshot.url,
|
|
400
|
+
payload: snapshot.payload,
|
|
401
|
+
retries: snapshot.retries
|
|
402
|
+
})
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
...base,
|
|
407
|
+
purpose,
|
|
349
408
|
snapshotContextFingerprint: canonicalJsonSha256({
|
|
409
|
+
purpose,
|
|
350
410
|
key,
|
|
351
411
|
url: snapshot.url,
|
|
352
412
|
payload: snapshot.payload,
|
|
@@ -402,15 +462,56 @@ function snapshotFingerprint(snapshot) {
|
|
|
402
462
|
url: snapshot.url,
|
|
403
463
|
payload: snapshot.payload,
|
|
404
464
|
retries: snapshot.retries,
|
|
405
|
-
authentication: snapshot.authentication.kind === "none" ? { kind: "none" } : { kind: "protected", fingerprint: snapshot.authentication.fingerprint }
|
|
465
|
+
authentication: snapshot.authentication.kind === "none" ? { kind: "none" } : { kind: "protected", fingerprint: snapshot.authentication.fingerprint },
|
|
466
|
+
...snapshot.payloadToken && { payloadToken: { fingerprint: snapshot.payloadToken.fingerprint } }
|
|
406
467
|
});
|
|
407
468
|
}
|
|
469
|
+
async function protectAuthentication(adapter, authentication, context) {
|
|
470
|
+
let protectedAuth;
|
|
471
|
+
try {
|
|
472
|
+
protectedAuth = await adapter.protect(authentication, context);
|
|
473
|
+
} catch (cause) {
|
|
474
|
+
throw new WebhookAuthenticationProtectionError("Webhook authentication protection failed", { cause });
|
|
475
|
+
}
|
|
476
|
+
if (!protectedAuth || typeof protectedAuth.fingerprint !== "string" || protectedAuth.fingerprint.length < 16) {
|
|
477
|
+
throw new TypeError(
|
|
478
|
+
"WebhookAuthenticationAdapter.protect() must return a stable fingerprint of at least 16 characters"
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
canonicalJsonSha256(protectedAuth.protectedValue);
|
|
482
|
+
const cleartext = authentication.type === "bearer" ? authentication.token : authentication.secret;
|
|
483
|
+
if (protectedValueContainsSecret(protectedAuth.protectedValue, cleartext) || protectedAuth.fingerprint.includes(cleartext)) {
|
|
484
|
+
throw new TypeError("WebhookAuthenticationAdapter.protect() returned durable state containing cleartext");
|
|
485
|
+
}
|
|
486
|
+
return {
|
|
487
|
+
protectedValue: structuredClone(protectedAuth.protectedValue),
|
|
488
|
+
fingerprint: protectedAuth.fingerprint
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function protectedValueContainsSecret(value, cleartext) {
|
|
492
|
+
if (typeof value === "string") return value.includes(cleartext);
|
|
493
|
+
if (Array.isArray(value)) return value.some((item) => protectedValueContainsSecret(item, cleartext));
|
|
494
|
+
if (value && typeof value === "object") {
|
|
495
|
+
return Object.entries(value).some(
|
|
496
|
+
([key, item]) => key.includes(cleartext) || protectedValueContainsSecret(item, cleartext)
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
function assertStoredSnapshotSize(snapshot, maxSnapshotBytes) {
|
|
502
|
+
const storedBytes = Buffer.byteLength(JSON.stringify(snapshot));
|
|
503
|
+
if (storedBytes > maxSnapshotBytes) {
|
|
504
|
+
throw new TypeError(`Webhook recovery snapshot exceeds maxSnapshotBytes (${maxSnapshotBytes})`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
408
507
|
function assertLeaseMs(value) {
|
|
409
508
|
if (!Number.isSafeInteger(value) || value < 1e3 || value > 36e5) {
|
|
410
509
|
throw new TypeError("leaseMs must be an integer from 1000 through 3600000");
|
|
411
510
|
}
|
|
412
511
|
}
|
|
413
512
|
export {
|
|
513
|
+
WebhookAuthenticationProtectionError,
|
|
514
|
+
WebhookAuthenticationResolutionError,
|
|
414
515
|
createWebhookDeliveryRecovery,
|
|
415
516
|
memoryWebhookDeliveryRecoveryBackend,
|
|
416
517
|
pollWebhookDeliveryRecovery
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
export { resolveAgent } from './resolve-agent.mjs';
|
|
19
19
|
export { ResolvedAgentJwksResolver } from './resolved-agent-jwks.mjs';
|
|
20
|
-
export type { AgentResolution, AgentProtocol, AuthorizedOperatorScope, FetchCapabilitiesFn, ResolveAgentOptions, TraceStep, } from './resolve-agent.mjs';
|
|
20
|
+
export type { AgentResolution, AgentProtocol, AuthorizedOperatorScope, DelegatedOperatorAuthorizationContext, FetchCapabilitiesFn, ResolveAgentOptions, TraceStep, } from './resolve-agent.mjs';
|
|
21
21
|
export type { ResolvedAgentJwksResolverOptions } from './resolved-agent-jwks.mjs';
|
|
22
22
|
export { getAgentJwks, createAgentJwksSet } from './jwks-set.mjs';
|
|
23
23
|
export type { AgentJwksResult, GetAgentJwksOptions, CreateAgentJwksSetOptions } from './jwks-set.mjs';
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
export { resolveAgent } from './resolve-agent';
|
|
19
19
|
export { ResolvedAgentJwksResolver } from './resolved-agent-jwks';
|
|
20
|
-
export type { AgentResolution, AgentProtocol, AuthorizedOperatorScope, FetchCapabilitiesFn, ResolveAgentOptions, TraceStep, } from './resolve-agent';
|
|
20
|
+
export type { AgentResolution, AgentProtocol, AuthorizedOperatorScope, DelegatedOperatorAuthorizationContext, FetchCapabilitiesFn, ResolveAgentOptions, TraceStep, } from './resolve-agent';
|
|
21
21
|
export type { ResolvedAgentJwksResolverOptions } from './resolved-agent-jwks';
|
|
22
22
|
export { getAgentJwks, createAgentJwksSet } from './jwks-set';
|
|
23
23
|
export type { AgentJwksResult, GetAgentJwksOptions, CreateAgentJwksSetOptions } from './jwks-set';
|
|
@@ -33,6 +33,15 @@ import { type AgentResolverErrorDetail } from './errors.mjs';
|
|
|
33
33
|
import { type AgentEntry } from './select-agent.mjs';
|
|
34
34
|
export type AgentProtocol = 'mcp' | 'a2a';
|
|
35
35
|
export type AuthorizedOperatorScope = 'media_buying' | 'creative_generation' | 'rights_clearance' | 'governance' | 'measurement' | 'agent_operations';
|
|
36
|
+
/**
|
|
37
|
+
* Trusted local context selecting one delegated-operator authorization tuple.
|
|
38
|
+
* This is receiver policy, never counterparty-supplied wire data.
|
|
39
|
+
*/
|
|
40
|
+
export interface DelegatedOperatorAuthorizationContext {
|
|
41
|
+
brand?: string;
|
|
42
|
+
scope?: AuthorizedOperatorScope;
|
|
43
|
+
country?: string;
|
|
44
|
+
}
|
|
36
45
|
export interface FetchCapabilitiesFn {
|
|
37
46
|
(agentUrl: string): Promise<unknown>;
|
|
38
47
|
}
|
|
@@ -33,6 +33,15 @@ import { type AgentResolverErrorDetail } from './errors';
|
|
|
33
33
|
import { type AgentEntry } from './select-agent';
|
|
34
34
|
export type AgentProtocol = 'mcp' | 'a2a';
|
|
35
35
|
export type AuthorizedOperatorScope = 'media_buying' | 'creative_generation' | 'rights_clearance' | 'governance' | 'measurement' | 'agent_operations';
|
|
36
|
+
/**
|
|
37
|
+
* Trusted local context selecting one delegated-operator authorization tuple.
|
|
38
|
+
* This is receiver policy, never counterparty-supplied wire data.
|
|
39
|
+
*/
|
|
40
|
+
export interface DelegatedOperatorAuthorizationContext {
|
|
41
|
+
brand?: string;
|
|
42
|
+
scope?: AuthorizedOperatorScope;
|
|
43
|
+
country?: string;
|
|
44
|
+
}
|
|
36
45
|
export interface FetchCapabilitiesFn {
|
|
37
46
|
(agentUrl: string): Promise<unknown>;
|
|
38
47
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AdcpJsonWebKey } from '../types.mjs';
|
|
2
|
-
import type { JwksResolver } from '../jwks.mjs';
|
|
2
|
+
import type { JwksResolution, JwksResolver } from '../jwks.mjs';
|
|
3
3
|
import { type AgentProtocol, type AgentResolution, type ResolveAgentOptions } from './resolve-agent.mjs';
|
|
4
4
|
export interface ResolvedAgentJwksResolverOptions extends Pick<ResolveAgentOptions, 'fetchCapabilities' | 'allowPrivateIp' | 'bodyCaps' | 'timeoutMs' | 'now' | 'requiredOperatorBrand' | 'requiredOperatorScope' | 'requiredOperatorCountry'> {
|
|
5
5
|
/** Positive cache lifetime in seconds. Defaults to 300. */
|
|
@@ -20,9 +20,12 @@ export declare class ResolvedAgentJwksResolver implements JwksResolver {
|
|
|
20
20
|
private readonly resolveAgentFn;
|
|
21
21
|
private keys;
|
|
22
22
|
private expiresAt;
|
|
23
|
+
private operatorAuthorizationValidUntil?;
|
|
23
24
|
private lastUnknownKidRefresh;
|
|
24
25
|
private inFlight?;
|
|
25
26
|
constructor(agentUrl: string, protocol: AgentProtocol, options?: ResolvedAgentJwksResolverOptions);
|
|
26
27
|
resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
|
|
28
|
+
resolveWithMetadata(keyid: string): Promise<JwksResolution>;
|
|
29
|
+
private resolution;
|
|
27
30
|
private refresh;
|
|
28
31
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AdcpJsonWebKey } from '../types';
|
|
2
|
-
import type { JwksResolver } from '../jwks';
|
|
2
|
+
import type { JwksResolution, JwksResolver } from '../jwks';
|
|
3
3
|
import { type AgentProtocol, type AgentResolution, type ResolveAgentOptions } from './resolve-agent';
|
|
4
4
|
export interface ResolvedAgentJwksResolverOptions extends Pick<ResolveAgentOptions, 'fetchCapabilities' | 'allowPrivateIp' | 'bodyCaps' | 'timeoutMs' | 'now' | 'requiredOperatorBrand' | 'requiredOperatorScope' | 'requiredOperatorCountry'> {
|
|
5
5
|
/** Positive cache lifetime in seconds. Defaults to 300. */
|
|
@@ -20,9 +20,12 @@ export declare class ResolvedAgentJwksResolver implements JwksResolver {
|
|
|
20
20
|
private readonly resolveAgentFn;
|
|
21
21
|
private keys;
|
|
22
22
|
private expiresAt;
|
|
23
|
+
private operatorAuthorizationValidUntil?;
|
|
23
24
|
private lastUnknownKidRefresh;
|
|
24
25
|
private inFlight?;
|
|
25
26
|
constructor(agentUrl: string, protocol: AgentProtocol, options?: ResolvedAgentJwksResolverOptions);
|
|
26
27
|
resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
|
|
28
|
+
resolveWithMetadata(keyid: string): Promise<JwksResolution>;
|
|
29
|
+
private resolution;
|
|
27
30
|
private refresh;
|
|
28
31
|
}
|
|
@@ -48,9 +48,13 @@ class ResolvedAgentJwksResolver {
|
|
|
48
48
|
resolveAgentFn;
|
|
49
49
|
keys = /* @__PURE__ */ new Map();
|
|
50
50
|
expiresAt = 0;
|
|
51
|
+
operatorAuthorizationValidUntil;
|
|
51
52
|
lastUnknownKidRefresh = Number.NEGATIVE_INFINITY;
|
|
52
53
|
inFlight;
|
|
53
54
|
async resolve(keyid) {
|
|
55
|
+
return (await this.resolveWithMetadata(keyid)).jwk;
|
|
56
|
+
}
|
|
57
|
+
async resolveWithMetadata(keyid) {
|
|
54
58
|
const now = this.now();
|
|
55
59
|
let refreshed = false;
|
|
56
60
|
if (now >= this.expiresAt) {
|
|
@@ -58,15 +62,23 @@ class ResolvedAgentJwksResolver {
|
|
|
58
62
|
refreshed = true;
|
|
59
63
|
}
|
|
60
64
|
const cached = this.keys.get(keyid);
|
|
61
|
-
if (cached) return cached;
|
|
65
|
+
if (cached) return this.resolution(cached);
|
|
62
66
|
if (refreshed) {
|
|
63
67
|
this.lastUnknownKidRefresh = now;
|
|
64
|
-
return null;
|
|
68
|
+
return this.resolution(null);
|
|
65
69
|
}
|
|
66
|
-
if (now - this.lastUnknownKidRefresh < this.unknownKidCooldownSeconds) return null;
|
|
70
|
+
if (now - this.lastUnknownKidRefresh < this.unknownKidCooldownSeconds) return this.resolution(null);
|
|
67
71
|
await this.refresh();
|
|
68
72
|
this.lastUnknownKidRefresh = now;
|
|
69
|
-
return this.keys.get(keyid) ?? null;
|
|
73
|
+
return this.resolution(this.keys.get(keyid) ?? null);
|
|
74
|
+
}
|
|
75
|
+
resolution(jwk) {
|
|
76
|
+
return {
|
|
77
|
+
jwk,
|
|
78
|
+
...this.operatorAuthorizationValidUntil !== void 0 && {
|
|
79
|
+
operatorAuthorizationValidUntil: this.operatorAuthorizationValidUntil
|
|
80
|
+
}
|
|
81
|
+
};
|
|
70
82
|
}
|
|
71
83
|
async refresh() {
|
|
72
84
|
if (this.inFlight) return this.inFlight;
|
|
@@ -104,6 +116,7 @@ class ResolvedAgentJwksResolver {
|
|
|
104
116
|
}
|
|
105
117
|
}
|
|
106
118
|
this.keys = next;
|
|
119
|
+
this.operatorAuthorizationValidUntil = resolution.operatorAuthorizationValidUntil;
|
|
107
120
|
this.expiresAt = Math.min(
|
|
108
121
|
refreshedAt + this.cacheTtlSeconds,
|
|
109
122
|
resolution.operatorAuthorizationValidUntil ?? Number.POSITIVE_INFINITY
|
|
@@ -25,9 +25,13 @@ class ResolvedAgentJwksResolver {
|
|
|
25
25
|
resolveAgentFn;
|
|
26
26
|
keys = /* @__PURE__ */ new Map();
|
|
27
27
|
expiresAt = 0;
|
|
28
|
+
operatorAuthorizationValidUntil;
|
|
28
29
|
lastUnknownKidRefresh = Number.NEGATIVE_INFINITY;
|
|
29
30
|
inFlight;
|
|
30
31
|
async resolve(keyid) {
|
|
32
|
+
return (await this.resolveWithMetadata(keyid)).jwk;
|
|
33
|
+
}
|
|
34
|
+
async resolveWithMetadata(keyid) {
|
|
31
35
|
const now = this.now();
|
|
32
36
|
let refreshed = false;
|
|
33
37
|
if (now >= this.expiresAt) {
|
|
@@ -35,15 +39,23 @@ class ResolvedAgentJwksResolver {
|
|
|
35
39
|
refreshed = true;
|
|
36
40
|
}
|
|
37
41
|
const cached = this.keys.get(keyid);
|
|
38
|
-
if (cached) return cached;
|
|
42
|
+
if (cached) return this.resolution(cached);
|
|
39
43
|
if (refreshed) {
|
|
40
44
|
this.lastUnknownKidRefresh = now;
|
|
41
|
-
return null;
|
|
45
|
+
return this.resolution(null);
|
|
42
46
|
}
|
|
43
|
-
if (now - this.lastUnknownKidRefresh < this.unknownKidCooldownSeconds) return null;
|
|
47
|
+
if (now - this.lastUnknownKidRefresh < this.unknownKidCooldownSeconds) return this.resolution(null);
|
|
44
48
|
await this.refresh();
|
|
45
49
|
this.lastUnknownKidRefresh = now;
|
|
46
|
-
return this.keys.get(keyid) ?? null;
|
|
50
|
+
return this.resolution(this.keys.get(keyid) ?? null);
|
|
51
|
+
}
|
|
52
|
+
resolution(jwk) {
|
|
53
|
+
return {
|
|
54
|
+
jwk,
|
|
55
|
+
...this.operatorAuthorizationValidUntil !== void 0 && {
|
|
56
|
+
operatorAuthorizationValidUntil: this.operatorAuthorizationValidUntil
|
|
57
|
+
}
|
|
58
|
+
};
|
|
47
59
|
}
|
|
48
60
|
async refresh() {
|
|
49
61
|
if (this.inFlight) return this.inFlight;
|
|
@@ -81,6 +93,7 @@ class ResolvedAgentJwksResolver {
|
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
this.keys = next;
|
|
96
|
+
this.operatorAuthorizationValidUntil = resolution.operatorAuthorizationValidUntil;
|
|
84
97
|
this.expiresAt = Math.min(
|
|
85
98
|
refreshedAt + this.cacheTtlSeconds,
|
|
86
99
|
resolution.operatorAuthorizationValidUntil ?? Number.POSITIVE_INFINITY
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { AdcpJsonWebKey } from './types.mjs';
|
|
2
|
+
export interface JwksResolution {
|
|
3
|
+
jwk: AdcpJsonWebKey | null;
|
|
4
|
+
/** Epoch seconds for a delegated-operator authorization boundary, when applicable. */
|
|
5
|
+
operatorAuthorizationValidUntil?: number;
|
|
6
|
+
}
|
|
2
7
|
export interface JwksResolver {
|
|
3
8
|
resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
|
|
9
|
+
/** Optional metadata-aware lookup used by verifiers that must enforce delegation expiry at acceptance time. */
|
|
10
|
+
resolveWithMetadata?(keyid: string): Promise<JwksResolution>;
|
|
4
11
|
}
|
|
5
12
|
export declare class StaticJwksResolver implements JwksResolver {
|
|
6
13
|
private readonly byKid;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { AdcpJsonWebKey } from './types';
|
|
2
|
+
export interface JwksResolution {
|
|
3
|
+
jwk: AdcpJsonWebKey | null;
|
|
4
|
+
/** Epoch seconds for a delegated-operator authorization boundary, when applicable. */
|
|
5
|
+
operatorAuthorizationValidUntil?: number;
|
|
6
|
+
}
|
|
2
7
|
export interface JwksResolver {
|
|
3
8
|
resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
|
|
9
|
+
/** Optional metadata-aware lookup used by verifiers that must enforce delegation expiry at acceptance time. */
|
|
10
|
+
resolveWithMetadata?(keyid: string): Promise<JwksResolution>;
|
|
4
11
|
}
|
|
5
12
|
export declare class StaticJwksResolver implements JwksResolver {
|
|
6
13
|
private readonly byKid;
|
|
@@ -13,7 +13,7 @@ export { computeContentDigest, contentDigestMatches, parseContentDigest } from '
|
|
|
13
13
|
export { requestContextFromExpress, requestContextFromFetch, requestContextFromLambda, type ExpressRequestLike, type FetchRequestLike, type LambdaRequestEvent, type RequestContextFromExpressOptions, type RequestContextFromLambdaOptions, } from './request-context.mjs';
|
|
14
14
|
export { jwkToPublicKey, verifySignature } from './crypto.mjs';
|
|
15
15
|
export { RequestSignatureError, type RequestSignatureErrorCode, ResponseSignatureError, type ResponseSignatureErrorCode, WebhookSignatureError, type WebhookSignatureErrorCode, } from './errors.mjs';
|
|
16
|
-
export { StaticJwksResolver, type JwksResolver } from './jwks.mjs';
|
|
16
|
+
export { StaticJwksResolver, type JwksResolution, type JwksResolver } from './jwks.mjs';
|
|
17
17
|
export { HttpsJwksResolver, type HttpsJwksResolverOptions } from './jwks-https.mjs';
|
|
18
18
|
export { BrandJsonJwksResolver, BrandJsonResolverError, type BrandAgentType, type BrandJsonJwksResolverOptions, type BrandJsonResolverErrorCode, } from './brand-jwks.mjs';
|
|
19
19
|
export { parseSignature, parseSignatureInput, type ParsedSignature, type ParsedSignatureInput } from './parser.mjs';
|
|
@@ -28,4 +28,4 @@ export { createWebhookVerifier, isWebhookLoopbackHost, verifyWebhookSignature, W
|
|
|
28
28
|
export { createExpressVerifier, type ExpressLike, type ExpressMiddlewareOptions } from './middleware.mjs';
|
|
29
29
|
export { finalizeResponseSignature, prepareResponseSignature, signResponse, type PreparedResponseSignature, type SignedResponse, type SignResponseOptions, } from './signer.mjs';
|
|
30
30
|
export { signResponseAsync } from './signer-async.mjs';
|
|
31
|
-
export { resolveAgent, ResolvedAgentJwksResolver, getAgentJwks, createAgentJwksSet, AgentResolverError, attackerInfluencedFields, ATTACKER_INFLUENCED, readBrandJsonUrl, readIdentityPosture, type AgentResolution, type AgentProtocol, type AuthorizedOperatorScope, type AgentResolverErrorCode, type AgentResolverErrorDetail, type AgentEntry, type AgentJwksResult, type CapabilitiesWithBrandJsonUrl, type CreateAgentJwksSetOptions, type FetchCapabilitiesFn, type GetAgentJwksOptions, type IdentityKeyOriginPurpose, type IdentityKeyOrigins, type IdentityPosture, type ResolveAgentOptions, type ResolvedAgentJwksResolverOptions, type TraceStep, } from './agent-resolver/index.mjs';
|
|
31
|
+
export { resolveAgent, ResolvedAgentJwksResolver, getAgentJwks, createAgentJwksSet, AgentResolverError, attackerInfluencedFields, ATTACKER_INFLUENCED, readBrandJsonUrl, readIdentityPosture, type AgentResolution, type AgentProtocol, type AuthorizedOperatorScope, type DelegatedOperatorAuthorizationContext, type AgentResolverErrorCode, type AgentResolverErrorDetail, type AgentEntry, type AgentJwksResult, type CapabilitiesWithBrandJsonUrl, type CreateAgentJwksSetOptions, type FetchCapabilitiesFn, type GetAgentJwksOptions, type IdentityKeyOriginPurpose, type IdentityKeyOrigins, type IdentityPosture, type ResolveAgentOptions, type ResolvedAgentJwksResolverOptions, type TraceStep, } from './agent-resolver/index.mjs';
|
|
@@ -13,7 +13,7 @@ export { computeContentDigest, contentDigestMatches, parseContentDigest } from '
|
|
|
13
13
|
export { requestContextFromExpress, requestContextFromFetch, requestContextFromLambda, type ExpressRequestLike, type FetchRequestLike, type LambdaRequestEvent, type RequestContextFromExpressOptions, type RequestContextFromLambdaOptions, } from './request-context';
|
|
14
14
|
export { jwkToPublicKey, verifySignature } from './crypto';
|
|
15
15
|
export { RequestSignatureError, type RequestSignatureErrorCode, ResponseSignatureError, type ResponseSignatureErrorCode, WebhookSignatureError, type WebhookSignatureErrorCode, } from './errors';
|
|
16
|
-
export { StaticJwksResolver, type JwksResolver } from './jwks';
|
|
16
|
+
export { StaticJwksResolver, type JwksResolution, type JwksResolver } from './jwks';
|
|
17
17
|
export { HttpsJwksResolver, type HttpsJwksResolverOptions } from './jwks-https';
|
|
18
18
|
export { BrandJsonJwksResolver, BrandJsonResolverError, type BrandAgentType, type BrandJsonJwksResolverOptions, type BrandJsonResolverErrorCode, } from './brand-jwks';
|
|
19
19
|
export { parseSignature, parseSignatureInput, type ParsedSignature, type ParsedSignatureInput } from './parser';
|
|
@@ -28,4 +28,4 @@ export { createWebhookVerifier, isWebhookLoopbackHost, verifyWebhookSignature, W
|
|
|
28
28
|
export { createExpressVerifier, type ExpressLike, type ExpressMiddlewareOptions } from './middleware';
|
|
29
29
|
export { finalizeResponseSignature, prepareResponseSignature, signResponse, type PreparedResponseSignature, type SignedResponse, type SignResponseOptions, } from './signer';
|
|
30
30
|
export { signResponseAsync } from './signer-async';
|
|
31
|
-
export { resolveAgent, ResolvedAgentJwksResolver, getAgentJwks, createAgentJwksSet, AgentResolverError, attackerInfluencedFields, ATTACKER_INFLUENCED, readBrandJsonUrl, readIdentityPosture, type AgentResolution, type AgentProtocol, type AuthorizedOperatorScope, type AgentResolverErrorCode, type AgentResolverErrorDetail, type AgentEntry, type AgentJwksResult, type CapabilitiesWithBrandJsonUrl, type CreateAgentJwksSetOptions, type FetchCapabilitiesFn, type GetAgentJwksOptions, type IdentityKeyOriginPurpose, type IdentityKeyOrigins, type IdentityPosture, type ResolveAgentOptions, type ResolvedAgentJwksResolverOptions, type TraceStep, } from './agent-resolver';
|
|
31
|
+
export { resolveAgent, ResolvedAgentJwksResolver, getAgentJwks, createAgentJwksSet, AgentResolverError, attackerInfluencedFields, ATTACKER_INFLUENCED, readBrandJsonUrl, readIdentityPosture, type AgentResolution, type AgentProtocol, type AuthorizedOperatorScope, type DelegatedOperatorAuthorizationContext, type AgentResolverErrorCode, type AgentResolverErrorDetail, type AgentEntry, type AgentJwksResult, type CapabilitiesWithBrandJsonUrl, type CreateAgentJwksSetOptions, type FetchCapabilitiesFn, type GetAgentJwksOptions, type IdentityKeyOriginPurpose, type IdentityKeyOrigins, type IdentityPosture, type ResolveAgentOptions, type ResolvedAgentJwksResolverOptions, type TraceStep, } from './agent-resolver';
|
|
@@ -53,6 +53,12 @@ export interface VerifyWebhookOptions {
|
|
|
53
53
|
requiredTag?: string;
|
|
54
54
|
/** Optional reverse-lookup (kid → publisher URL) for result attribution. */
|
|
55
55
|
agentUrlForKeyid?: (keyid: string) => string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Trusted local discriminator appended to replay accounting. Receivers use
|
|
58
|
+
* this to isolate registrations authorized under different delegated-
|
|
59
|
+
* operator tuples even when they share a callback URL and signing key.
|
|
60
|
+
*/
|
|
61
|
+
replayScopeBinding?: string;
|
|
56
62
|
}
|
|
57
63
|
export interface VerifyWebhookResult {
|
|
58
64
|
status: 'verified';
|
|
@@ -53,6 +53,12 @@ export interface VerifyWebhookOptions {
|
|
|
53
53
|
requiredTag?: string;
|
|
54
54
|
/** Optional reverse-lookup (kid → publisher URL) for result attribution. */
|
|
55
55
|
agentUrlForKeyid?: (keyid: string) => string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Trusted local discriminator appended to replay accounting. Receivers use
|
|
58
|
+
* this to isolate registrations authorized under different delegated-
|
|
59
|
+
* operator tuples even when they share a callback URL and signing key.
|
|
60
|
+
*/
|
|
61
|
+
replayScopeBinding?: string;
|
|
56
62
|
}
|
|
57
63
|
export interface VerifyWebhookResult {
|
|
58
64
|
status: 'verified';
|
|
@@ -42,7 +42,8 @@ const WEBHOOK_MANDATORY_COMPONENTS = [
|
|
|
42
42
|
"content-digest"
|
|
43
43
|
];
|
|
44
44
|
async function verifyWebhookSignature(request, options) {
|
|
45
|
-
const
|
|
45
|
+
const currentTime = options.now ?? (() => Date.now() / 1e3);
|
|
46
|
+
const now = currentTime();
|
|
46
47
|
const requiredTag = options.requiredTag ?? WEBHOOK_SIGNING_TAG;
|
|
47
48
|
const sigInputHeader = (0, import_canonicalize.getHeaderValue)(request.headers, "Signature-Input");
|
|
48
49
|
const sigHeader = (0, import_canonicalize.getHeaderValue)(request.headers, "Signature");
|
|
@@ -83,7 +84,8 @@ async function verifyWebhookSignature(request, options) {
|
|
|
83
84
|
validateWindow(parsedInput.params.created, parsedInput.params.expires, now);
|
|
84
85
|
validateCoveredComponents(parsedInput.components);
|
|
85
86
|
const canonicalWebhookTarget = validateTargetUri(request.url);
|
|
86
|
-
const
|
|
87
|
+
const keyResolution = options.jwks.resolveWithMetadata ? await options.jwks.resolveWithMetadata(parsedInput.params.keyid) : { jwk: await options.jwks.resolve(parsedInput.params.keyid) };
|
|
88
|
+
const jwk = keyResolution.jwk;
|
|
87
89
|
if (!jwk) {
|
|
88
90
|
throw new import_errors.WebhookSignatureError(
|
|
89
91
|
"webhook_signature_key_unknown",
|
|
@@ -98,6 +100,9 @@ async function verifyWebhookSignature(request, options) {
|
|
|
98
100
|
`JWKS resolver returned a JWK whose kid "${jwk.kid}" does not match requested keyid "${parsedInput.params.keyid}".`
|
|
99
101
|
);
|
|
100
102
|
}
|
|
103
|
+
if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
|
|
104
|
+
assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
|
|
105
|
+
}
|
|
101
106
|
if (jwk.adcp_use === void 0 || !jwk.key_ops?.includes("verify") || jwk.adcp_use !== "request-signing" && jwk.adcp_use !== "webhook-signing") {
|
|
102
107
|
throw new import_errors.WebhookSignatureError(
|
|
103
108
|
"webhook_signature_key_purpose_invalid",
|
|
@@ -115,7 +120,7 @@ async function verifyWebhookSignature(request, options) {
|
|
|
115
120
|
}
|
|
116
121
|
throw err;
|
|
117
122
|
}
|
|
118
|
-
const replayScope = canonicalWebhookTarget;
|
|
123
|
+
const replayScope = replayScopeForWebhook(canonicalWebhookTarget, options.replayScopeBinding);
|
|
119
124
|
if (await options.replayStore.has(jwk.kid, replayScope, parsedInput.params.nonce, now)) {
|
|
120
125
|
throw new import_errors.WebhookSignatureError(
|
|
121
126
|
"webhook_signature_replayed",
|
|
@@ -161,6 +166,9 @@ async function verifyWebhookSignature(request, options) {
|
|
|
161
166
|
"Content-Digest header does not match recomputed body hash."
|
|
162
167
|
);
|
|
163
168
|
}
|
|
169
|
+
if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
|
|
170
|
+
assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
|
|
171
|
+
}
|
|
164
172
|
const remaining = parsedInput.params.expires - now + import_types.CLOCK_SKEW_TOLERANCE_SECONDS;
|
|
165
173
|
const ttl = Math.max(remaining, import_types.MAX_SIGNATURE_WINDOW_SECONDS + import_types.CLOCK_SKEW_TOLERANCE_SECONDS);
|
|
166
174
|
const insertResult = await options.replayStore.insert(jwk.kid, replayScope, parsedInput.params.nonce, ttl, now);
|
|
@@ -178,8 +186,34 @@ async function verifyWebhookSignature(request, options) {
|
|
|
178
186
|
`Per-keyid replay cache cap exceeded on commit for keyid=${jwk.kid}.`
|
|
179
187
|
);
|
|
180
188
|
}
|
|
189
|
+
if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
|
|
190
|
+
assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
|
|
191
|
+
}
|
|
181
192
|
const agent_url = options.agentUrlForKeyid?.(jwk.kid);
|
|
182
|
-
return {
|
|
193
|
+
return {
|
|
194
|
+
status: "verified",
|
|
195
|
+
keyid: jwk.kid,
|
|
196
|
+
...agent_url !== void 0 && { agent_url },
|
|
197
|
+
// Keep the established whole-second credential timestamp while using the
|
|
198
|
+
// fractional clock above for exact authorization-expiry enforcement.
|
|
199
|
+
verified_at: Math.floor(now)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function assertDelegatedOperatorAuthorizationActive(validUntil, now) {
|
|
203
|
+
if (validUntil !== void 0 && (!Number.isFinite(validUntil) || now >= validUntil)) {
|
|
204
|
+
throw new import_errors.WebhookSignatureError(
|
|
205
|
+
"webhook_signature_key_unknown",
|
|
206
|
+
7,
|
|
207
|
+
"The signing key is no longer authorized for this webhook registration."
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function replayScopeForWebhook(canonicalTarget, binding) {
|
|
212
|
+
if (binding === void 0) return canonicalTarget;
|
|
213
|
+
if (binding.length === 0 || binding.length > 1024 || binding.includes("\0")) {
|
|
214
|
+
throw new TypeError("replayScopeBinding must be a non-empty string of at most 1024 characters without NUL.");
|
|
215
|
+
}
|
|
216
|
+
return JSON.stringify([canonicalTarget, binding]);
|
|
183
217
|
}
|
|
184
218
|
function requireParams(parsed) {
|
|
185
219
|
const required = ["created", "expires", "nonce", "keyid", "alg", "tag"];
|