@adcp/sdk 16.0.0-beta.6 → 16.0.0-beta.7
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/core/TaskExecutor.d.mts +1 -0
- package/dist/lib/core/TaskExecutor.d.ts +1 -0
- package/dist/lib/core/TaskExecutor.js +48 -34
- package/dist/lib/core/TaskExecutor.mjs +49 -35
- package/dist/lib/identity-3.3/revocation.js +5 -4
- package/dist/lib/identity-3.3/revocation.mjs +5 -4
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/auth-signature.js +23 -2
- package/dist/lib/server/auth-signature.mjs +26 -2
- package/dist/lib/server/auth.d.mts +6 -0
- package/dist/lib/server/auth.d.ts +6 -0
- package/dist/lib/server/auth.js +6 -0
- package/dist/lib/server/auth.mjs +6 -0
- package/dist/lib/server/create-adcp-server.js +19 -3
- package/dist/lib/server/create-adcp-server.mjs +23 -4
- package/dist/lib/server/decisioning/runtime/from-platform.js +6 -2
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +6 -2
- package/dist/lib/server/serve.js +2 -1
- package/dist/lib/server/serve.mjs +2 -1
- package/dist/lib/server/webhook-emitter.js +1 -0
- package/dist/lib/server/webhook-emitter.mjs +2 -1
- package/dist/lib/signing/crypto.d.mts +1 -1
- package/dist/lib/signing/crypto.d.ts +1 -1
- package/dist/lib/signing/crypto.js +37 -2
- package/dist/lib/signing/crypto.mjs +37 -2
- package/dist/lib/signing/parser.d.mts +6 -0
- package/dist/lib/signing/parser.d.ts +6 -0
- package/dist/lib/signing/parser.js +5 -1
- package/dist/lib/signing/parser.mjs +4 -1
- package/dist/lib/signing/web-bot-auth.d.mts +61 -22
- package/dist/lib/signing/web-bot-auth.d.ts +61 -22
- package/dist/lib/signing/web-bot-auth.js +286 -266
- package/dist/lib/signing/web-bot-auth.mjs +288 -277
- package/dist/lib/testing/client.js +3 -5
- package/dist/lib/testing/client.mjs +4 -6
- package/dist/lib/testing/compliance/comply.js +7 -2
- package/dist/lib/testing/compliance/comply.mjs +8 -3
- package/dist/lib/testing/index.d.mts +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.js +4 -2
- package/dist/lib/testing/index.mjs +8 -2
- package/dist/lib/testing/storyboard/runner.js +12 -12
- package/dist/lib/testing/storyboard/runner.mjs +14 -13
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +23 -1
- package/dist/lib/testing/storyboard/validations.mjs +28 -2
- package/dist/lib/testing/types.d.mts +39 -0
- package/dist/lib/testing/types.d.ts +39 -0
- package/dist/lib/v2/projection/augment-response.js +6 -3
- package/dist/lib/v2/projection/augment-response.mjs +8 -5
- package/dist/lib/v2/projection/creative-delivery.js +29 -17
- package/dist/lib/v2/projection/creative-delivery.mjs +31 -18
- package/dist/lib/v2/projection/v1-to-v2.d.mts +7 -0
- package/dist/lib/v2/projection/v1-to-v2.d.ts +7 -0
- package/dist/lib/v2/projection/v1-to-v2.js +70 -2
- package/dist/lib/v2/projection/v1-to-v2.mjs +72 -1
- package/dist/lib/validation/index.d.mts +1 -1
- package/dist/lib/validation/index.d.ts +1 -1
- package/dist/lib/validation/index.js +4 -2
- package/dist/lib/validation/index.mjs +4 -2
- package/dist/lib/validation/schema-loader.d.mts +26 -0
- package/dist/lib/validation/schema-loader.d.ts +26 -0
- package/dist/lib/validation/schema-loader.js +98 -14
- package/dist/lib/validation/schema-loader.mjs +95 -13
- 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/VALIDATE-YOUR-AGENT.md +34 -0
- package/docs/llms.txt +2 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-identity-3.3.md +60 -23
- package/package.json +1 -1
|
@@ -4459,41 +4459,22 @@ class TaskExecutor {
|
|
|
4459
4459
|
const mode = this.responseValidationMode;
|
|
4460
4460
|
const logViolations = this.config.logSchemaViolations !== false;
|
|
4461
4461
|
try {
|
|
4462
|
-
|
|
4463
|
-
if (taskName === "get_adcp_capabilities" &&
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
const errorStrings = outcome.issues.map((i) => `${i.pointer}: ${i.message}`);
|
|
4470
|
-
if (logViolations) {
|
|
4471
|
-
debugLogs.push({
|
|
4472
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4473
|
-
type: mode === "strict" ? "error" : "warning",
|
|
4474
|
-
message: `Schema validation ${mode === "strict" ? "failed" : "warning"} for ${taskName}: ${(0, import_schema_validator.formatIssues)(
|
|
4475
|
-
outcome.issues
|
|
4476
|
-
)}`,
|
|
4477
|
-
errors: errorStrings,
|
|
4478
|
-
issues: outcome.issues,
|
|
4479
|
-
schemaVariant: outcome.variant,
|
|
4480
|
-
schemaVersion: validationVersion,
|
|
4481
|
-
mode
|
|
4482
|
-
});
|
|
4483
|
-
}
|
|
4484
|
-
if (mode === "warn") {
|
|
4485
|
-
if (logViolations) {
|
|
4486
|
-
console.warn(
|
|
4487
|
-
`Schema validation failed for ${taskName} against AdCP ${validationVersion} (non-blocking):`,
|
|
4488
|
-
errorStrings
|
|
4489
|
-
);
|
|
4490
|
-
}
|
|
4491
|
-
return { valid: true, errors: [] };
|
|
4492
|
-
}
|
|
4493
|
-
if (logViolations) {
|
|
4494
|
-
console.error(`Schema validation failed for ${taskName} against AdCP ${validationVersion}:`, errorStrings);
|
|
4462
|
+
const pinnedValidationVersion = (serverVersion ?? this.lastKnownServerVersion) === "v2" ? "v2.5" : this.responseAdcpVersionForValidation() ?? this.config.adcpVersion ?? import_version.ADCP_VERSION;
|
|
4463
|
+
if (taskName === "get_adcp_capabilities" && pinnedValidationVersion !== "v2.5") {
|
|
4464
|
+
return (0, import_schema_loader.withServedValidationVersion)(
|
|
4465
|
+
response,
|
|
4466
|
+
pinnedValidationVersion,
|
|
4467
|
+
(validationVersion) => this.validateResponseSchemaAgainst(response, taskName, debugLogs, mode, logViolations, validationVersion)
|
|
4468
|
+
);
|
|
4495
4469
|
}
|
|
4496
|
-
return
|
|
4470
|
+
return this.validateResponseSchemaAgainst(
|
|
4471
|
+
response,
|
|
4472
|
+
taskName,
|
|
4473
|
+
debugLogs,
|
|
4474
|
+
mode,
|
|
4475
|
+
logViolations,
|
|
4476
|
+
pinnedValidationVersion
|
|
4477
|
+
);
|
|
4497
4478
|
} catch (error) {
|
|
4498
4479
|
const errorMessage = error instanceof Error ? error.message : "unknown error";
|
|
4499
4480
|
console.error("Error during schema validation:", errorMessage);
|
|
@@ -4503,6 +4484,39 @@ class TaskExecutor {
|
|
|
4503
4484
|
};
|
|
4504
4485
|
}
|
|
4505
4486
|
}
|
|
4487
|
+
validateResponseSchemaAgainst(response, taskName, debugLogs, mode, logViolations, validationVersion) {
|
|
4488
|
+
const normalizedResponse = this.normalizeResponseForValidation(response, taskName, validationVersion === "v2.5");
|
|
4489
|
+
const outcome = (this.config.experimentalIdentity33?.() || this.config.identity33SchemaExperiment && normalizedResponse && typeof normalizedResponse === "object" && (0, import_adcp_version_config.isAdcpVersionAtLeast)(normalizedResponse.adcp_version, "3.3")) && mode !== "off" ? validateIdentity33Payload(taskName, "response", normalizedResponse, validationVersion) : (0, import_client_hooks.validateIncomingResponse)(taskName, normalizedResponse, mode, void 0, validationVersion);
|
|
4490
|
+
if (outcome.valid) return { valid: true, errors: [] };
|
|
4491
|
+
const errorStrings = outcome.issues.map((i) => `${i.pointer}: ${i.message}`);
|
|
4492
|
+
if (logViolations) {
|
|
4493
|
+
debugLogs.push({
|
|
4494
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4495
|
+
type: mode === "strict" ? "error" : "warning",
|
|
4496
|
+
message: `Schema validation ${mode === "strict" ? "failed" : "warning"} for ${taskName}: ${(0, import_schema_validator.formatIssues)(
|
|
4497
|
+
outcome.issues
|
|
4498
|
+
)}`,
|
|
4499
|
+
errors: errorStrings,
|
|
4500
|
+
issues: outcome.issues,
|
|
4501
|
+
schemaVariant: outcome.variant,
|
|
4502
|
+
schemaVersion: validationVersion,
|
|
4503
|
+
mode
|
|
4504
|
+
});
|
|
4505
|
+
}
|
|
4506
|
+
if (mode === "warn") {
|
|
4507
|
+
if (logViolations) {
|
|
4508
|
+
console.warn(
|
|
4509
|
+
`Schema validation failed for ${taskName} against AdCP ${validationVersion} (non-blocking):`,
|
|
4510
|
+
errorStrings
|
|
4511
|
+
);
|
|
4512
|
+
}
|
|
4513
|
+
return { valid: true, errors: [] };
|
|
4514
|
+
}
|
|
4515
|
+
if (logViolations) {
|
|
4516
|
+
console.error(`Schema validation failed for ${taskName} against AdCP ${validationVersion}:`, errorStrings);
|
|
4517
|
+
}
|
|
4518
|
+
return { valid: false, errors: errorStrings };
|
|
4519
|
+
}
|
|
4506
4520
|
responseAdcpVersionForValidation() {
|
|
4507
4521
|
if (this.config.versionEnvelope === "major-only") return "3.0";
|
|
4508
4522
|
return void 0;
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
resolveValidationModes
|
|
27
27
|
} from "../validation/client-hooks.mjs";
|
|
28
28
|
import { formatIssues } from "../validation/schema-validator.mjs";
|
|
29
|
-
import {
|
|
29
|
+
import { withServedValidationVersion } from "../validation/schema-loader.mjs";
|
|
30
30
|
import { ADCP_VERSION } from "../version.mjs";
|
|
31
31
|
import { unwrapProtocolResponse, isAdcpOperationSuccess } from "../utils/response-unwrapper.mjs";
|
|
32
32
|
import { extractAdcpErrorInfo, extractCorrelationId } from "../utils/error-extraction.mjs";
|
|
@@ -4437,41 +4437,22 @@ class TaskExecutor {
|
|
|
4437
4437
|
const mode = this.responseValidationMode;
|
|
4438
4438
|
const logViolations = this.config.logSchemaViolations !== false;
|
|
4439
4439
|
try {
|
|
4440
|
-
|
|
4441
|
-
if (taskName === "get_adcp_capabilities" &&
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
const errorStrings = outcome.issues.map((i) => `${i.pointer}: ${i.message}`);
|
|
4448
|
-
if (logViolations) {
|
|
4449
|
-
debugLogs.push({
|
|
4450
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4451
|
-
type: mode === "strict" ? "error" : "warning",
|
|
4452
|
-
message: `Schema validation ${mode === "strict" ? "failed" : "warning"} for ${taskName}: ${formatIssues(
|
|
4453
|
-
outcome.issues
|
|
4454
|
-
)}`,
|
|
4455
|
-
errors: errorStrings,
|
|
4456
|
-
issues: outcome.issues,
|
|
4457
|
-
schemaVariant: outcome.variant,
|
|
4458
|
-
schemaVersion: validationVersion,
|
|
4459
|
-
mode
|
|
4460
|
-
});
|
|
4461
|
-
}
|
|
4462
|
-
if (mode === "warn") {
|
|
4463
|
-
if (logViolations) {
|
|
4464
|
-
console.warn(
|
|
4465
|
-
`Schema validation failed for ${taskName} against AdCP ${validationVersion} (non-blocking):`,
|
|
4466
|
-
errorStrings
|
|
4467
|
-
);
|
|
4468
|
-
}
|
|
4469
|
-
return { valid: true, errors: [] };
|
|
4470
|
-
}
|
|
4471
|
-
if (logViolations) {
|
|
4472
|
-
console.error(`Schema validation failed for ${taskName} against AdCP ${validationVersion}:`, errorStrings);
|
|
4440
|
+
const pinnedValidationVersion = (serverVersion ?? this.lastKnownServerVersion) === "v2" ? "v2.5" : this.responseAdcpVersionForValidation() ?? this.config.adcpVersion ?? ADCP_VERSION;
|
|
4441
|
+
if (taskName === "get_adcp_capabilities" && pinnedValidationVersion !== "v2.5") {
|
|
4442
|
+
return withServedValidationVersion(
|
|
4443
|
+
response,
|
|
4444
|
+
pinnedValidationVersion,
|
|
4445
|
+
(validationVersion) => this.validateResponseSchemaAgainst(response, taskName, debugLogs, mode, logViolations, validationVersion)
|
|
4446
|
+
);
|
|
4473
4447
|
}
|
|
4474
|
-
return
|
|
4448
|
+
return this.validateResponseSchemaAgainst(
|
|
4449
|
+
response,
|
|
4450
|
+
taskName,
|
|
4451
|
+
debugLogs,
|
|
4452
|
+
mode,
|
|
4453
|
+
logViolations,
|
|
4454
|
+
pinnedValidationVersion
|
|
4455
|
+
);
|
|
4475
4456
|
} catch (error) {
|
|
4476
4457
|
const errorMessage = error instanceof Error ? error.message : "unknown error";
|
|
4477
4458
|
console.error("Error during schema validation:", errorMessage);
|
|
@@ -4481,6 +4462,39 @@ class TaskExecutor {
|
|
|
4481
4462
|
};
|
|
4482
4463
|
}
|
|
4483
4464
|
}
|
|
4465
|
+
validateResponseSchemaAgainst(response, taskName, debugLogs, mode, logViolations, validationVersion) {
|
|
4466
|
+
const normalizedResponse = this.normalizeResponseForValidation(response, taskName, validationVersion === "v2.5");
|
|
4467
|
+
const outcome = (this.config.experimentalIdentity33?.() || this.config.identity33SchemaExperiment && normalizedResponse && typeof normalizedResponse === "object" && isAdcpVersionAtLeast(normalizedResponse.adcp_version, "3.3")) && mode !== "off" ? validateIdentity33Payload(taskName, "response", normalizedResponse, validationVersion) : validateIncomingResponse(taskName, normalizedResponse, mode, void 0, validationVersion);
|
|
4468
|
+
if (outcome.valid) return { valid: true, errors: [] };
|
|
4469
|
+
const errorStrings = outcome.issues.map((i) => `${i.pointer}: ${i.message}`);
|
|
4470
|
+
if (logViolations) {
|
|
4471
|
+
debugLogs.push({
|
|
4472
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4473
|
+
type: mode === "strict" ? "error" : "warning",
|
|
4474
|
+
message: `Schema validation ${mode === "strict" ? "failed" : "warning"} for ${taskName}: ${formatIssues(
|
|
4475
|
+
outcome.issues
|
|
4476
|
+
)}`,
|
|
4477
|
+
errors: errorStrings,
|
|
4478
|
+
issues: outcome.issues,
|
|
4479
|
+
schemaVariant: outcome.variant,
|
|
4480
|
+
schemaVersion: validationVersion,
|
|
4481
|
+
mode
|
|
4482
|
+
});
|
|
4483
|
+
}
|
|
4484
|
+
if (mode === "warn") {
|
|
4485
|
+
if (logViolations) {
|
|
4486
|
+
console.warn(
|
|
4487
|
+
`Schema validation failed for ${taskName} against AdCP ${validationVersion} (non-blocking):`,
|
|
4488
|
+
errorStrings
|
|
4489
|
+
);
|
|
4490
|
+
}
|
|
4491
|
+
return { valid: true, errors: [] };
|
|
4492
|
+
}
|
|
4493
|
+
if (logViolations) {
|
|
4494
|
+
console.error(`Schema validation failed for ${taskName} against AdCP ${validationVersion}:`, errorStrings);
|
|
4495
|
+
}
|
|
4496
|
+
return { valid: false, errors: errorStrings };
|
|
4497
|
+
}
|
|
4484
4498
|
responseAdcpVersionForValidation() {
|
|
4485
4499
|
if (this.config.versionEnvelope === "major-only") return "3.0";
|
|
4486
4500
|
return void 0;
|
|
@@ -225,7 +225,8 @@ class OriginRevocationResolver {
|
|
|
225
225
|
for (const token of tokens) {
|
|
226
226
|
try {
|
|
227
227
|
const header = (0, import_jose.decodeProtectedHeader)(token);
|
|
228
|
-
if (header.typ !== "adcp-gov-revocation+jws" || typeof header.kid !== "string" ||
|
|
228
|
+
if (header.typ !== "adcp-gov-revocation+jws" || typeof header.kid !== "string" || // Directory keys are Ed25519 or RSA-PSS under the profile (adcp#7894).
|
|
229
|
+
header.alg !== "EdDSA" && header.alg !== "PS512")
|
|
229
230
|
continue;
|
|
230
231
|
await this.options.directory.resolve(header.kid, origin);
|
|
231
232
|
const checked = this.options.directory.getVerifiedDirectory(origin);
|
|
@@ -234,7 +235,7 @@ class OriginRevocationResolver {
|
|
|
234
235
|
const jwk = checked.keys.find((key) => key.kid === header.kid);
|
|
235
236
|
if (!jwk) continue;
|
|
236
237
|
const result = await (0, import_jose.compactVerify)(token, await (0, import_jose.importJWK)(jwk, header.alg), {
|
|
237
|
-
algorithms: [
|
|
238
|
+
algorithms: [header.alg]
|
|
238
239
|
});
|
|
239
240
|
verifiedPayload = result.payload;
|
|
240
241
|
break;
|
|
@@ -295,8 +296,8 @@ async function createSignedOriginRevocationList(snapshot, privateKey) {
|
|
|
295
296
|
const kid = (0, import_web_bot_auth.jwkThumbprint)(privateKey);
|
|
296
297
|
if (privateKey.kid && privateKey.kid !== kid)
|
|
297
298
|
throw new TypeError("Revocation signing kid must be the JWK thumbprint");
|
|
298
|
-
const alg = privateKey.crv === "Ed25519" ? "EdDSA" : privateKey.
|
|
299
|
-
if (!alg || !privateKey.d) throw new TypeError("Revocations require a private Ed25519 or
|
|
299
|
+
const alg = privateKey.crv === "Ed25519" ? "EdDSA" : privateKey.kty === "RSA" ? "PS512" : void 0;
|
|
300
|
+
if (!alg || !privateKey.d) throw new TypeError("Revocations require a private Ed25519 or RSA key");
|
|
300
301
|
return new import_jose.CompactSign(Buffer.from(JSON.stringify(snapshot))).setProtectedHeader({ alg, kid, typ: "adcp-gov-revocation+jws" }).sign(await (0, import_jose.importJWK)(privateKey, alg));
|
|
301
302
|
}
|
|
302
303
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -204,7 +204,8 @@ class OriginRevocationResolver {
|
|
|
204
204
|
for (const token of tokens) {
|
|
205
205
|
try {
|
|
206
206
|
const header = decodeProtectedHeader(token);
|
|
207
|
-
if (header.typ !== "adcp-gov-revocation+jws" || typeof header.kid !== "string" ||
|
|
207
|
+
if (header.typ !== "adcp-gov-revocation+jws" || typeof header.kid !== "string" || // Directory keys are Ed25519 or RSA-PSS under the profile (adcp#7894).
|
|
208
|
+
header.alg !== "EdDSA" && header.alg !== "PS512")
|
|
208
209
|
continue;
|
|
209
210
|
await this.options.directory.resolve(header.kid, origin);
|
|
210
211
|
const checked = this.options.directory.getVerifiedDirectory(origin);
|
|
@@ -213,7 +214,7 @@ class OriginRevocationResolver {
|
|
|
213
214
|
const jwk = checked.keys.find((key) => key.kid === header.kid);
|
|
214
215
|
if (!jwk) continue;
|
|
215
216
|
const result = await compactVerify(token, await importJWK(jwk, header.alg), {
|
|
216
|
-
algorithms: [
|
|
217
|
+
algorithms: [header.alg]
|
|
217
218
|
});
|
|
218
219
|
verifiedPayload = result.payload;
|
|
219
220
|
break;
|
|
@@ -274,8 +275,8 @@ async function createSignedOriginRevocationList(snapshot, privateKey) {
|
|
|
274
275
|
const kid = jwkThumbprint(privateKey);
|
|
275
276
|
if (privateKey.kid && privateKey.kid !== kid)
|
|
276
277
|
throw new TypeError("Revocation signing kid must be the JWK thumbprint");
|
|
277
|
-
const alg = privateKey.crv === "Ed25519" ? "EdDSA" : privateKey.
|
|
278
|
-
if (!alg || !privateKey.d) throw new TypeError("Revocations require a private Ed25519 or
|
|
278
|
+
const alg = privateKey.crv === "Ed25519" ? "EdDSA" : privateKey.kty === "RSA" ? "PS512" : void 0;
|
|
279
|
+
if (!alg || !privateKey.d) throw new TypeError("Revocations require a private Ed25519 or RSA key");
|
|
279
280
|
return new CompactSign(Buffer.from(JSON.stringify(snapshot))).setProtectedHeader({ alg, kid, typ: "adcp-gov-revocation+jws" }).sign(await importJWK(privateKey, alg));
|
|
280
281
|
}
|
|
281
282
|
export {
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
"source_sha": "4e553ad955f83b49c7d221ab5c3ff78237ad02e3",
|
|
5
5
|
"source_tarball_sha256": "580656d6466ef9f0d1119985e6726c2efea718dc671e2ad30957fcb2fd54af0f",
|
|
6
6
|
"upstream_adcp_version": "2.5.3",
|
|
7
|
-
"synced_at": "2026-10-
|
|
7
|
+
"synced_at": "2026-10-17T14:29:51.574Z"
|
|
8
8
|
}
|
|
@@ -149,8 +149,29 @@ function verifySignatureAsAuthenticator(options) {
|
|
|
149
149
|
return principal;
|
|
150
150
|
};
|
|
151
151
|
const tagged = (0, import_auth.tagAuthenticatorNeedsRawBody)(authenticator);
|
|
152
|
+
if (options.webBotAuth) webBotAuthProfiles.set(tagged, options.capability.profiles ?? []);
|
|
152
153
|
return options.webBotAuth ? (0, import_auth.tagAuthenticatorPresenceGated)(tagged) : tagged;
|
|
153
154
|
}
|
|
155
|
+
const webBotAuthProfiles = /* @__PURE__ */ new WeakMap();
|
|
156
|
+
function unsignedRequiredError(err, signatureAuth, body) {
|
|
157
|
+
const profiles = webBotAuthProfiles.get(signatureAuth);
|
|
158
|
+
if (!profiles?.includes("web-bot-auth") || !(err instanceof import_errors.RequestSignatureError) || err.code !== "request_signature_required")
|
|
159
|
+
return asSignatureAuthError(err);
|
|
160
|
+
const hasBody = body !== void 0 && body !== "";
|
|
161
|
+
if ((0, import_web_bot_auth2.advertisesOnlyWebBotAuth)(profiles)) {
|
|
162
|
+
const refused = (0, import_web_bot_auth2.unsignedWebBotAuthError)(hasBody);
|
|
163
|
+
return new import_auth.AuthError(refused.message, {
|
|
164
|
+
cause: refused,
|
|
165
|
+
status: refused.status,
|
|
166
|
+
headers: refused.headers,
|
|
167
|
+
body: refused.body
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return new import_auth.AuthError(`Signature rejected (${err.code}).`, {
|
|
171
|
+
cause: err,
|
|
172
|
+
headers: { "Accept-Signature": (0, import_web_bot_auth2.webBotAuthAcceptSignature)("sig1", hasBody) }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
154
175
|
function principalForVerifiedSigner(signer, makePrincipal) {
|
|
155
176
|
if (!SAFE_KEYID.test(signer.keyid)) {
|
|
156
177
|
throw new import_auth.AuthError("Signature key id contains unsupported characters.", {
|
|
@@ -207,7 +228,7 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
|
|
|
207
228
|
try {
|
|
208
229
|
(0, import_verifier.assertUnsignedWebhookAuthenticationAbsent)(body);
|
|
209
230
|
} catch (err) {
|
|
210
|
-
throw
|
|
231
|
+
throw unsignedRequiredError(err, signatureAuth, body);
|
|
211
232
|
}
|
|
212
233
|
let fallbackResult = null;
|
|
213
234
|
let fallbackError;
|
|
@@ -223,7 +244,7 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
|
|
|
223
244
|
(0, import_verifier.assertUnsignedOperationNotRequired)(operation, unsignedCapability);
|
|
224
245
|
(0, import_verifier.assertUnsignedProtocolMethodNotRequired)(body, unsignedCapability);
|
|
225
246
|
} catch (err) {
|
|
226
|
-
throw
|
|
247
|
+
throw unsignedRequiredError(err, signatureAuth, body);
|
|
227
248
|
}
|
|
228
249
|
if (fallbackThrew) throw fallbackError;
|
|
229
250
|
return null;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { getWebBotAuthLegacyOriginGate } from "../signing/web-bot-auth.mjs";
|
|
2
2
|
import { RequestSignatureError } from "../signing/errors.mjs";
|
|
3
3
|
import {
|
|
4
|
+
advertisesOnlyWebBotAuth,
|
|
4
5
|
selectSignatureProfile,
|
|
6
|
+
unsignedWebBotAuthError,
|
|
5
7
|
verifyWebBotAuth,
|
|
8
|
+
webBotAuthAcceptSignature,
|
|
6
9
|
isWebBotAuthRevocationChecked,
|
|
7
10
|
webBotAuthUnavailableResponse,
|
|
8
11
|
WebBotAuthUnavailableError,
|
|
@@ -146,8 +149,29 @@ function verifySignatureAsAuthenticator(options) {
|
|
|
146
149
|
return principal;
|
|
147
150
|
};
|
|
148
151
|
const tagged = tagAuthenticatorNeedsRawBody(authenticator);
|
|
152
|
+
if (options.webBotAuth) webBotAuthProfiles.set(tagged, options.capability.profiles ?? []);
|
|
149
153
|
return options.webBotAuth ? tagAuthenticatorPresenceGated(tagged) : tagged;
|
|
150
154
|
}
|
|
155
|
+
const webBotAuthProfiles = /* @__PURE__ */ new WeakMap();
|
|
156
|
+
function unsignedRequiredError(err, signatureAuth, body) {
|
|
157
|
+
const profiles = webBotAuthProfiles.get(signatureAuth);
|
|
158
|
+
if (!profiles?.includes("web-bot-auth") || !(err instanceof RequestSignatureError) || err.code !== "request_signature_required")
|
|
159
|
+
return asSignatureAuthError(err);
|
|
160
|
+
const hasBody = body !== void 0 && body !== "";
|
|
161
|
+
if (advertisesOnlyWebBotAuth(profiles)) {
|
|
162
|
+
const refused = unsignedWebBotAuthError(hasBody);
|
|
163
|
+
return new AuthError(refused.message, {
|
|
164
|
+
cause: refused,
|
|
165
|
+
status: refused.status,
|
|
166
|
+
headers: refused.headers,
|
|
167
|
+
body: refused.body
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return new AuthError(`Signature rejected (${err.code}).`, {
|
|
171
|
+
cause: err,
|
|
172
|
+
headers: { "Accept-Signature": webBotAuthAcceptSignature("sig1", hasBody) }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
151
175
|
function principalForVerifiedSigner(signer, makePrincipal) {
|
|
152
176
|
if (!SAFE_KEYID.test(signer.keyid)) {
|
|
153
177
|
throw new AuthError("Signature key id contains unsupported characters.", {
|
|
@@ -204,7 +228,7 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
|
|
|
204
228
|
try {
|
|
205
229
|
assertUnsignedWebhookAuthenticationAbsent(body);
|
|
206
230
|
} catch (err) {
|
|
207
|
-
throw
|
|
231
|
+
throw unsignedRequiredError(err, signatureAuth, body);
|
|
208
232
|
}
|
|
209
233
|
let fallbackResult = null;
|
|
210
234
|
let fallbackError;
|
|
@@ -220,7 +244,7 @@ function requireSignatureWhenPresent(signatureAuth, fallbackAuth, options = {})
|
|
|
220
244
|
assertUnsignedOperationNotRequired(operation, unsignedCapability);
|
|
221
245
|
assertUnsignedProtocolMethodNotRequired(body, unsignedCapability);
|
|
222
246
|
} catch (err) {
|
|
223
|
-
throw
|
|
247
|
+
throw unsignedRequiredError(err, signatureAuth, body);
|
|
224
248
|
}
|
|
225
249
|
if (fallbackThrew) throw fallbackError;
|
|
226
250
|
return null;
|
|
@@ -346,6 +346,12 @@ export interface RespondUnauthorizedOptions {
|
|
|
346
346
|
* directly.
|
|
347
347
|
*/
|
|
348
348
|
signatureError?: RequestSignatureErrorCode;
|
|
349
|
+
/**
|
|
350
|
+
* Additional response headers, such as the Web Bot Auth `Accept-Signature`
|
|
351
|
+
* challenge added to an AdCP RFC 9421 `request_signature_required` answer.
|
|
352
|
+
* Cannot replace `WWW-Authenticate` or `Content-Type`.
|
|
353
|
+
*/
|
|
354
|
+
headers?: Record<string, string>;
|
|
349
355
|
}
|
|
350
356
|
/**
|
|
351
357
|
* Send an RFC 6750-compliant 401/403 response with the `WWW-Authenticate`
|
|
@@ -346,6 +346,12 @@ export interface RespondUnauthorizedOptions {
|
|
|
346
346
|
* directly.
|
|
347
347
|
*/
|
|
348
348
|
signatureError?: RequestSignatureErrorCode;
|
|
349
|
+
/**
|
|
350
|
+
* Additional response headers, such as the Web Bot Auth `Accept-Signature`
|
|
351
|
+
* challenge added to an AdCP RFC 9421 `request_signature_required` answer.
|
|
352
|
+
* Cannot replace `WWW-Authenticate` or `Content-Type`.
|
|
353
|
+
*/
|
|
354
|
+
headers?: Record<string, string>;
|
|
349
355
|
}
|
|
350
356
|
/**
|
|
351
357
|
* Send an RFC 6750-compliant 401/403 response with the `WWW-Authenticate`
|
package/dist/lib/server/auth.js
CHANGED
|
@@ -247,7 +247,13 @@ function respondUnauthorized(_req, res, options = {}) {
|
|
|
247
247
|
if (options.errorDescription) parts.push(`error_description="${escapeQuotes(options.errorDescription)}"`);
|
|
248
248
|
if (options.resourceMetadata) parts.push(`resource_metadata="${escapeQuotes(options.resourceMetadata)}"`);
|
|
249
249
|
const scheme = useSignatureChallenge ? "Signature" : "Bearer";
|
|
250
|
+
const extra = Object.fromEntries(
|
|
251
|
+
Object.entries(options.headers ?? {}).filter(
|
|
252
|
+
([name]) => !["content-type", "www-authenticate"].includes(name.toLowerCase())
|
|
253
|
+
)
|
|
254
|
+
);
|
|
250
255
|
res.writeHead(options.status ?? 401, {
|
|
256
|
+
...extra,
|
|
251
257
|
"Content-Type": "application/json",
|
|
252
258
|
"WWW-Authenticate": `${scheme} ${parts.join(", ")}`
|
|
253
259
|
});
|
package/dist/lib/server/auth.mjs
CHANGED
|
@@ -208,7 +208,13 @@ function respondUnauthorized(_req, res, options = {}) {
|
|
|
208
208
|
if (options.errorDescription) parts.push(`error_description="${escapeQuotes(options.errorDescription)}"`);
|
|
209
209
|
if (options.resourceMetadata) parts.push(`resource_metadata="${escapeQuotes(options.resourceMetadata)}"`);
|
|
210
210
|
const scheme = useSignatureChallenge ? "Signature" : "Bearer";
|
|
211
|
+
const extra = Object.fromEntries(
|
|
212
|
+
Object.entries(options.headers ?? {}).filter(
|
|
213
|
+
([name]) => !["content-type", "www-authenticate"].includes(name.toLowerCase())
|
|
214
|
+
)
|
|
215
|
+
);
|
|
211
216
|
res.writeHead(options.status ?? 401, {
|
|
217
|
+
...extra,
|
|
212
218
|
"Content-Type": "application/json",
|
|
213
219
|
"WWW-Authenticate": `${scheme} ${parts.join(", ")}`
|
|
214
220
|
});
|
|
@@ -1438,6 +1438,8 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1438
1438
|
"Web Bot Auth requires origin revocation: use createIdentity33PrincipalResolver({ revocation }) or supply isRevoked"
|
|
1439
1439
|
);
|
|
1440
1440
|
const requiredFor = effectiveRequestSigning.required_for ?? [];
|
|
1441
|
+
const advertisedProfiles = effectiveRequestSigning.profiles;
|
|
1442
|
+
const webBotAuthAdvertised = !!signedRequests.webBotAuth && !!advertisedProfiles?.includes("web-bot-auth");
|
|
1441
1443
|
const verifier = (0, import_middleware.createExpressVerifier)({
|
|
1442
1444
|
capability: {
|
|
1443
1445
|
supported: true,
|
|
@@ -1543,6 +1545,19 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1543
1545
|
res.setHeader(k, v);
|
|
1544
1546
|
return {
|
|
1545
1547
|
json(body) {
|
|
1548
|
+
if (webBotAuthAdvertised && code === 401 && body?.error === "request_signature_required") {
|
|
1549
|
+
const hasBody = !!req.rawBody;
|
|
1550
|
+
if ((0, import_web_bot_auth2.advertisesOnlyWebBotAuth)(advertisedProfiles)) {
|
|
1551
|
+
const refused = (0, import_web_bot_auth2.unsignedWebBotAuthError)(hasBody);
|
|
1552
|
+
res.removeHeader("WWW-Authenticate");
|
|
1553
|
+
res.statusCode = refused.status;
|
|
1554
|
+
for (const [name, value] of Object.entries(refused.headers)) res.setHeader(name, value);
|
|
1555
|
+
res.setHeader("Content-Type", "application/json");
|
|
1556
|
+
res.end(JSON.stringify(refused.body));
|
|
1557
|
+
return;
|
|
1558
|
+
}
|
|
1559
|
+
res.setHeader("Accept-Signature", (0, import_web_bot_auth2.webBotAuthAcceptSignature)("sig1", hasBody));
|
|
1560
|
+
}
|
|
1546
1561
|
res.setHeader("Content-Type", "application/json");
|
|
1547
1562
|
res.end(JSON.stringify(body));
|
|
1548
1563
|
}
|
|
@@ -2127,6 +2142,7 @@ function createAdcpServer(config) {
|
|
|
2127
2142
|
throw new Error("Select Web Bot Auth on each trusted webhook registration, not as a server-wide default");
|
|
2128
2143
|
if (config.webhooks?.webBotAuth && !config.identity33)
|
|
2129
2144
|
throw new Error("Web Bot Auth webhook signing requires the identity33 experiment");
|
|
2145
|
+
if (config.webhooks?.webBotAuth?.key) (0, import_web_bot_auth.assertWebBotAuthSigningKey)(config.webhooks.webBotAuth.key);
|
|
2130
2146
|
const toolsWithErrorArm = (0, import_error_arm_tools.getToolsWithErrorArm)(adcpVersion);
|
|
2131
2147
|
const isProduction = process.env.NODE_ENV === "production";
|
|
2132
2148
|
const requestValidationMode = validationConfig?.requests ?? (isProduction ? "off" : "strict");
|
|
@@ -4558,9 +4574,9 @@ function createAdcpServer(config) {
|
|
|
4558
4574
|
algorithms: [
|
|
4559
4575
|
.../* @__PURE__ */ new Set([
|
|
4560
4576
|
algorithm,
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
] : []
|
|
4577
|
+
// `algorithms` lists AdCP webhook-profile values; a Web Bot Auth key
|
|
4578
|
+
// is Ed25519 or RSA-PSS (adcp#7894), and only Ed25519 has a value here.
|
|
4579
|
+
...webhooks.webBotAuth && ("privateKey" in webhooks.webBotAuth.key ? webhooks.webBotAuth.key.privateKey.crv : webhooks.webBotAuth.key.publicKey.crv) === "Ed25519" ? ["ed25519"] : []
|
|
4564
4580
|
])
|
|
4565
4581
|
],
|
|
4566
4582
|
...webhooks.webBotAuth && { profiles: ["adcp/webhook-signing/v1", "web-bot-auth"] },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getWebBotAuthLegacyOriginGate } from "../signing/web-bot-auth.mjs";
|
|
1
|
+
import { assertWebBotAuthSigningKey, getWebBotAuthLegacyOriginGate } from "../signing/web-bot-auth.mjs";
|
|
2
2
|
import {
|
|
3
3
|
isIdentity33SchemaScope,
|
|
4
4
|
withIdentity33SchemaScope,
|
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
webBotAuthToolFailure,
|
|
13
13
|
selectSignatureProfile,
|
|
14
14
|
verifyWebBotAuth,
|
|
15
|
+
advertisesOnlyWebBotAuth,
|
|
16
|
+
unsignedWebBotAuthError,
|
|
17
|
+
webBotAuthAcceptSignature,
|
|
15
18
|
isWebBotAuthRevocationChecked,
|
|
16
19
|
webBotAuthUnavailableResponse,
|
|
17
20
|
WebBotAuthUnavailableError,
|
|
@@ -1526,6 +1529,8 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1526
1529
|
"Web Bot Auth requires origin revocation: use createIdentity33PrincipalResolver({ revocation }) or supply isRevoked"
|
|
1527
1530
|
);
|
|
1528
1531
|
const requiredFor = effectiveRequestSigning.required_for ?? [];
|
|
1532
|
+
const advertisedProfiles = effectiveRequestSigning.profiles;
|
|
1533
|
+
const webBotAuthAdvertised = !!signedRequests.webBotAuth && !!advertisedProfiles?.includes("web-bot-auth");
|
|
1529
1534
|
const verifier = createExpressVerifier({
|
|
1530
1535
|
capability: {
|
|
1531
1536
|
supported: true,
|
|
@@ -1631,6 +1636,19 @@ function buildSignedRequestsPreTransport(signedRequests, effectiveRequestSigning
|
|
|
1631
1636
|
res.setHeader(k, v);
|
|
1632
1637
|
return {
|
|
1633
1638
|
json(body) {
|
|
1639
|
+
if (webBotAuthAdvertised && code === 401 && body?.error === "request_signature_required") {
|
|
1640
|
+
const hasBody = !!req.rawBody;
|
|
1641
|
+
if (advertisesOnlyWebBotAuth(advertisedProfiles)) {
|
|
1642
|
+
const refused = unsignedWebBotAuthError(hasBody);
|
|
1643
|
+
res.removeHeader("WWW-Authenticate");
|
|
1644
|
+
res.statusCode = refused.status;
|
|
1645
|
+
for (const [name, value] of Object.entries(refused.headers)) res.setHeader(name, value);
|
|
1646
|
+
res.setHeader("Content-Type", "application/json");
|
|
1647
|
+
res.end(JSON.stringify(refused.body));
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
res.setHeader("Accept-Signature", webBotAuthAcceptSignature("sig1", hasBody));
|
|
1651
|
+
}
|
|
1634
1652
|
res.setHeader("Content-Type", "application/json");
|
|
1635
1653
|
res.end(JSON.stringify(body));
|
|
1636
1654
|
}
|
|
@@ -2215,6 +2233,7 @@ function createAdcpServer(config) {
|
|
|
2215
2233
|
throw new Error("Select Web Bot Auth on each trusted webhook registration, not as a server-wide default");
|
|
2216
2234
|
if (config.webhooks?.webBotAuth && !config.identity33)
|
|
2217
2235
|
throw new Error("Web Bot Auth webhook signing requires the identity33 experiment");
|
|
2236
|
+
if (config.webhooks?.webBotAuth?.key) assertWebBotAuthSigningKey(config.webhooks.webBotAuth.key);
|
|
2218
2237
|
const toolsWithErrorArm = getToolsWithErrorArm(adcpVersion);
|
|
2219
2238
|
const isProduction = process.env.NODE_ENV === "production";
|
|
2220
2239
|
const requestValidationMode = validationConfig?.requests ?? (isProduction ? "off" : "strict");
|
|
@@ -4646,9 +4665,9 @@ function createAdcpServer(config) {
|
|
|
4646
4665
|
algorithms: [
|
|
4647
4666
|
.../* @__PURE__ */ new Set([
|
|
4648
4667
|
algorithm,
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
] : []
|
|
4668
|
+
// `algorithms` lists AdCP webhook-profile values; a Web Bot Auth key
|
|
4669
|
+
// is Ed25519 or RSA-PSS (adcp#7894), and only Ed25519 has a value here.
|
|
4670
|
+
...webhooks.webBotAuth && ("privateKey" in webhooks.webBotAuth.key ? webhooks.webBotAuth.key.privateKey.crv : webhooks.webBotAuth.key.publicKey.crv) === "Ed25519" ? ["ed25519"] : []
|
|
4652
4671
|
])
|
|
4653
4672
|
],
|
|
4654
4673
|
...webhooks.webBotAuth && { profiles: ["adcp/webhook-signing/v1", "web-bot-auth"] },
|
|
@@ -311,7 +311,10 @@ function projectResponseCreativeIdentities(value, operation, legacyFormatConvert
|
|
|
311
311
|
let projected = dataRecord;
|
|
312
312
|
if ((0, import_types2.isProjectionProductInput)(projected)) {
|
|
313
313
|
if (wireMode === "canonical") {
|
|
314
|
-
const canonical = (0, import_augment_response.toCanonicalOnlyResponse)(
|
|
314
|
+
const canonical = (0, import_augment_response.toCanonicalOnlyResponse)(
|
|
315
|
+
{ products: [projected] },
|
|
316
|
+
{ legacyFormatConverter, _inferDualEmissionLegacyRefs: false }
|
|
317
|
+
);
|
|
315
318
|
if (canonical.diagnostics.length > 0 || canonical.response.products.length !== 1) {
|
|
316
319
|
throw new import_creative_delivery.CreativeFormatProjectionError(
|
|
317
320
|
operation,
|
|
@@ -550,7 +553,8 @@ function asCanonicalProductResponse(response, legacyFormatConverter) {
|
|
|
550
553
|
assertSafeGetProductsResponse(response);
|
|
551
554
|
validateDualProductFormatDeclarations(response, legacyFormatConverter);
|
|
552
555
|
const projected = (0, import_augment_response.toCanonicalOnlyResponse)(response, {
|
|
553
|
-
legacyFormatConverter
|
|
556
|
+
legacyFormatConverter,
|
|
557
|
+
_inferDualEmissionLegacyRefs: false
|
|
554
558
|
});
|
|
555
559
|
const blockingDiagnostics = projected.diagnostics.filter(
|
|
556
560
|
(diagnostic) => diagnostic.code !== "LEGACY_FORMAT_ID_DROPPED_UNMAPPED"
|
|
@@ -343,7 +343,10 @@ function projectResponseCreativeIdentities(value, operation, legacyFormatConvert
|
|
|
343
343
|
let projected = dataRecord;
|
|
344
344
|
if (isProjectionProductInput(projected)) {
|
|
345
345
|
if (wireMode === "canonical") {
|
|
346
|
-
const canonical = toCanonicalOnlyResponse(
|
|
346
|
+
const canonical = toCanonicalOnlyResponse(
|
|
347
|
+
{ products: [projected] },
|
|
348
|
+
{ legacyFormatConverter, _inferDualEmissionLegacyRefs: false }
|
|
349
|
+
);
|
|
347
350
|
if (canonical.diagnostics.length > 0 || canonical.response.products.length !== 1) {
|
|
348
351
|
throw new CreativeFormatProjectionError(
|
|
349
352
|
operation,
|
|
@@ -582,7 +585,8 @@ function asCanonicalProductResponse(response, legacyFormatConverter) {
|
|
|
582
585
|
assertSafeGetProductsResponse(response);
|
|
583
586
|
validateDualProductFormatDeclarations(response, legacyFormatConverter);
|
|
584
587
|
const projected = toCanonicalOnlyResponse(response, {
|
|
585
|
-
legacyFormatConverter
|
|
588
|
+
legacyFormatConverter,
|
|
589
|
+
_inferDualEmissionLegacyRefs: false
|
|
586
590
|
});
|
|
587
591
|
const blockingDiagnostics = projected.diagnostics.filter(
|
|
588
592
|
(diagnostic) => diagnostic.code !== "LEGACY_FORMAT_ID_DROPPED_UNMAPPED"
|
package/dist/lib/server/serve.js
CHANGED
|
@@ -322,7 +322,8 @@ function serve(createAgent, options) {
|
|
|
322
322
|
(0, import_auth.respondUnauthorized)(req, res, {
|
|
323
323
|
signatureError: signatureCode,
|
|
324
324
|
errorDescription: publicMessage,
|
|
325
|
-
resourceMetadata: resourceMetadataUrl
|
|
325
|
+
resourceMetadata: resourceMetadataUrl,
|
|
326
|
+
...err instanceof import_auth.AuthError && err.headers && { headers: err.headers }
|
|
326
327
|
});
|
|
327
328
|
return;
|
|
328
329
|
}
|
|
@@ -305,7 +305,8 @@ function serve(createAgent, options) {
|
|
|
305
305
|
respondUnauthorized(req, res, {
|
|
306
306
|
signatureError: signatureCode,
|
|
307
307
|
errorDescription: publicMessage,
|
|
308
|
-
resourceMetadata: resourceMetadataUrl
|
|
308
|
+
resourceMetadata: resourceMetadataUrl,
|
|
309
|
+
...err instanceof AuthError && err.headers && { headers: err.headers }
|
|
309
310
|
});
|
|
310
311
|
return;
|
|
311
312
|
}
|