@adcp/sdk 16.0.0-beta.1 → 16.0.0-beta.2
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/SingleAgentClient.d.mts +10 -1
- package/dist/lib/core/SingleAgentClient.d.ts +10 -1
- package/dist/lib/core/SingleAgentClient.js +29 -8
- package/dist/lib/core/SingleAgentClient.mjs +29 -8
- package/dist/lib/identity-3.3/generated-compat.d.mts +34 -0
- package/dist/lib/identity-3.3/generated-compat.d.ts +34 -0
- package/dist/lib/identity-3.3/generated-compat.js +16 -0
- package/dist/lib/identity-3.3/generated-compat.mjs +0 -0
- package/dist/lib/identity-3.3/index.d.mts +2 -2
- package/dist/lib/identity-3.3/index.d.ts +2 -2
- package/dist/lib/identity-3.3/index.js +4 -0
- package/dist/lib/identity-3.3/index.mjs +5 -1
- package/dist/lib/identity-3.3/lifecycle.d.mts +17 -5
- package/dist/lib/identity-3.3/lifecycle.d.ts +17 -5
- package/dist/lib/identity-3.3/lifecycle.js +32 -5
- package/dist/lib/identity-3.3/lifecycle.mjs +38 -6
- package/dist/lib/identity-3.3/revocation.d.mts +39 -3
- package/dist/lib/identity-3.3/revocation.d.ts +39 -3
- package/dist/lib/identity-3.3/revocation.js +88 -18
- package/dist/lib/identity-3.3/revocation.mjs +89 -19
- package/dist/lib/identity-3.3/server.d.mts +12 -0
- package/dist/lib/identity-3.3/server.d.ts +12 -0
- package/dist/lib/identity-3.3/server.js +103 -43
- package/dist/lib/identity-3.3/server.mjs +111 -45
- package/dist/lib/identity-3.3/trust.d.mts +62 -1
- package/dist/lib/identity-3.3/trust.d.ts +62 -1
- package/dist/lib/identity-3.3/trust.js +126 -21
- package/dist/lib/identity-3.3/trust.mjs +124 -21
- package/dist/lib/identity-3.3/types.d.mts +39 -11
- package/dist/lib/identity-3.3/types.d.ts +39 -11
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.mjs +2 -0
- package/dist/lib/protocols/index.d.mts +1 -1
- package/dist/lib/protocols/index.d.ts +1 -1
- package/dist/lib/protocols/index.js +2 -2
- package/dist/lib/protocols/index.mjs +2 -2
- package/dist/lib/reporting/bigquery/index.js +8 -0
- package/dist/lib/reporting/bigquery/index.mjs +8 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/auth-signature.d.mts +6 -1
- package/dist/lib/server/auth-signature.d.ts +6 -1
- package/dist/lib/server/auth-signature.js +24 -13
- package/dist/lib/server/auth-signature.mjs +29 -13
- package/dist/lib/server/auth.d.mts +2 -2
- package/dist/lib/server/auth.d.ts +2 -2
- package/dist/lib/server/create-adcp-server.js +31 -10
- package/dist/lib/server/create-adcp-server.mjs +37 -11
- package/dist/lib/signing/crypto.js +8 -0
- package/dist/lib/signing/crypto.mjs +9 -1
- package/dist/lib/signing/fetch-async.js +1 -1
- package/dist/lib/signing/fetch-async.mjs +1 -1
- package/dist/lib/signing/fetch.js +1 -1
- package/dist/lib/signing/fetch.mjs +1 -1
- package/dist/lib/signing/middleware.js +9 -0
- package/dist/lib/signing/middleware.mjs +14 -0
- package/dist/lib/signing/parser.d.mts +8 -5
- package/dist/lib/signing/parser.d.ts +8 -5
- package/dist/lib/signing/parser.js +77 -61
- package/dist/lib/signing/parser.mjs +77 -61
- package/dist/lib/signing/revocation.js +3 -0
- package/dist/lib/signing/revocation.mjs +3 -0
- package/dist/lib/signing/verifier.js +1 -1
- package/dist/lib/signing/verifier.mjs +1 -1
- package/dist/lib/signing/web-bot-auth.d.mts +84 -10
- package/dist/lib/signing/web-bot-auth.d.ts +84 -10
- package/dist/lib/signing/web-bot-auth.js +259 -82
- package/dist/lib/signing/web-bot-auth.mjs +259 -87
- package/dist/lib/signing/webhook-verifier.js +1 -1
- package/dist/lib/signing/webhook-verifier.mjs +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/v2/projection/catalog-snapshot.js +3 -1
- package/dist/lib/v2/projection/catalog-snapshot.mjs +3 -1
- package/dist/lib/v2/projection/creative-delivery.d.mts +31 -9
- package/dist/lib/v2/projection/creative-delivery.d.ts +31 -9
- package/dist/lib/v2/projection/index.d.mts +1 -1
- package/dist/lib/v2/projection/index.d.ts +1 -1
- package/dist/lib/v2/projection/index.js +2 -0
- package/dist/lib/v2/projection/index.mjs +2 -0
- package/dist/lib/v2/projection/legacy-routes.d.mts +10 -0
- package/dist/lib/v2/projection/legacy-routes.d.ts +10 -0
- package/dist/lib/v2/projection/legacy-routes.js +7 -0
- package/dist/lib/v2/projection/legacy-routes.mjs +6 -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/TYPE-SUMMARY.md +2 -2
- package/docs/guides/REPORTING-LEDGER.md +15 -5
- package/docs/guides/REPORTING-OPERATIONS.md +17 -11
- package/docs/llms.txt +2 -2
- package/docs/migration-12-to-13.md +8 -0
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-identity-3.3.md +150 -11
- package/package.json +3 -1
|
@@ -24,21 +24,28 @@ __export(web_bot_auth_exports, {
|
|
|
24
24
|
WEB_BOT_AUTH_DRAFT_REVISION: () => WEB_BOT_AUTH_DRAFT_REVISION,
|
|
25
25
|
WEB_BOT_AUTH_PROFILE: () => WEB_BOT_AUTH_PROFILE,
|
|
26
26
|
WebBotAuthError: () => WebBotAuthError,
|
|
27
|
+
WebBotAuthUnavailableError: () => WebBotAuthUnavailableError,
|
|
27
28
|
buildWebBotAuthSignatureBase: () => buildWebBotAuthSignatureBase,
|
|
28
29
|
createSignedSignatureDirectory: () => createSignedSignatureDirectory,
|
|
29
30
|
createWebBotAuthAuthorizationFailure: () => createWebBotAuthAuthorizationFailure,
|
|
30
31
|
createWebBotAuthFetch: () => createWebBotAuthFetch,
|
|
32
|
+
deferWebBotAuthPrincipalFailure: () => deferWebBotAuthPrincipalFailure,
|
|
31
33
|
getWebBotAuthAuthorizationFailure: () => getWebBotAuthAuthorizationFailure,
|
|
32
34
|
getWebBotAuthLegacyOriginGate: () => getWebBotAuthLegacyOriginGate,
|
|
33
35
|
isVerifiedWebBotSigner: () => isVerifiedWebBotSigner,
|
|
36
|
+
isWebBotAuthRevocationChecked: () => isWebBotAuthRevocationChecked,
|
|
34
37
|
jwkThumbprint: () => jwkThumbprint,
|
|
35
38
|
receivedAgentOrigin: () => receivedAgentOrigin,
|
|
36
39
|
registerWebBotAuthLegacyOriginGate: () => registerWebBotAuthLegacyOriginGate,
|
|
40
|
+
registerWebBotAuthRevocationCheck: () => registerWebBotAuthRevocationCheck,
|
|
37
41
|
selectSignatureProfile: () => selectSignatureProfile,
|
|
38
42
|
signWebBotAuth: () => signWebBotAuth,
|
|
43
|
+
verifyKeyDirectory: () => verifyKeyDirectory,
|
|
39
44
|
verifySignatureDirectory: () => verifySignatureDirectory,
|
|
40
45
|
verifyWebBotAuth: () => verifyWebBotAuth,
|
|
41
|
-
|
|
46
|
+
webBotAuthPrincipalFailureError: () => webBotAuthPrincipalFailureError,
|
|
47
|
+
webBotAuthToolFailure: () => webBotAuthToolFailure,
|
|
48
|
+
webBotAuthUnavailableResponse: () => webBotAuthUnavailableResponse
|
|
42
49
|
});
|
|
43
50
|
module.exports = __toCommonJS(web_bot_auth_exports);
|
|
44
51
|
var import_version = require('../version.js');
|
|
@@ -58,12 +65,14 @@ const WEB_BOT_AUTH_DRAFT_REVISION = "draft-ietf-webbotauth-httpsig-protocol-00";
|
|
|
58
65
|
const SIGNATURE_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
|
|
59
66
|
const SIGNATURE_DIRECTORY_MEDIA_TYPE = "application/http-message-signatures-directory+json";
|
|
60
67
|
const DIRECTORY_TAG = "http-message-signatures-directory";
|
|
68
|
+
const DIRECTORY_CLOCK_SKEW_SECONDS = 60;
|
|
61
69
|
const TEST_KIDS = /* @__PURE__ */ new Set([
|
|
62
70
|
"6WsKMCObba49V3uN0vIENnk1BKAk7c9zjBJcWKyQ9_I",
|
|
63
71
|
"hHurKlVBnPJ4AGiPyl6HWMhxgkIcRBzaC8qWwEtnuVI",
|
|
64
72
|
"ZtaFf5jj-EzorJmZn6mX3YnmK_QyZKVLbbinzIQKOjQ",
|
|
65
73
|
"1XlJ3EIte7bPOEqKQtKrKNTNy5hywTyrwFXWrMbXKqE"
|
|
66
74
|
]);
|
|
75
|
+
const NO_CHALLENGE_REASONS = /* @__PURE__ */ new Set(["key_revoked", "identity_not_bound", "profile_disabled", "authorization_failed"]);
|
|
67
76
|
class WebBotAuthError extends Error {
|
|
68
77
|
constructor(status, reason) {
|
|
69
78
|
super(
|
|
@@ -72,13 +81,7 @@ class WebBotAuthError extends Error {
|
|
|
72
81
|
this.status = status;
|
|
73
82
|
this.reason = reason;
|
|
74
83
|
this.name = "WebBotAuthError";
|
|
75
|
-
this.headers = status === 403 && !
|
|
76
|
-
"key_revoked",
|
|
77
|
-
"identity_not_bound",
|
|
78
|
-
"revocation_unavailable",
|
|
79
|
-
"revocation_persistence_failed",
|
|
80
|
-
"profile_disabled"
|
|
81
|
-
].includes(reason) ? {
|
|
84
|
+
this.headers = status === 403 && !NO_CHALLENGE_REASONS.has(reason) ? {
|
|
82
85
|
"Accept-Signature": 'sig1=("@method" "@target-uri" "@authority" "content-type" "content-digest" "signature-agent";key="sig1");created;expires;nonce;keyid;tag="web-bot-auth"'
|
|
83
86
|
} : status === 429 ? { "Retry-After": "60" } : {};
|
|
84
87
|
}
|
|
@@ -86,18 +89,29 @@ class WebBotAuthError extends Error {
|
|
|
86
89
|
reason;
|
|
87
90
|
headers;
|
|
88
91
|
get body() {
|
|
89
|
-
if (
|
|
90
|
-
this.reason
|
|
91
|
-
))
|
|
92
|
+
if (this.reason === "authorization_failed" || this.reason === "identity_not_bound")
|
|
92
93
|
return {
|
|
93
|
-
adcp_error: {
|
|
94
|
-
code: this.reason.startsWith("revocation_") ? "SERVICE_UNAVAILABLE" : "PERMISSION_DENIED",
|
|
95
|
-
message: this.message,
|
|
96
|
-
recovery: this.reason.startsWith("revocation_") ? "transient" : "correctable"
|
|
97
|
-
}
|
|
94
|
+
adcp_error: { code: "PERMISSION_DENIED", message: "Agent authorization failed", recovery: "correctable" }
|
|
98
95
|
};
|
|
99
|
-
return {
|
|
96
|
+
return { adcp_error: { code: "AUTH_INVALID", message: this.message, recovery: "terminal" } };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The signer's identity could not be established because key discovery
|
|
100
|
+
* failed (WBA §6.10) or its revocation status is unknown. The request is
|
|
101
|
+
* unverified; servers apply their unverified-request policy.
|
|
102
|
+
*/
|
|
103
|
+
get unverified() {
|
|
104
|
+
return this.reason.startsWith("directory_") || this.reason === "identity_unverified";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
class WebBotAuthUnavailableError extends Error {
|
|
108
|
+
constructor(reason, cause) {
|
|
109
|
+
super("Web Bot Auth verification dependency is unavailable");
|
|
110
|
+
this.reason = reason;
|
|
111
|
+
this.name = "WebBotAuthUnavailableError";
|
|
112
|
+
if (cause !== void 0) Object.defineProperty(this, "cause", { value: cause });
|
|
100
113
|
}
|
|
114
|
+
reason;
|
|
101
115
|
}
|
|
102
116
|
function bad(reason) {
|
|
103
117
|
throw new WebBotAuthError(400, reason);
|
|
@@ -106,7 +120,8 @@ function denied(reason) {
|
|
|
106
120
|
throw new WebBotAuthError(403, reason);
|
|
107
121
|
}
|
|
108
122
|
function dictionary(value, field) {
|
|
109
|
-
if (
|
|
123
|
+
if (value === void 0 || value === "") return denied("headers_missing");
|
|
124
|
+
if (value.length > 64 * 1024) return bad("headers");
|
|
110
125
|
try {
|
|
111
126
|
(0, import_parser.rejectDuplicateDictionaryKeys)(value, field);
|
|
112
127
|
return (0, import_structured_headers.parseDictionary)(value);
|
|
@@ -126,12 +141,18 @@ function jwkThumbprint(jwk) {
|
|
|
126
141
|
fields = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
|
|
127
142
|
else if (jwk.kty === "EC" && typeof jwk.crv === "string" && typeof jwk.x === "string" && typeof jwk.y === "string")
|
|
128
143
|
fields = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
|
|
144
|
+
else if (jwk.kty === "RSA" && typeof jwk.e === "string" && typeof jwk.n === "string")
|
|
145
|
+
fields = { e: jwk.e, kty: jwk.kty, n: jwk.n };
|
|
129
146
|
else return denied("unsupported_key");
|
|
130
147
|
return (0, import_crypto.createHash)("sha256").update(JSON.stringify(fields)).digest("base64url");
|
|
131
148
|
}
|
|
132
149
|
function algorithm(jwk) {
|
|
133
150
|
if (jwk.kty === "OKP" && jwk.crv === "Ed25519") return "ed25519";
|
|
134
151
|
if (jwk.kty === "EC" && jwk.crv === "P-256") return "ecdsa-p256-sha256";
|
|
152
|
+
if (jwk.kty === "RSA" && typeof jwk.n === "string" && jwk.e === "AQAB") {
|
|
153
|
+
const bytes = Buffer.from(jwk.n, "base64url").length;
|
|
154
|
+
if (bytes >= 256 && bytes <= 512) return "rsa-pss-sha512";
|
|
155
|
+
}
|
|
135
156
|
return denied("unsupported_key");
|
|
136
157
|
}
|
|
137
158
|
function inputs(headers) {
|
|
@@ -175,8 +196,8 @@ function buildWebBotAuthSignatureBase(input, request, response) {
|
|
|
175
196
|
if (params.has("key")) return bad("derived_dictionary");
|
|
176
197
|
if (response && bare !== "@status" && !params.has("req")) return bad("response_component");
|
|
177
198
|
if (bare === "@method") value = request.method.toUpperCase();
|
|
178
|
-
else if (bare === "@target-uri") value = (
|
|
179
|
-
else if (bare === "@authority") value = (
|
|
199
|
+
else if (bare === "@target-uri") value = receivedTargetUri(request.url);
|
|
200
|
+
else if (bare === "@authority") value = receivedAuthority(request.url);
|
|
180
201
|
else if (bare === "@status" && response && !params.has("req")) value = String(response.status);
|
|
181
202
|
else return bad("derived_component");
|
|
182
203
|
} else {
|
|
@@ -196,6 +217,21 @@ function buildWebBotAuthSignatureBase(input, request, response) {
|
|
|
196
217
|
});
|
|
197
218
|
return [...lines, `"@signature-params": ${input.signatureParamsValue}`].join("\n");
|
|
198
219
|
}
|
|
220
|
+
function receivedTargetUri(url) {
|
|
221
|
+
try {
|
|
222
|
+
new URL(url);
|
|
223
|
+
} catch {
|
|
224
|
+
return bad("target_uri");
|
|
225
|
+
}
|
|
226
|
+
return url;
|
|
227
|
+
}
|
|
228
|
+
function receivedAuthority(url) {
|
|
229
|
+
try {
|
|
230
|
+
return new URL(url).host.toLowerCase();
|
|
231
|
+
} catch {
|
|
232
|
+
return bad("target_uri");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
199
235
|
function assertDigest(headers, body) {
|
|
200
236
|
const digest = (0, import_canonicalize2.getHeaderValue)(headers, "content-digest");
|
|
201
237
|
if (!digest || !(0, import_content_digest.contentDigestUsesEncoding)(digest, "rfc8941-base64") || !(0, import_content_digest.contentDigestMatches)(digest, body))
|
|
@@ -205,6 +241,7 @@ function verifyBytes(input, request, jwk, response) {
|
|
|
205
241
|
const alg = algorithm(jwk);
|
|
206
242
|
if (input.params.alg !== void 0 && input.params.alg !== alg) denied("algorithm");
|
|
207
243
|
const signature = (0, import_canonicalize2.getHeaderValue)(response?.headers ?? request.headers, "signature");
|
|
244
|
+
if (signature === void 0 || signature === "") denied("headers_missing");
|
|
208
245
|
let bytes;
|
|
209
246
|
try {
|
|
210
247
|
bytes = (0, import_parser.parseSignature)(signature ?? "", input.label, "rfc8941-base64").bytes;
|
|
@@ -231,16 +268,63 @@ function registerWebBotAuthLegacyOriginGate(resolver, gate) {
|
|
|
231
268
|
function getWebBotAuthLegacyOriginGate(resolver) {
|
|
232
269
|
return resolver && legacyOriginGates.get(resolver);
|
|
233
270
|
}
|
|
271
|
+
const revocationCheckingResolvers = /* @__PURE__ */ new WeakSet();
|
|
272
|
+
function registerWebBotAuthRevocationCheck(resolver) {
|
|
273
|
+
revocationCheckingResolvers.add(resolver);
|
|
274
|
+
}
|
|
275
|
+
function isWebBotAuthRevocationChecked(resolver) {
|
|
276
|
+
return !!resolver && revocationCheckingResolvers.has(resolver);
|
|
277
|
+
}
|
|
278
|
+
function webBotAuthUnavailableResponse() {
|
|
279
|
+
return {
|
|
280
|
+
status: 503,
|
|
281
|
+
headers: { "Retry-After": "60" },
|
|
282
|
+
body: {
|
|
283
|
+
adcp_error: {
|
|
284
|
+
code: "SERVICE_UNAVAILABLE",
|
|
285
|
+
message: "Agent identity verification is temporarily unavailable",
|
|
286
|
+
recovery: "transient"
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
234
291
|
const authorizationFailures = /* @__PURE__ */ new WeakSet();
|
|
292
|
+
const AUTHORIZATION_FAILURE_MESSAGES = {
|
|
293
|
+
key_revoked: "Agent identity key is revoked",
|
|
294
|
+
identity_not_bound: "Agent identity is not onboarded for this operation",
|
|
295
|
+
revocation_unavailable: "Identity revocation information is unavailable",
|
|
296
|
+
authorization_failed: "Agent authorization failed",
|
|
297
|
+
authorization_unavailable: "Agent authorization is temporarily unavailable"
|
|
298
|
+
};
|
|
235
299
|
function createWebBotAuthAuthorizationFailure(reason) {
|
|
300
|
+
const transient = reason === "revocation_unavailable" || reason === "authorization_unavailable";
|
|
236
301
|
const failure = Object.freeze({
|
|
237
|
-
code:
|
|
238
|
-
message: reason
|
|
239
|
-
recovery:
|
|
302
|
+
code: transient ? "SERVICE_UNAVAILABLE" : "PERMISSION_DENIED",
|
|
303
|
+
message: AUTHORIZATION_FAILURE_MESSAGES[reason],
|
|
304
|
+
recovery: transient ? "transient" : "correctable"
|
|
240
305
|
});
|
|
241
306
|
authorizationFailures.add(failure);
|
|
242
307
|
return failure;
|
|
243
308
|
}
|
|
309
|
+
function principalFailureIsTransient(error) {
|
|
310
|
+
const value = error;
|
|
311
|
+
if (value?.transient === true || value?.recovery === "transient" || value?.code === "SERVICE_UNAVAILABLE")
|
|
312
|
+
return true;
|
|
313
|
+
return typeof value?.code !== "string";
|
|
314
|
+
}
|
|
315
|
+
function webBotAuthPrincipalFailureError(error) {
|
|
316
|
+
return principalFailureIsTransient(error) ? new WebBotAuthUnavailableError("authorization_unavailable", error) : new WebBotAuthError(403, "authorization_failed");
|
|
317
|
+
}
|
|
318
|
+
function deferWebBotAuthPrincipalFailure(signers, error) {
|
|
319
|
+
const signer = signers[signers.length - 1];
|
|
320
|
+
const authorizationFailure = createWebBotAuthAuthorizationFailure(
|
|
321
|
+
principalFailureIsTransient(error) ? "authorization_unavailable" : "authorization_failed"
|
|
322
|
+
);
|
|
323
|
+
return {
|
|
324
|
+
principal: `anonymous:${(0, import_crypto.createHash)("sha256").update(signer.origin).digest("hex")}`,
|
|
325
|
+
authorizationFailure
|
|
326
|
+
};
|
|
327
|
+
}
|
|
244
328
|
function getWebBotAuthAuthorizationFailure(extra) {
|
|
245
329
|
for (const value of [extra?.webBotAuthAuthorization, extra?.identity33]) {
|
|
246
330
|
const claim = value;
|
|
@@ -306,7 +390,8 @@ async function verifyWebBotAuth(request, options) {
|
|
|
306
390
|
const memberKey = component[1].get("key");
|
|
307
391
|
if (typeof memberKey !== "string" || memberKey !== input.label) bad("agent_member");
|
|
308
392
|
const member = agents.get(memberKey);
|
|
309
|
-
if (!member
|
|
393
|
+
if (!member) denied("agent_not_covered");
|
|
394
|
+
if (typeof member[0] !== "string") bad("signature_agent");
|
|
310
395
|
const type = member[1].get("type");
|
|
311
396
|
if (type !== void 0 && type !== "directory") denied("agent_type");
|
|
312
397
|
const origin = receivedAgentOrigin(member[0]);
|
|
@@ -326,27 +411,16 @@ async function verifyWebBotAuth(request, options) {
|
|
|
326
411
|
assertDigest(request.headers, request.body);
|
|
327
412
|
}
|
|
328
413
|
await options.directory.resolve(keyid, origin);
|
|
329
|
-
const
|
|
330
|
-
if (!
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
);
|
|
414
|
+
const checkedDirectory = options.directory.getVerifiedDirectory(origin);
|
|
415
|
+
if (!checkedDirectory || new URL(checkedDirectory.proof.request.url).origin !== origin)
|
|
416
|
+
denied("directory_origin");
|
|
417
|
+
if (checkedDirectory.expires <= now) denied("directory_expired");
|
|
418
|
+
if (!testKeysAllowed(options.allowTestKeys ?? options.directory.allowsTestKeys) && checkedDirectory.keys.some((key2) => TEST_KIDS.has(key2.kid)))
|
|
419
|
+
denied("directory_key");
|
|
336
420
|
const key = checkedDirectory.keys.find((key2) => key2.kid === keyid);
|
|
337
421
|
if (!key || jwkThumbprint(key) !== keyid) denied("key");
|
|
338
422
|
verifyBytes(input, request, key);
|
|
339
|
-
|
|
340
|
-
try {
|
|
341
|
-
if (await options.isRevoked(origin, keyid)) denied("key_revoked");
|
|
342
|
-
} catch (error) {
|
|
343
|
-
if (options.requireFreshRevocation === false && error instanceof WebBotAuthError && error.reason === "revocation_unavailable") {
|
|
344
|
-
} else if (options.deferAuthorizationErrors && error instanceof WebBotAuthError && ["key_revoked", "revocation_unavailable", "revocation_persistence_failed"].includes(error.reason))
|
|
345
|
-
authorizationFailure = createWebBotAuthAuthorizationFailure(
|
|
346
|
-
error.reason === "revocation_persistence_failed" ? "revocation_unavailable" : error.reason
|
|
347
|
-
);
|
|
348
|
-
else throw error;
|
|
349
|
-
}
|
|
423
|
+
if (options.isRevoked) await legacyRevocationCheck(options, origin, keyid);
|
|
350
424
|
const nonceTuple = JSON.stringify([origin, nonce]);
|
|
351
425
|
if (nonces.has(nonceTuple)) throw new WebBotAuthError(options.replayStatus ?? 429, "nonce_replayed");
|
|
352
426
|
nonces.add(nonceTuple);
|
|
@@ -355,20 +429,25 @@ async function verifyWebBotAuth(request, options) {
|
|
|
355
429
|
origin,
|
|
356
430
|
keyid,
|
|
357
431
|
label: input.label,
|
|
358
|
-
verified_at: now
|
|
359
|
-
...authorizationFailure && { authorizationFailure }
|
|
432
|
+
verified_at: now
|
|
360
433
|
});
|
|
361
434
|
verified.push(signer);
|
|
362
435
|
}
|
|
363
436
|
for (const signer of verified) {
|
|
364
437
|
const input = selected.find((i) => i.label === signer.label);
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
438
|
+
let result;
|
|
439
|
+
try {
|
|
440
|
+
result = await options.replayStore.insert(
|
|
441
|
+
signer.origin,
|
|
442
|
+
WEB_BOT_AUTH_PROFILE,
|
|
443
|
+
input.params.nonce,
|
|
444
|
+
// Acceptance is inclusive of expires + 60, so the nonce must outlive it.
|
|
445
|
+
input.params.expires + 60 - now + 1,
|
|
446
|
+
now
|
|
447
|
+
);
|
|
448
|
+
} catch (cause) {
|
|
449
|
+
throw new WebBotAuthUnavailableError("replay_unavailable", cause);
|
|
450
|
+
}
|
|
372
451
|
if (result !== "ok")
|
|
373
452
|
throw new WebBotAuthError(result === "replayed" ? options.replayStatus ?? 429 : 429, result);
|
|
374
453
|
}
|
|
@@ -377,48 +456,109 @@ async function verifyWebBotAuth(request, options) {
|
|
|
377
456
|
} catch (error) {
|
|
378
457
|
if (error instanceof WebBotAuthError) {
|
|
379
458
|
if (error.headers["Accept-Signature"]) {
|
|
380
|
-
|
|
459
|
+
let label = "sig1";
|
|
460
|
+
try {
|
|
461
|
+
label = inputs(request.headers)[0]?.label ?? "sig1";
|
|
462
|
+
} catch {
|
|
463
|
+
}
|
|
381
464
|
const bodyComponents = request.body ? '"content-type" "content-digest" ' : "";
|
|
382
465
|
error.headers["Accept-Signature"] = `${label}=("@method" "@target-uri" "@authority" ${bodyComponents}"signature-agent";key="${label}");created;expires;nonce;keyid;tag="web-bot-auth"`;
|
|
383
466
|
}
|
|
384
467
|
throw error;
|
|
385
468
|
}
|
|
469
|
+
if (error instanceof WebBotAuthUnavailableError) throw error;
|
|
386
470
|
return denied("verification_unavailable");
|
|
387
471
|
}
|
|
388
472
|
}
|
|
389
|
-
function
|
|
473
|
+
async function legacyRevocationCheck(options, origin, keyid) {
|
|
474
|
+
let revoked;
|
|
475
|
+
try {
|
|
476
|
+
revoked = await options.isRevoked(origin, keyid);
|
|
477
|
+
} catch (error) {
|
|
478
|
+
if (error instanceof WebBotAuthUnavailableError) throw error;
|
|
479
|
+
if (error instanceof WebBotAuthError && error.reason === "revocation_persistence_failed")
|
|
480
|
+
throw new WebBotAuthUnavailableError("revocation_persistence_failed", error);
|
|
481
|
+
if (error instanceof WebBotAuthError && error.reason === "key_revoked") return denied("key_revoked");
|
|
482
|
+
if (options.requireFreshRevocation === false && error instanceof WebBotAuthError && (error.reason === "revocation_unavailable" || error.reason === "revocation_absent"))
|
|
483
|
+
return;
|
|
484
|
+
return denied("identity_unverified");
|
|
485
|
+
}
|
|
486
|
+
if (revoked) denied("key_revoked");
|
|
487
|
+
}
|
|
488
|
+
function testKeysAllowed(allowTestKeys) {
|
|
489
|
+
return allowTestKeys === true && ["test", "development"].includes(process.env.NODE_ENV ?? "");
|
|
490
|
+
}
|
|
491
|
+
const UNSIGNED_DIRECTORY_MEDIA_TYPES = /* @__PURE__ */ new Set([
|
|
492
|
+
SIGNATURE_DIRECTORY_MEDIA_TYPE,
|
|
493
|
+
"application/jwk-set+json",
|
|
494
|
+
"application/json"
|
|
495
|
+
]);
|
|
496
|
+
function verifyKeyDirectory(response, now = Date.now() / 1e3, options = {}) {
|
|
390
497
|
try {
|
|
391
498
|
if (!Number.isFinite(now)) denied("directory_invalid");
|
|
392
499
|
const origin = (0, import_canonicalize.canonicalAgentOrigin)(response.request.url);
|
|
393
|
-
if (response.request.url !== origin + SIGNATURE_DIRECTORY_PATH || response.status !== 200
|
|
500
|
+
if (response.request.url !== origin + SIGNATURE_DIRECTORY_PATH || response.status !== 200)
|
|
501
|
+
denied("directory_response");
|
|
502
|
+
const signed = (0, import_canonicalize2.getHeaderValue)(response.headers, "signature-input") !== void 0;
|
|
503
|
+
if (!signed && options.requireSigned) denied("directory_unsigned");
|
|
504
|
+
const mediaType = (0, import_canonicalize2.getHeaderValue)(response.headers, "content-type")?.split(";")[0]?.trim().toLowerCase();
|
|
505
|
+
if (signed ? mediaType !== SIGNATURE_DIRECTORY_MEDIA_TYPE : !UNSIGNED_DIRECTORY_MEDIA_TYPES.has(mediaType ?? ""))
|
|
394
506
|
denied("directory_response");
|
|
395
507
|
const body = response.body ?? "";
|
|
396
508
|
if (Buffer.byteLength(body) > 256 * 1024) denied("directory_size");
|
|
397
|
-
|
|
509
|
+
if (signed || (0, import_canonicalize2.getHeaderValue)(response.headers, "content-digest") !== void 0)
|
|
510
|
+
assertDirectoryDigest(response.headers, body);
|
|
398
511
|
const document = (0, import_strict_json.parseStrictJson)(body, 32);
|
|
399
512
|
if (!Array.isArray(document.keys) || !document.keys.length || document.keys.length > 64) denied("directory_keys");
|
|
400
|
-
const signatures = inputs(response.headers);
|
|
401
|
-
const
|
|
513
|
+
const signatures = signed ? inputs(response.headers) : [];
|
|
514
|
+
const kids = /* @__PURE__ */ new Set();
|
|
515
|
+
const keys = [];
|
|
402
516
|
let expires = Infinity;
|
|
403
|
-
for (const
|
|
404
|
-
if (!
|
|
517
|
+
for (const raw of document.keys) {
|
|
518
|
+
if (!raw || typeof raw !== "object") denied("directory_key");
|
|
519
|
+
const thumbprint = jwkThumbprint(raw);
|
|
520
|
+
const key = { ...raw, kid: raw.kid ?? thumbprint };
|
|
521
|
+
if (key.kid !== thumbprint || kids.has(key.kid) || key.d !== void 0 || Object.keys(key).some((k) => k.startsWith("_private")) || key.adcp_use !== void 0 || !testKeysAllowed(options.allowTestKeys) && TEST_KIDS.has(key.kid))
|
|
405
522
|
denied("directory_key");
|
|
406
|
-
|
|
523
|
+
algorithm(key);
|
|
524
|
+
kids.add(key.kid);
|
|
525
|
+
keys.push(key);
|
|
526
|
+
if (!signed) continue;
|
|
407
527
|
const proof = signatures.find((input) => input.params.keyid === key.kid && input.params.tag === DIRECTORY_TAG);
|
|
408
|
-
if (!proof || !proof.components.includes("@authority;req") || !proof.components.includes("content-digest") || !Number.isInteger(proof.params.created) || !Number.isInteger(proof.params.expires) || proof.params.created > now || proof.params.expires <= now || proof.params.expires <= proof.params.created)
|
|
528
|
+
if (!proof || !proof.components.includes("@authority;req") || !proof.components.includes("content-digest") || !Number.isInteger(proof.params.created) || !Number.isInteger(proof.params.expires) || proof.params.created > now + DIRECTORY_CLOCK_SKEW_SECONDS || proof.params.expires <= now || proof.params.expires <= proof.params.created)
|
|
409
529
|
denied("directory_proof");
|
|
410
530
|
verifyBytes(proof, { ...response.request, headers: response.request.headers ?? {} }, key, response);
|
|
411
531
|
expires = Math.min(expires, proof.params.expires);
|
|
412
532
|
}
|
|
413
|
-
|
|
533
|
+
if (!signed) expires = now + unsignedAge(options.maxUnsignedAgeSeconds);
|
|
534
|
+
return { keys, expires, signed, proof: response };
|
|
414
535
|
} catch (error) {
|
|
415
|
-
if (error instanceof WebBotAuthError)
|
|
536
|
+
if (error instanceof WebBotAuthError) {
|
|
537
|
+
throw error.reason.startsWith("directory_") ? error : new WebBotAuthError(403, `directory_${error.reason}`);
|
|
538
|
+
}
|
|
416
539
|
return denied("directory_invalid");
|
|
417
540
|
}
|
|
418
541
|
}
|
|
542
|
+
function unsignedAge(seconds) {
|
|
543
|
+
const value = seconds ?? 86400;
|
|
544
|
+
if (!Number.isFinite(value) || value < 60)
|
|
545
|
+
throw new TypeError("Unsigned directory age must be a finite number of seconds, at least 60");
|
|
546
|
+
return value;
|
|
547
|
+
}
|
|
548
|
+
function verifySignatureDirectory(response, now = Date.now() / 1e3, allowTestKeys = false) {
|
|
549
|
+
return verifyKeyDirectory(response, now, { allowTestKeys, requireSigned: true });
|
|
550
|
+
}
|
|
551
|
+
function assertDirectoryDigest(headers, body) {
|
|
552
|
+
try {
|
|
553
|
+
assertDigest(headers, body);
|
|
554
|
+
} catch {
|
|
555
|
+
denied("directory_digest");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
419
558
|
class SignatureDirectoryResolver {
|
|
420
559
|
constructor(options = {}) {
|
|
421
560
|
this.options = options;
|
|
561
|
+
unsignedAge(options.maxUnsignedDirectoryAgeSeconds);
|
|
422
562
|
}
|
|
423
563
|
options;
|
|
424
564
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -430,19 +570,22 @@ class SignatureDirectoryResolver {
|
|
|
430
570
|
const origin = receivedAgentOrigin(signerOrigin);
|
|
431
571
|
const now = (this.options.now ?? (() => Date.now() / 1e3))();
|
|
432
572
|
const cached = this.cache.get(origin);
|
|
573
|
+
const usable = () => cached && cached.directory.expires > now ? cached.directory : void 0;
|
|
433
574
|
if (cached && cached.freshUntil > now && cached.directory.expires > now) {
|
|
434
575
|
const key = cached.directory.keys.find((k) => k.kid === keyid);
|
|
435
576
|
if (key || (this.keyMissCooldown.get(origin) ?? 0) > now) return key ?? null;
|
|
436
577
|
this.keyMissCooldown.set(origin, now + 60);
|
|
437
578
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return denied("directory_unavailable");
|
|
441
|
-
}
|
|
579
|
+
const fromFallback = () => {
|
|
580
|
+
const key = usable()?.keys.find((k) => k.kid === keyid);
|
|
581
|
+
return key ?? denied("directory_unavailable");
|
|
582
|
+
};
|
|
583
|
+
if ((this.failures.get(origin) ?? 0) > now) return fromFallback();
|
|
442
584
|
let pending = this.pending.get(origin);
|
|
443
585
|
if (!pending) {
|
|
444
586
|
if ([...this.pending.keys()].filter((pendingOrigin) => (0, import_etld.eTldPlusOne)(pendingOrigin) === (0, import_etld.eTldPlusOne)(origin)).length >= 2 || this.pending.size >= (this.options.maxConcurrentFetches ?? 16)) {
|
|
445
|
-
|
|
587
|
+
const key = usable()?.keys.find((k) => k.kid === keyid);
|
|
588
|
+
if (key) return key;
|
|
446
589
|
throw new WebBotAuthError(429, "directory_fetch_limit");
|
|
447
590
|
}
|
|
448
591
|
pending = this.fetch(origin, now);
|
|
@@ -451,7 +594,7 @@ class SignatureDirectoryResolver {
|
|
|
451
594
|
try {
|
|
452
595
|
return (await pending).keys.find((k) => k.kid === keyid) ?? null;
|
|
453
596
|
} catch (error) {
|
|
454
|
-
if (
|
|
597
|
+
if (usable()) return fromFallback();
|
|
455
598
|
throw error;
|
|
456
599
|
} finally {
|
|
457
600
|
if (this.pending.get(origin) === pending) this.pending.delete(origin);
|
|
@@ -462,15 +605,22 @@ class SignatureDirectoryResolver {
|
|
|
462
605
|
const url = origin + SIGNATURE_DIRECTORY_PATH;
|
|
463
606
|
const result = await (0, import_net.ssrfSafeFetch)(url, {
|
|
464
607
|
...this.options.fetchOptions,
|
|
465
|
-
headers: {
|
|
608
|
+
headers: {
|
|
609
|
+
accept: `${SIGNATURE_DIRECTORY_MEDIA_TYPE}, application/jwk-set+json;q=0.9, application/json;q=0.8`
|
|
610
|
+
},
|
|
466
611
|
timeoutMs: 1e4,
|
|
467
612
|
maxBodyBytes: 256 * 1024
|
|
468
613
|
});
|
|
469
|
-
|
|
470
|
-
const
|
|
614
|
+
if (result.status !== 200) denied("directory_unavailable");
|
|
615
|
+
const body = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(result.body);
|
|
616
|
+
const directory = verifyKeyDirectory(
|
|
471
617
|
{ status: result.status, headers: result.headers, body, request: { method: "GET", url } },
|
|
472
618
|
now,
|
|
473
|
-
|
|
619
|
+
{
|
|
620
|
+
allowTestKeys: this.options.allowTestKeys,
|
|
621
|
+
requireSigned: this.options.requireSignedDirectories,
|
|
622
|
+
maxUnsignedAgeSeconds: this.options.maxUnsignedDirectoryAgeSeconds
|
|
623
|
+
}
|
|
474
624
|
);
|
|
475
625
|
const cacheControl = result.headers["cache-control"] ?? "";
|
|
476
626
|
const maxAge = /(?:^|,)\s*(?:no-cache|no-store)(?:,|$)/i.test(cacheControl) ? 0 : Number(cacheControl.match(/(?:^|,)\s*max-age=(\d+)/i)?.[1] ?? 300);
|
|
@@ -495,10 +645,14 @@ class SignatureDirectoryResolver {
|
|
|
495
645
|
}
|
|
496
646
|
}
|
|
497
647
|
get allowsTestKeys() {
|
|
498
|
-
return this.options.allowTestKeys
|
|
648
|
+
return testKeysAllowed(this.options.allowTestKeys);
|
|
649
|
+
}
|
|
650
|
+
/** The directory currently used to verify `origin`, including one kept through a failed refresh. */
|
|
651
|
+
getVerifiedDirectory(origin) {
|
|
652
|
+
return this.cache.get(receivedAgentOrigin(origin))?.directory;
|
|
499
653
|
}
|
|
500
654
|
getAuditProof(origin) {
|
|
501
|
-
return this.
|
|
655
|
+
return this.getVerifiedDirectory(origin)?.proof;
|
|
502
656
|
}
|
|
503
657
|
}
|
|
504
658
|
async function signBase(base, key) {
|
|
@@ -512,6 +666,12 @@ async function signBase(base, key) {
|
|
|
512
666
|
return key.provider.sign(Buffer.from(base));
|
|
513
667
|
}
|
|
514
668
|
const privateKey = (0, import_crypto.createPrivateKey)({ key: jwk, format: "jwk" });
|
|
669
|
+
if (alg === "rsa-pss-sha512")
|
|
670
|
+
return (0, import_crypto.sign)("sha512", Buffer.from(base), {
|
|
671
|
+
key: privateKey,
|
|
672
|
+
padding: import_crypto.constants.RSA_PKCS1_PSS_PADDING,
|
|
673
|
+
saltLength: 64
|
|
674
|
+
});
|
|
515
675
|
return alg === "ed25519" ? (0, import_crypto.sign)(null, Buffer.from(base), privateKey) : (0, import_crypto.sign)("sha256", Buffer.from(base), { key: privateKey, dsaEncoding: "ieee-p1363" });
|
|
516
676
|
}
|
|
517
677
|
async function signWebBotAuth(request, key, options) {
|
|
@@ -521,9 +681,15 @@ async function signWebBotAuth(request, key, options) {
|
|
|
521
681
|
const jwk = "privateKey" in key ? key.privateKey : key.publicKey;
|
|
522
682
|
const now = Math.floor((options.now ?? (() => Date.now() / 1e3))());
|
|
523
683
|
const window = options.windowSeconds ?? 300;
|
|
524
|
-
const nonce = options.nonce ?? (0, import_crypto.randomBytes)(
|
|
684
|
+
const nonce = options.nonce ?? (0, import_crypto.randomBytes)(64).toString("base64url");
|
|
525
685
|
if (!Number.isInteger(window) || window < 1 || window > 300 || !/^[A-Za-z0-9_+\/-]{22,256}={0,2}$/.test(nonce))
|
|
526
686
|
throw new TypeError("Invalid Web Bot Auth lifetime or nonce");
|
|
687
|
+
if (!options.relay && ["signature", "signature-input", "signature-agent"].some(
|
|
688
|
+
(name) => (0, import_canonicalize2.getHeaderValue)(request.headers, name) !== void 0
|
|
689
|
+
))
|
|
690
|
+
throw new TypeError(
|
|
691
|
+
"Request already carries signature fields; pass relay: true to add a covering signature instead of replacing them"
|
|
692
|
+
);
|
|
527
693
|
const headers = {};
|
|
528
694
|
for (const [name, value] of Object.entries(request.headers))
|
|
529
695
|
if (value !== void 0 && !["signature", "signature-input", "signature-agent", "content-digest"].includes(name.toLowerCase()))
|
|
@@ -535,6 +701,8 @@ async function signWebBotAuth(request, key, options) {
|
|
|
535
701
|
headers["content-digest"] = (0, import_content_digest.computeContentDigest)(request.body, "rfc8941-base64");
|
|
536
702
|
components.push('"content-type"', '"content-digest"');
|
|
537
703
|
}
|
|
704
|
+
if (options.relay && ["signature", "signature-input", "signature-agent"].some((name) => !(0, import_canonicalize2.getHeaderValue)(request.headers, name)))
|
|
705
|
+
throw new TypeError("Relay requires the existing signature fields");
|
|
538
706
|
const earlier = options.relay ? inputs(request.headers) : [];
|
|
539
707
|
if (earlier.some((input2) => input2.label === label || input2.params.tag !== WEB_BOT_AUTH_PROFILE))
|
|
540
708
|
throw new TypeError("Relay requires existing Web Bot Auth signatures and a fresh label");
|
|
@@ -557,7 +725,10 @@ async function signWebBotAuth(request, key, options) {
|
|
|
557
725
|
headers["signature-input"] = `${priorInput ? priorInput + ", " : ""}${label}=(${components.join(" ")});created=${now};expires=${now + window};nonce="${nonce}";keyid="${jwkThumbprint(jwk)}";alg="${algorithm(jwk)}";tag="web-bot-auth"`;
|
|
558
726
|
const input = (0, import_parser.parseSignatureInput)(headers["signature-input"], label);
|
|
559
727
|
if (priorSignature) headers.signature = priorSignature;
|
|
560
|
-
const bytes = await signBase(
|
|
728
|
+
const bytes = await signBase(
|
|
729
|
+
buildWebBotAuthSignatureBase(input, { ...request, url: new URL(request.url).href, headers }),
|
|
730
|
+
key
|
|
731
|
+
);
|
|
561
732
|
headers.signature = `${priorSignature ? priorSignature + ", " : ""}${label}=:${Buffer.from(bytes).toString("base64")}:`;
|
|
562
733
|
return headers;
|
|
563
734
|
}
|
|
@@ -573,8 +744,7 @@ async function createSignedSignatureDirectory(originUrl, signingKeys, options =
|
|
|
573
744
|
const jwk = "privateKey" in key ? key.privateKey : key.publicKey;
|
|
574
745
|
return {
|
|
575
746
|
kty: jwk.kty,
|
|
576
|
-
crv: jwk.crv,
|
|
577
|
-
x: jwk.x,
|
|
747
|
+
...jwk.kty === "RSA" ? { n: jwk.n, e: jwk.e } : { crv: jwk.crv, x: jwk.x },
|
|
578
748
|
...jwk.y ? { y: jwk.y } : {},
|
|
579
749
|
kid: jwkThumbprint(jwk),
|
|
580
750
|
use: "sig"
|
|
@@ -595,7 +765,7 @@ async function createSignedSignatureDirectory(originUrl, signingKeys, options =
|
|
|
595
765
|
const signatureInputs = [], signatures = [];
|
|
596
766
|
for (let i = 0; i < keys.length; i++) {
|
|
597
767
|
const label = `key${i + 1}`;
|
|
598
|
-
const value = `${label}=("@authority";req "content-digest");created=${now};expires=${now + lifetime};keyid="${keys[i].kid}";alg="${algorithm(keys[i])}";tag="${DIRECTORY_TAG}"`;
|
|
768
|
+
const value = `${label}=("@authority";req "content-digest");created=${now - DIRECTORY_CLOCK_SKEW_SECONDS};expires=${now + lifetime};keyid="${keys[i].kid}";alg="${algorithm(keys[i])}";tag="${DIRECTORY_TAG}"`;
|
|
599
769
|
const input = (0, import_parser.parseSignatureInput)(value, label);
|
|
600
770
|
const bytes = await signBase(
|
|
601
771
|
buildWebBotAuthSignatureBase(input, { method: "GET", url: response.request.url, headers: {} }, response),
|
|
@@ -621,7 +791,7 @@ function createWebBotAuthFetch(upstream, key, options) {
|
|
|
621
791
|
if (init?.body != null) {
|
|
622
792
|
if (typeof init.body === "string") body = init.body;
|
|
623
793
|
else if (init.body instanceof Uint8Array || init.body instanceof ArrayBuffer)
|
|
624
|
-
body = new TextDecoder("utf-8", { fatal: true }).decode(init.body);
|
|
794
|
+
body = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(init.body);
|
|
625
795
|
else throw new TypeError("Web Bot Auth fetch requires a UTF-8 string or byte body");
|
|
626
796
|
}
|
|
627
797
|
const headers = {};
|
|
@@ -642,19 +812,26 @@ function createWebBotAuthFetch(upstream, key, options) {
|
|
|
642
812
|
WEB_BOT_AUTH_DRAFT_REVISION,
|
|
643
813
|
WEB_BOT_AUTH_PROFILE,
|
|
644
814
|
WebBotAuthError,
|
|
815
|
+
WebBotAuthUnavailableError,
|
|
645
816
|
buildWebBotAuthSignatureBase,
|
|
646
817
|
createSignedSignatureDirectory,
|
|
647
818
|
createWebBotAuthAuthorizationFailure,
|
|
648
819
|
createWebBotAuthFetch,
|
|
820
|
+
deferWebBotAuthPrincipalFailure,
|
|
649
821
|
getWebBotAuthAuthorizationFailure,
|
|
650
822
|
getWebBotAuthLegacyOriginGate,
|
|
651
823
|
isVerifiedWebBotSigner,
|
|
824
|
+
isWebBotAuthRevocationChecked,
|
|
652
825
|
jwkThumbprint,
|
|
653
826
|
receivedAgentOrigin,
|
|
654
827
|
registerWebBotAuthLegacyOriginGate,
|
|
828
|
+
registerWebBotAuthRevocationCheck,
|
|
655
829
|
selectSignatureProfile,
|
|
656
830
|
signWebBotAuth,
|
|
831
|
+
verifyKeyDirectory,
|
|
657
832
|
verifySignatureDirectory,
|
|
658
833
|
verifyWebBotAuth,
|
|
659
|
-
|
|
834
|
+
webBotAuthPrincipalFailureError,
|
|
835
|
+
webBotAuthToolFailure,
|
|
836
|
+
webBotAuthUnavailableResponse
|
|
660
837
|
});
|