@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
|
@@ -83,6 +83,7 @@ function createWebhookEmitter(options) {
|
|
|
83
83
|
if (options.deliveryStore && options.idempotencyKeyStore) {
|
|
84
84
|
throw new TypeError("createWebhookEmitter: provide deliveryStore, not both deliveryStore and idempotencyKeyStore");
|
|
85
85
|
}
|
|
86
|
+
if (options.webBotAuth?.key) (0, import_web_bot_auth.assertWebBotAuthSigningKey)(options.webBotAuth.key);
|
|
86
87
|
const retryHorizonSeconds = resolveRetryHorizonSeconds(options.deliveryRetryHorizonSeconds);
|
|
87
88
|
const explicitStore = options.deliveryStore ?? options.idempotencyKeyStore;
|
|
88
89
|
const store = explicitStore ?? memoryWebhookDeliveryStore({ now: options.now });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { signWebBotAuth } from "../signing/web-bot-auth.mjs";
|
|
1
|
+
import { assertWebBotAuthSigningKey, signWebBotAuth } from "../signing/web-bot-auth.mjs";
|
|
2
2
|
import { signWebhook } from "../signing/signer.mjs";
|
|
3
3
|
import { signWebhookAsync } from "../signing/signer-async.mjs";
|
|
4
4
|
import { canonicalJsonSha256 } from "../utils/jcs.mjs";
|
|
@@ -64,6 +64,7 @@ function createWebhookEmitter(options) {
|
|
|
64
64
|
if (options.deliveryStore && options.idempotencyKeyStore) {
|
|
65
65
|
throw new TypeError("createWebhookEmitter: provide deliveryStore, not both deliveryStore and idempotencyKeyStore");
|
|
66
66
|
}
|
|
67
|
+
if (options.webBotAuth?.key) assertWebBotAuthSigningKey(options.webBotAuth.key);
|
|
67
68
|
const retryHorizonSeconds = resolveRetryHorizonSeconds(options.deliveryRetryHorizonSeconds);
|
|
68
69
|
const explicitStore = options.deliveryStore ?? options.idempotencyKeyStore;
|
|
69
70
|
const store = explicitStore ?? memoryWebhookDeliveryStore({ now: options.now });
|
|
@@ -15,5 +15,5 @@ export declare function verifySignatureAsync(alg: string, publicKey: KeyObject |
|
|
|
15
15
|
* path passes the key as a JWK so workerd accepts it (#3243).
|
|
16
16
|
*/
|
|
17
17
|
export declare function signWithPrivateJwk(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Uint8Array;
|
|
18
|
-
/** {@link signWithPrivateJwk}, preferring WebCrypto for ES256 when available. */
|
|
18
|
+
/** {@link signWithPrivateJwk}, preferring WebCrypto for ES256 and RSA-PSS when available (workerd, #3243). */
|
|
19
19
|
export declare function signWithPrivateJwkAsync(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Promise<Uint8Array>;
|
|
@@ -15,5 +15,5 @@ export declare function verifySignatureAsync(alg: string, publicKey: KeyObject |
|
|
|
15
15
|
* path passes the key as a JWK so workerd accepts it (#3243).
|
|
16
16
|
*/
|
|
17
17
|
export declare function signWithPrivateJwk(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Uint8Array;
|
|
18
|
-
/** {@link signWithPrivateJwk}, preferring WebCrypto for ES256 when available. */
|
|
18
|
+
/** {@link signWithPrivateJwk}, preferring WebCrypto for ES256 and RSA-PSS when available (workerd, #3243). */
|
|
19
19
|
export declare function signWithPrivateJwkAsync(alg: string, privateJwk: AdcpJsonWebKey, data: Uint8Array): Promise<Uint8Array>;
|
|
@@ -87,15 +87,36 @@ function privateP256Jwk(jwk) {
|
|
|
87
87
|
async function verifySignatureAsync(alg, publicKey, data, signature) {
|
|
88
88
|
const subtle = subtleCrypto();
|
|
89
89
|
const keyObject = () => isKeyObject(publicKey) ? publicKey : jwkToPublicKey(publicKey);
|
|
90
|
-
if (alg !== "ecdsa-p256-sha256"
|
|
90
|
+
if (alg !== "ecdsa-p256-sha256" && alg !== "rsa-pss-sha512" || !subtle)
|
|
91
|
+
return verifySignature(alg, keyObject(), data, signature);
|
|
91
92
|
try {
|
|
92
93
|
const jwk = isKeyObject(publicKey) ? publicKey.export({ format: "jwk" }) : publicKey;
|
|
94
|
+
if (alg === "rsa-pss-sha512") {
|
|
95
|
+
const key2 = await importRsaPssVerifyKey(subtle, jwk);
|
|
96
|
+
return await subtle.verify(RSA_PSS_SHA512_SIGN, key2, toArrayBuffer(signature), toArrayBuffer(data));
|
|
97
|
+
}
|
|
93
98
|
const key = await importP256VerifyKey(subtle, jwk);
|
|
94
99
|
return await subtle.verify(P256_SHA256, key, toArrayBuffer(signature), toArrayBuffer(data));
|
|
95
100
|
} catch {
|
|
96
101
|
return false;
|
|
97
102
|
}
|
|
98
103
|
}
|
|
104
|
+
const RSA_PSS_SHA512 = { name: "RSA-PSS", hash: "SHA-512" };
|
|
105
|
+
const RSA_PSS_SHA512_SIGN = { name: "RSA-PSS", saltLength: 64 };
|
|
106
|
+
const rsaPssVerifyKeys = /* @__PURE__ */ new Map();
|
|
107
|
+
function importRsaPssVerifyKey(subtle, jwk) {
|
|
108
|
+
if (jwk.kty !== "RSA" || typeof jwk.n !== "string" || typeof jwk.e !== "string")
|
|
109
|
+
return Promise.reject(new TypeError("Not an RSA public key"));
|
|
110
|
+
const cacheKey = `${jwk.e}.${jwk.n}`;
|
|
111
|
+
let key = rsaPssVerifyKeys.get(cacheKey);
|
|
112
|
+
if (!key) {
|
|
113
|
+
key = subtle.importKey("jwk", { kty: "RSA", n: jwk.n, e: jwk.e }, RSA_PSS_SHA512, false, ["verify"]);
|
|
114
|
+
key.catch(() => rsaPssVerifyKeys.delete(cacheKey));
|
|
115
|
+
if (rsaPssVerifyKeys.size >= 256) rsaPssVerifyKeys.delete(rsaPssVerifyKeys.keys().next().value);
|
|
116
|
+
rsaPssVerifyKeys.set(cacheKey, key);
|
|
117
|
+
}
|
|
118
|
+
return key;
|
|
119
|
+
}
|
|
99
120
|
function isKeyObject(value) {
|
|
100
121
|
return typeof value?.export === "function" && "asymmetricKeyType" in value;
|
|
101
122
|
}
|
|
@@ -118,11 +139,25 @@ function signWithPrivateJwk(alg, privateJwk, data) {
|
|
|
118
139
|
return new Uint8Array((0, import_crypto.sign)(null, data, (0, import_crypto.createPrivateKey)({ key: privateJwk, format: "jwk" })));
|
|
119
140
|
if (alg === "ecdsa-p256-sha256")
|
|
120
141
|
return new Uint8Array((0, import_crypto.sign)("sha256", data, { key: privateJwk, format: "jwk", dsaEncoding: "ieee-p1363" }));
|
|
142
|
+
if (alg === "rsa-pss-sha512")
|
|
143
|
+
return new Uint8Array(
|
|
144
|
+
(0, import_crypto.sign)("sha512", data, {
|
|
145
|
+
key: (0, import_crypto.createPrivateKey)({ key: privateJwk, format: "jwk" }),
|
|
146
|
+
padding: import_crypto.constants.RSA_PKCS1_PSS_PADDING,
|
|
147
|
+
saltLength: 64
|
|
148
|
+
})
|
|
149
|
+
);
|
|
121
150
|
throw new TypeError(`Unsupported signing algorithm: ${alg}`);
|
|
122
151
|
}
|
|
123
152
|
async function signWithPrivateJwkAsync(alg, privateJwk, data) {
|
|
124
153
|
const subtle = subtleCrypto();
|
|
125
|
-
if (alg !== "ecdsa-p256-sha256"
|
|
154
|
+
if (alg !== "ecdsa-p256-sha256" && alg !== "rsa-pss-sha512" || !subtle)
|
|
155
|
+
return signWithPrivateJwk(alg, privateJwk, data);
|
|
156
|
+
if (alg === "rsa-pss-sha512") {
|
|
157
|
+
const { kty, n, e, d, p, q, dp, dq, qi } = privateJwk;
|
|
158
|
+
const key2 = await subtle.importKey("jwk", { kty, n, e, d, p, q, dp, dq, qi }, RSA_PSS_SHA512, false, ["sign"]);
|
|
159
|
+
return new Uint8Array(await subtle.sign(RSA_PSS_SHA512_SIGN, key2, toArrayBuffer(data)));
|
|
160
|
+
}
|
|
126
161
|
const key = await subtle.importKey("jwk", privateP256Jwk(privateJwk), P256, false, ["sign"]);
|
|
127
162
|
return new Uint8Array(await subtle.sign(P256_SHA256, key, toArrayBuffer(data)));
|
|
128
163
|
}
|
|
@@ -66,15 +66,36 @@ function privateP256Jwk(jwk) {
|
|
|
66
66
|
async function verifySignatureAsync(alg, publicKey, data, signature) {
|
|
67
67
|
const subtle = subtleCrypto();
|
|
68
68
|
const keyObject = () => isKeyObject(publicKey) ? publicKey : jwkToPublicKey(publicKey);
|
|
69
|
-
if (alg !== "ecdsa-p256-sha256"
|
|
69
|
+
if (alg !== "ecdsa-p256-sha256" && alg !== "rsa-pss-sha512" || !subtle)
|
|
70
|
+
return verifySignature(alg, keyObject(), data, signature);
|
|
70
71
|
try {
|
|
71
72
|
const jwk = isKeyObject(publicKey) ? publicKey.export({ format: "jwk" }) : publicKey;
|
|
73
|
+
if (alg === "rsa-pss-sha512") {
|
|
74
|
+
const key2 = await importRsaPssVerifyKey(subtle, jwk);
|
|
75
|
+
return await subtle.verify(RSA_PSS_SHA512_SIGN, key2, toArrayBuffer(signature), toArrayBuffer(data));
|
|
76
|
+
}
|
|
72
77
|
const key = await importP256VerifyKey(subtle, jwk);
|
|
73
78
|
return await subtle.verify(P256_SHA256, key, toArrayBuffer(signature), toArrayBuffer(data));
|
|
74
79
|
} catch {
|
|
75
80
|
return false;
|
|
76
81
|
}
|
|
77
82
|
}
|
|
83
|
+
const RSA_PSS_SHA512 = { name: "RSA-PSS", hash: "SHA-512" };
|
|
84
|
+
const RSA_PSS_SHA512_SIGN = { name: "RSA-PSS", saltLength: 64 };
|
|
85
|
+
const rsaPssVerifyKeys = /* @__PURE__ */ new Map();
|
|
86
|
+
function importRsaPssVerifyKey(subtle, jwk) {
|
|
87
|
+
if (jwk.kty !== "RSA" || typeof jwk.n !== "string" || typeof jwk.e !== "string")
|
|
88
|
+
return Promise.reject(new TypeError("Not an RSA public key"));
|
|
89
|
+
const cacheKey = `${jwk.e}.${jwk.n}`;
|
|
90
|
+
let key = rsaPssVerifyKeys.get(cacheKey);
|
|
91
|
+
if (!key) {
|
|
92
|
+
key = subtle.importKey("jwk", { kty: "RSA", n: jwk.n, e: jwk.e }, RSA_PSS_SHA512, false, ["verify"]);
|
|
93
|
+
key.catch(() => rsaPssVerifyKeys.delete(cacheKey));
|
|
94
|
+
if (rsaPssVerifyKeys.size >= 256) rsaPssVerifyKeys.delete(rsaPssVerifyKeys.keys().next().value);
|
|
95
|
+
rsaPssVerifyKeys.set(cacheKey, key);
|
|
96
|
+
}
|
|
97
|
+
return key;
|
|
98
|
+
}
|
|
78
99
|
function isKeyObject(value) {
|
|
79
100
|
return typeof value?.export === "function" && "asymmetricKeyType" in value;
|
|
80
101
|
}
|
|
@@ -97,11 +118,25 @@ function signWithPrivateJwk(alg, privateJwk, data) {
|
|
|
97
118
|
return new Uint8Array(nodeSign(null, data, createPrivateKey({ key: privateJwk, format: "jwk" })));
|
|
98
119
|
if (alg === "ecdsa-p256-sha256")
|
|
99
120
|
return new Uint8Array(nodeSign("sha256", data, { key: privateJwk, format: "jwk", dsaEncoding: "ieee-p1363" }));
|
|
121
|
+
if (alg === "rsa-pss-sha512")
|
|
122
|
+
return new Uint8Array(
|
|
123
|
+
nodeSign("sha512", data, {
|
|
124
|
+
key: createPrivateKey({ key: privateJwk, format: "jwk" }),
|
|
125
|
+
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
126
|
+
saltLength: 64
|
|
127
|
+
})
|
|
128
|
+
);
|
|
100
129
|
throw new TypeError(`Unsupported signing algorithm: ${alg}`);
|
|
101
130
|
}
|
|
102
131
|
async function signWithPrivateJwkAsync(alg, privateJwk, data) {
|
|
103
132
|
const subtle = subtleCrypto();
|
|
104
|
-
if (alg !== "ecdsa-p256-sha256"
|
|
133
|
+
if (alg !== "ecdsa-p256-sha256" && alg !== "rsa-pss-sha512" || !subtle)
|
|
134
|
+
return signWithPrivateJwk(alg, privateJwk, data);
|
|
135
|
+
if (alg === "rsa-pss-sha512") {
|
|
136
|
+
const { kty, n, e, d, p, q, dp, dq, qi } = privateJwk;
|
|
137
|
+
const key2 = await subtle.importKey("jwk", { kty, n, e, d, p, q, dp, dq, qi }, RSA_PSS_SHA512, false, ["sign"]);
|
|
138
|
+
return new Uint8Array(await subtle.sign(RSA_PSS_SHA512_SIGN, key2, toArrayBuffer(data)));
|
|
139
|
+
}
|
|
105
140
|
const key = await subtle.importKey("jwk", privateP256Jwk(privateJwk), P256, false, ["sign"]);
|
|
106
141
|
return new Uint8Array(await subtle.sign(P256_SHA256, key, toArrayBuffer(data)));
|
|
107
142
|
}
|
|
@@ -24,6 +24,12 @@ export interface ParsedSignature {
|
|
|
24
24
|
bytes: Uint8Array;
|
|
25
25
|
}
|
|
26
26
|
export declare function parseSignatureInput(headerValue: string, expectedLabel?: string): ParsedSignatureInput;
|
|
27
|
+
/**
|
|
28
|
+
* {@link parseSignatureInput} for one member of an already parsed (and
|
|
29
|
+
* duplicate-checked) `Signature-Input` dictionary, so a caller that needs
|
|
30
|
+
* every label parses the field once.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseSignatureInputMember(label: string, entry: unknown): ParsedSignatureInput;
|
|
27
33
|
export declare function parseSignature(headerValue: string, expectedLabel: string, encoding?: SfBinaryEncoding): ParsedSignature;
|
|
28
34
|
/**
|
|
29
35
|
* RFC 8941 / RFC 9651 parsers deduplicate dictionary keys and parameter keys
|
|
@@ -24,6 +24,12 @@ export interface ParsedSignature {
|
|
|
24
24
|
bytes: Uint8Array;
|
|
25
25
|
}
|
|
26
26
|
export declare function parseSignatureInput(headerValue: string, expectedLabel?: string): ParsedSignatureInput;
|
|
27
|
+
/**
|
|
28
|
+
* {@link parseSignatureInput} for one member of an already parsed (and
|
|
29
|
+
* duplicate-checked) `Signature-Input` dictionary, so a caller that needs
|
|
30
|
+
* every label parses the field once.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseSignatureInputMember(label: string, entry: unknown): ParsedSignatureInput;
|
|
27
33
|
export declare function parseSignature(headerValue: string, expectedLabel: string, encoding?: SfBinaryEncoding): ParsedSignature;
|
|
28
34
|
/**
|
|
29
35
|
* RFC 8941 / RFC 9651 parsers deduplicate dictionary keys and parameter keys
|
|
@@ -20,6 +20,7 @@ var parser_exports = {};
|
|
|
20
20
|
__export(parser_exports, {
|
|
21
21
|
parseSignature: () => parseSignature,
|
|
22
22
|
parseSignatureInput: () => parseSignatureInput,
|
|
23
|
+
parseSignatureInputMember: () => parseSignatureInputMember,
|
|
23
24
|
rejectDuplicateDictionaryKeys: () => rejectDuplicateDictionaryKeys
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(parser_exports);
|
|
@@ -43,7 +44,9 @@ function parseSignatureInput(headerValue, expectedLabel) {
|
|
|
43
44
|
if (dict.size === 0) malformed("Signature-Input header is empty");
|
|
44
45
|
const label = expectedLabel ?? (dict.has("sig1") ? "sig1" : dict.keys().next().value);
|
|
45
46
|
if (!dict.has(label)) malformed("Signature-Input label is missing");
|
|
46
|
-
|
|
47
|
+
return parseSignatureInputMember(label, dict.get(label));
|
|
48
|
+
}
|
|
49
|
+
function parseSignatureInputMember(label, entry) {
|
|
47
50
|
if (!isInnerList(entry)) {
|
|
48
51
|
malformed("Signature-Input value must be a parenthesized component list");
|
|
49
52
|
}
|
|
@@ -255,5 +258,6 @@ function normalizeByteSequenceBase64(input) {
|
|
|
255
258
|
0 && (module.exports = {
|
|
256
259
|
parseSignature,
|
|
257
260
|
parseSignatureInput,
|
|
261
|
+
parseSignatureInputMember,
|
|
258
262
|
rejectDuplicateDictionaryKeys
|
|
259
263
|
});
|
|
@@ -18,7 +18,9 @@ function parseSignatureInput(headerValue, expectedLabel) {
|
|
|
18
18
|
if (dict.size === 0) malformed("Signature-Input header is empty");
|
|
19
19
|
const label = expectedLabel ?? (dict.has("sig1") ? "sig1" : dict.keys().next().value);
|
|
20
20
|
if (!dict.has(label)) malformed("Signature-Input label is missing");
|
|
21
|
-
|
|
21
|
+
return parseSignatureInputMember(label, dict.get(label));
|
|
22
|
+
}
|
|
23
|
+
function parseSignatureInputMember(label, entry) {
|
|
22
24
|
if (!isInnerList(entry)) {
|
|
23
25
|
malformed("Signature-Input value must be a parenthesized component list");
|
|
24
26
|
}
|
|
@@ -229,5 +231,6 @@ function normalizeByteSequenceBase64(input) {
|
|
|
229
231
|
export {
|
|
230
232
|
parseSignature,
|
|
231
233
|
parseSignatureInput,
|
|
234
|
+
parseSignatureInputMember,
|
|
232
235
|
rejectDuplicateDictionaryKeys
|
|
233
236
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type SsrfFetchOptions } from '../net/index.mjs';
|
|
2
2
|
import { type RequestLike, type ResponseLike } from './canonicalize.mjs';
|
|
3
|
-
import { type ParsedSignatureInput } from './parser.mjs';
|
|
4
3
|
import type { JwksResolver } from './jwks.mjs';
|
|
5
4
|
import type { ReplayStore } from './replay.mjs';
|
|
6
5
|
import type { AdcpJsonWebKey } from './types.mjs';
|
|
@@ -9,11 +8,21 @@ export declare const WEB_BOT_AUTH_PROFILE: "web-bot-auth";
|
|
|
9
8
|
export declare const WEB_BOT_AUTH_DRAFT_REVISION: "draft-ietf-webbotauth-httpsig-protocol-00";
|
|
10
9
|
export declare const SIGNATURE_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
11
10
|
export declare const SIGNATURE_DIRECTORY_MEDIA_TYPE = "application/http-message-signatures-directory+json";
|
|
11
|
+
/** The profile's signature algorithms (wba-profile.mdx "Profile rules"). ES256 is RFC 9421 profile only. */
|
|
12
|
+
export declare const WEB_BOT_AUTH_ALGORITHMS: readonly ["ed25519", "rsa-pss-sha512"];
|
|
12
13
|
/**
|
|
13
|
-
* Signature
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
14
|
+
* The profile's `Accept-Signature` member (RFC 9421 §5.1): the label, the
|
|
15
|
+
* components the profile requires, and its parameters. A replay challenge
|
|
16
|
+
* carries the fresh `nonce` the signer uses on its next attempt.
|
|
17
|
+
*/
|
|
18
|
+
export declare function webBotAuthAcceptSignature(label?: string, hasBody?: boolean, nonce?: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Signature-path outcome (wba-profile.mdx "Error responses"): 400 when the
|
|
21
|
+
* signature fields do not parse; 403 with `Accept-Signature` when a signature
|
|
22
|
+
* fails verification or the profile rules (without it for a revoked key); 429
|
|
23
|
+
* with `Accept-Signature` carrying a fresh `nonce` for a reused signature or
|
|
24
|
+
* nonce. A 429 for local load shedding carries `Retry-After` instead. Any AdCP
|
|
25
|
+
* body is the generic authentication error; detail stays in `reason` (logs).
|
|
17
26
|
* Infrastructure the verifier depends on is reported separately by
|
|
18
27
|
* {@link WebBotAuthUnavailableError}.
|
|
19
28
|
*/
|
|
@@ -40,6 +49,16 @@ export declare class WebBotAuthUnavailableError extends Error {
|
|
|
40
49
|
readonly reason: string;
|
|
41
50
|
constructor(reason: string, cause?: unknown);
|
|
42
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The answer to an unsigned request to an operation in `required_for` from a
|
|
54
|
+
* caller with no other credential the verifier accepts, when the verifier
|
|
55
|
+
* advertises only `web-bot-auth`: 403 with `Accept-Signature`. A verifier that
|
|
56
|
+
* also advertises the AdCP RFC 9421 profile answers as that profile does (401
|
|
57
|
+
* `request_signature_required`) and adds {@link webBotAuthAcceptSignature}.
|
|
58
|
+
*/
|
|
59
|
+
export declare function unsignedWebBotAuthError(hasBody: boolean): WebBotAuthError;
|
|
60
|
+
/** Whether a `request_signing.profiles` advertisement names Web Bot Auth and not the AdCP RFC 9421 profile. */
|
|
61
|
+
export declare function advertisesOnlyWebBotAuth(profiles: readonly string[] | undefined): boolean;
|
|
43
62
|
export declare function selectSignatureProfile(request: Pick<RequestLike, 'headers'>): 'web-bot-auth' | 'rfc9421';
|
|
44
63
|
/**
|
|
45
64
|
* RFC 7638 JWK SHA-256 thumbprint, synchronous. Byte-identical to jose's
|
|
@@ -49,10 +68,13 @@ export declare function jwkThumbprint(jwk: Partial<Pick<AdcpJsonWebKey, 'kty' |
|
|
|
49
68
|
n?: unknown;
|
|
50
69
|
e?: unknown;
|
|
51
70
|
}): string;
|
|
71
|
+
/**
|
|
72
|
+
* Refuse a Web Bot Auth signing key the profile does not allow, at wire-up
|
|
73
|
+
* time rather than on the first request.
|
|
74
|
+
*/
|
|
75
|
+
export declare function assertWebBotAuthSigningKey(key: WebBotSigningKey): void;
|
|
52
76
|
/** Reject authority normalization differentials on received identifiers. */
|
|
53
77
|
export declare function receivedAgentOrigin(value: string): string;
|
|
54
|
-
/** RFC 9421 dictionary-member extraction, including relay member coverage. */
|
|
55
|
-
export declare function buildWebBotAuthSignatureBase(input: ParsedSignatureInput, request: RequestLike, response?: ResponseLike): string;
|
|
56
78
|
export declare function getWebBotAuthLegacyOriginGate(resolver: Function | undefined): ((origin: string) => Promise<void>) | undefined;
|
|
57
79
|
export interface WebBotAuthAuthorizationFailure {
|
|
58
80
|
code: 'PERMISSION_DENIED' | 'SERVICE_UNAVAILABLE';
|
|
@@ -92,7 +114,10 @@ export interface VerifyWebBotAuthOptions {
|
|
|
92
114
|
/** Atomic, shared across endpoints and replicas. Nonce scope is the identity origin, not the rotating kid. */
|
|
93
115
|
replayStore: ReplayStore;
|
|
94
116
|
now?: () => number;
|
|
95
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* @deprecated No effect. A reused signature or nonce is always 429 with
|
|
119
|
+
* `Accept-Signature` carrying a fresh nonce (adcp#7894, `negative/002`).
|
|
120
|
+
*/
|
|
96
121
|
replayStatus?: 403 | 429;
|
|
97
122
|
/**
|
|
98
123
|
* @deprecated Revocation is an identity and authorization check, not part of
|
|
@@ -108,30 +133,39 @@ export interface VerifyWebBotAuthOptions {
|
|
|
108
133
|
/** @deprecated No effect; revocation outcomes are no longer deferred into tool errors. */
|
|
109
134
|
deferAuthorizationErrors?: boolean;
|
|
110
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Verify a Web Bot Auth request (wba-profile.mdx "Verification"). Resolves to
|
|
138
|
+
* exactly one verified signer: the profile takes one `web-bot-auth` signature
|
|
139
|
+
* and one `Signature-Agent` member per request. The array shape is kept for
|
|
140
|
+
* principal resolvers written against earlier drafts.
|
|
141
|
+
*/
|
|
111
142
|
export declare function verifyWebBotAuth(request: RequestLike, options: VerifyWebBotAuthOptions): Promise<VerifiedWebBotSigner[]>;
|
|
112
143
|
export interface VerifiedSignatureDirectory {
|
|
113
144
|
keys: AdcpJsonWebKey[];
|
|
114
|
-
/**
|
|
145
|
+
/** The earliest proof expiry. */
|
|
115
146
|
expires: number;
|
|
116
|
-
/**
|
|
117
|
-
signed:
|
|
147
|
+
/** @deprecated Always `true`: the profile accepts only signed directories (WBA Appendix B.1). */
|
|
148
|
+
signed: true;
|
|
118
149
|
/** Preserve exact bytes and response headers for auditing. */
|
|
119
150
|
proof: ResponseLike;
|
|
120
151
|
}
|
|
121
152
|
/**
|
|
122
|
-
* Verify a key directory response (
|
|
123
|
-
* Appendix B
|
|
124
|
-
* `
|
|
125
|
-
*
|
|
126
|
-
*
|
|
153
|
+
* Verify a key directory response (wba-profile.mdx "Key directories", WBA
|
|
154
|
+
* Appendix B.1). The response must be signed once per key, each signature
|
|
155
|
+
* covering `"@authority";req` and `content-digest` with `created`, `expires`,
|
|
156
|
+
* the key's thumbprint as `keyid` and `tag="http-message-signatures-directory"`,
|
|
157
|
+
* and verifying under the key the directory itself provides. An unsigned
|
|
158
|
+
* directory is a discovery failure.
|
|
127
159
|
*/
|
|
128
160
|
export declare function verifyKeyDirectory(response: ResponseLike, now?: number, options?: {
|
|
129
161
|
allowTestKeys?: boolean;
|
|
162
|
+
/** @deprecated No effect: directories must always be signed. */
|
|
130
163
|
requireSigned?: boolean;
|
|
164
|
+
/** @deprecated No effect: unsigned directories are refused. */
|
|
131
165
|
maxUnsignedAgeSeconds?: number;
|
|
132
|
-
}): VerifiedSignatureDirectory
|
|
133
|
-
/**
|
|
134
|
-
export declare function verifySignatureDirectory(response: ResponseLike, now?: number, allowTestKeys?: boolean): VerifiedSignatureDirectory
|
|
166
|
+
}): Promise<VerifiedSignatureDirectory>;
|
|
167
|
+
/** @deprecated Same as {@link verifyKeyDirectory}: every directory must be signed. */
|
|
168
|
+
export declare function verifySignatureDirectory(response: ResponseLike, now?: number, allowTestKeys?: boolean): Promise<VerifiedSignatureDirectory>;
|
|
135
169
|
export interface SignatureDirectoryResolverOptions {
|
|
136
170
|
now?: () => number;
|
|
137
171
|
fetchOptions?: Pick<SsrfFetchOptions, 'lookup' | 'trustedFetchFn'>;
|
|
@@ -139,9 +173,9 @@ export interface SignatureDirectoryResolverOptions {
|
|
|
139
173
|
maxOrigins?: number;
|
|
140
174
|
/** Globally bound concurrency across attacker-selected origins. */
|
|
141
175
|
maxConcurrentFetches?: number;
|
|
142
|
-
/**
|
|
176
|
+
/** @deprecated No effect: signed directories (WBA Appendix B.1) are always required. */
|
|
143
177
|
requireSignedDirectories?: boolean;
|
|
144
|
-
/**
|
|
178
|
+
/** @deprecated No effect: unsigned directories are refused. */
|
|
145
179
|
maxUnsignedDirectoryAgeSeconds?: number;
|
|
146
180
|
}
|
|
147
181
|
/**
|
|
@@ -168,9 +202,9 @@ export interface WebBotSigningOptions {
|
|
|
168
202
|
origin: string;
|
|
169
203
|
label?: string;
|
|
170
204
|
now?: () => number;
|
|
205
|
+
/** 64 bytes, unpadded Base64URL or canonical Base64. Defaults to 64 fresh random bytes. */
|
|
171
206
|
nonce?: string;
|
|
172
207
|
windowSeconds?: number;
|
|
173
|
-
relay?: boolean;
|
|
174
208
|
}
|
|
175
209
|
export type WebBotSigningKey = {
|
|
176
210
|
privateKey: AdcpJsonWebKey;
|
|
@@ -178,6 +212,11 @@ export type WebBotSigningKey = {
|
|
|
178
212
|
publicKey: AdcpJsonWebKey;
|
|
179
213
|
provider: SigningProvider;
|
|
180
214
|
};
|
|
215
|
+
/**
|
|
216
|
+
* Sign a request under the profile: one `web-bot-auth` signature labelled
|
|
217
|
+
* `label`, one covered `Signature-Agent` member naming `origin`, a 64-byte
|
|
218
|
+
* nonce, and a lifetime of at most 300 seconds.
|
|
219
|
+
*/
|
|
181
220
|
export declare function signWebBotAuth(request: RequestLike, key: WebBotSigningKey, options: WebBotSigningOptions): Promise<Record<string, string>>;
|
|
182
221
|
/** Regenerate after rotation and periodically before proofs expire. No private material is published. */
|
|
183
222
|
export declare function createSignedSignatureDirectory(originUrl: string, signingKeys: WebBotSigningKey[], options?: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type SsrfFetchOptions } from '../net';
|
|
2
2
|
import { type RequestLike, type ResponseLike } from './canonicalize';
|
|
3
|
-
import { type ParsedSignatureInput } from './parser';
|
|
4
3
|
import type { JwksResolver } from './jwks';
|
|
5
4
|
import type { ReplayStore } from './replay';
|
|
6
5
|
import type { AdcpJsonWebKey } from './types';
|
|
@@ -9,11 +8,21 @@ export declare const WEB_BOT_AUTH_PROFILE: "web-bot-auth";
|
|
|
9
8
|
export declare const WEB_BOT_AUTH_DRAFT_REVISION: "draft-ietf-webbotauth-httpsig-protocol-00";
|
|
10
9
|
export declare const SIGNATURE_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
11
10
|
export declare const SIGNATURE_DIRECTORY_MEDIA_TYPE = "application/http-message-signatures-directory+json";
|
|
11
|
+
/** The profile's signature algorithms (wba-profile.mdx "Profile rules"). ES256 is RFC 9421 profile only. */
|
|
12
|
+
export declare const WEB_BOT_AUTH_ALGORITHMS: readonly ["ed25519", "rsa-pss-sha512"];
|
|
12
13
|
/**
|
|
13
|
-
* Signature
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
14
|
+
* The profile's `Accept-Signature` member (RFC 9421 §5.1): the label, the
|
|
15
|
+
* components the profile requires, and its parameters. A replay challenge
|
|
16
|
+
* carries the fresh `nonce` the signer uses on its next attempt.
|
|
17
|
+
*/
|
|
18
|
+
export declare function webBotAuthAcceptSignature(label?: string, hasBody?: boolean, nonce?: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Signature-path outcome (wba-profile.mdx "Error responses"): 400 when the
|
|
21
|
+
* signature fields do not parse; 403 with `Accept-Signature` when a signature
|
|
22
|
+
* fails verification or the profile rules (without it for a revoked key); 429
|
|
23
|
+
* with `Accept-Signature` carrying a fresh `nonce` for a reused signature or
|
|
24
|
+
* nonce. A 429 for local load shedding carries `Retry-After` instead. Any AdCP
|
|
25
|
+
* body is the generic authentication error; detail stays in `reason` (logs).
|
|
17
26
|
* Infrastructure the verifier depends on is reported separately by
|
|
18
27
|
* {@link WebBotAuthUnavailableError}.
|
|
19
28
|
*/
|
|
@@ -40,6 +49,16 @@ export declare class WebBotAuthUnavailableError extends Error {
|
|
|
40
49
|
readonly reason: string;
|
|
41
50
|
constructor(reason: string, cause?: unknown);
|
|
42
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The answer to an unsigned request to an operation in `required_for` from a
|
|
54
|
+
* caller with no other credential the verifier accepts, when the verifier
|
|
55
|
+
* advertises only `web-bot-auth`: 403 with `Accept-Signature`. A verifier that
|
|
56
|
+
* also advertises the AdCP RFC 9421 profile answers as that profile does (401
|
|
57
|
+
* `request_signature_required`) and adds {@link webBotAuthAcceptSignature}.
|
|
58
|
+
*/
|
|
59
|
+
export declare function unsignedWebBotAuthError(hasBody: boolean): WebBotAuthError;
|
|
60
|
+
/** Whether a `request_signing.profiles` advertisement names Web Bot Auth and not the AdCP RFC 9421 profile. */
|
|
61
|
+
export declare function advertisesOnlyWebBotAuth(profiles: readonly string[] | undefined): boolean;
|
|
43
62
|
export declare function selectSignatureProfile(request: Pick<RequestLike, 'headers'>): 'web-bot-auth' | 'rfc9421';
|
|
44
63
|
/**
|
|
45
64
|
* RFC 7638 JWK SHA-256 thumbprint, synchronous. Byte-identical to jose's
|
|
@@ -49,10 +68,13 @@ export declare function jwkThumbprint(jwk: Partial<Pick<AdcpJsonWebKey, 'kty' |
|
|
|
49
68
|
n?: unknown;
|
|
50
69
|
e?: unknown;
|
|
51
70
|
}): string;
|
|
71
|
+
/**
|
|
72
|
+
* Refuse a Web Bot Auth signing key the profile does not allow, at wire-up
|
|
73
|
+
* time rather than on the first request.
|
|
74
|
+
*/
|
|
75
|
+
export declare function assertWebBotAuthSigningKey(key: WebBotSigningKey): void;
|
|
52
76
|
/** Reject authority normalization differentials on received identifiers. */
|
|
53
77
|
export declare function receivedAgentOrigin(value: string): string;
|
|
54
|
-
/** RFC 9421 dictionary-member extraction, including relay member coverage. */
|
|
55
|
-
export declare function buildWebBotAuthSignatureBase(input: ParsedSignatureInput, request: RequestLike, response?: ResponseLike): string;
|
|
56
78
|
export declare function getWebBotAuthLegacyOriginGate(resolver: Function | undefined): ((origin: string) => Promise<void>) | undefined;
|
|
57
79
|
export interface WebBotAuthAuthorizationFailure {
|
|
58
80
|
code: 'PERMISSION_DENIED' | 'SERVICE_UNAVAILABLE';
|
|
@@ -92,7 +114,10 @@ export interface VerifyWebBotAuthOptions {
|
|
|
92
114
|
/** Atomic, shared across endpoints and replicas. Nonce scope is the identity origin, not the rotating kid. */
|
|
93
115
|
replayStore: ReplayStore;
|
|
94
116
|
now?: () => number;
|
|
95
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* @deprecated No effect. A reused signature or nonce is always 429 with
|
|
119
|
+
* `Accept-Signature` carrying a fresh nonce (adcp#7894, `negative/002`).
|
|
120
|
+
*/
|
|
96
121
|
replayStatus?: 403 | 429;
|
|
97
122
|
/**
|
|
98
123
|
* @deprecated Revocation is an identity and authorization check, not part of
|
|
@@ -108,30 +133,39 @@ export interface VerifyWebBotAuthOptions {
|
|
|
108
133
|
/** @deprecated No effect; revocation outcomes are no longer deferred into tool errors. */
|
|
109
134
|
deferAuthorizationErrors?: boolean;
|
|
110
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Verify a Web Bot Auth request (wba-profile.mdx "Verification"). Resolves to
|
|
138
|
+
* exactly one verified signer: the profile takes one `web-bot-auth` signature
|
|
139
|
+
* and one `Signature-Agent` member per request. The array shape is kept for
|
|
140
|
+
* principal resolvers written against earlier drafts.
|
|
141
|
+
*/
|
|
111
142
|
export declare function verifyWebBotAuth(request: RequestLike, options: VerifyWebBotAuthOptions): Promise<VerifiedWebBotSigner[]>;
|
|
112
143
|
export interface VerifiedSignatureDirectory {
|
|
113
144
|
keys: AdcpJsonWebKey[];
|
|
114
|
-
/**
|
|
145
|
+
/** The earliest proof expiry. */
|
|
115
146
|
expires: number;
|
|
116
|
-
/**
|
|
117
|
-
signed:
|
|
147
|
+
/** @deprecated Always `true`: the profile accepts only signed directories (WBA Appendix B.1). */
|
|
148
|
+
signed: true;
|
|
118
149
|
/** Preserve exact bytes and response headers for auditing. */
|
|
119
150
|
proof: ResponseLike;
|
|
120
151
|
}
|
|
121
152
|
/**
|
|
122
|
-
* Verify a key directory response (
|
|
123
|
-
* Appendix B
|
|
124
|
-
* `
|
|
125
|
-
*
|
|
126
|
-
*
|
|
153
|
+
* Verify a key directory response (wba-profile.mdx "Key directories", WBA
|
|
154
|
+
* Appendix B.1). The response must be signed once per key, each signature
|
|
155
|
+
* covering `"@authority";req` and `content-digest` with `created`, `expires`,
|
|
156
|
+
* the key's thumbprint as `keyid` and `tag="http-message-signatures-directory"`,
|
|
157
|
+
* and verifying under the key the directory itself provides. An unsigned
|
|
158
|
+
* directory is a discovery failure.
|
|
127
159
|
*/
|
|
128
160
|
export declare function verifyKeyDirectory(response: ResponseLike, now?: number, options?: {
|
|
129
161
|
allowTestKeys?: boolean;
|
|
162
|
+
/** @deprecated No effect: directories must always be signed. */
|
|
130
163
|
requireSigned?: boolean;
|
|
164
|
+
/** @deprecated No effect: unsigned directories are refused. */
|
|
131
165
|
maxUnsignedAgeSeconds?: number;
|
|
132
|
-
}): VerifiedSignatureDirectory
|
|
133
|
-
/**
|
|
134
|
-
export declare function verifySignatureDirectory(response: ResponseLike, now?: number, allowTestKeys?: boolean): VerifiedSignatureDirectory
|
|
166
|
+
}): Promise<VerifiedSignatureDirectory>;
|
|
167
|
+
/** @deprecated Same as {@link verifyKeyDirectory}: every directory must be signed. */
|
|
168
|
+
export declare function verifySignatureDirectory(response: ResponseLike, now?: number, allowTestKeys?: boolean): Promise<VerifiedSignatureDirectory>;
|
|
135
169
|
export interface SignatureDirectoryResolverOptions {
|
|
136
170
|
now?: () => number;
|
|
137
171
|
fetchOptions?: Pick<SsrfFetchOptions, 'lookup' | 'trustedFetchFn'>;
|
|
@@ -139,9 +173,9 @@ export interface SignatureDirectoryResolverOptions {
|
|
|
139
173
|
maxOrigins?: number;
|
|
140
174
|
/** Globally bound concurrency across attacker-selected origins. */
|
|
141
175
|
maxConcurrentFetches?: number;
|
|
142
|
-
/**
|
|
176
|
+
/** @deprecated No effect: signed directories (WBA Appendix B.1) are always required. */
|
|
143
177
|
requireSignedDirectories?: boolean;
|
|
144
|
-
/**
|
|
178
|
+
/** @deprecated No effect: unsigned directories are refused. */
|
|
145
179
|
maxUnsignedDirectoryAgeSeconds?: number;
|
|
146
180
|
}
|
|
147
181
|
/**
|
|
@@ -168,9 +202,9 @@ export interface WebBotSigningOptions {
|
|
|
168
202
|
origin: string;
|
|
169
203
|
label?: string;
|
|
170
204
|
now?: () => number;
|
|
205
|
+
/** 64 bytes, unpadded Base64URL or canonical Base64. Defaults to 64 fresh random bytes. */
|
|
171
206
|
nonce?: string;
|
|
172
207
|
windowSeconds?: number;
|
|
173
|
-
relay?: boolean;
|
|
174
208
|
}
|
|
175
209
|
export type WebBotSigningKey = {
|
|
176
210
|
privateKey: AdcpJsonWebKey;
|
|
@@ -178,6 +212,11 @@ export type WebBotSigningKey = {
|
|
|
178
212
|
publicKey: AdcpJsonWebKey;
|
|
179
213
|
provider: SigningProvider;
|
|
180
214
|
};
|
|
215
|
+
/**
|
|
216
|
+
* Sign a request under the profile: one `web-bot-auth` signature labelled
|
|
217
|
+
* `label`, one covered `Signature-Agent` member naming `origin`, a 64-byte
|
|
218
|
+
* nonce, and a lifetime of at most 300 seconds.
|
|
219
|
+
*/
|
|
181
220
|
export declare function signWebBotAuth(request: RequestLike, key: WebBotSigningKey, options: WebBotSigningOptions): Promise<Record<string, string>>;
|
|
182
221
|
/** Regenerate after rotation and periodically before proofs expire. No private material is published. */
|
|
183
222
|
export declare function createSignedSignatureDirectory(originUrl: string, signingKeys: WebBotSigningKey[], options?: {
|