@fedify/fedify 2.1.0-dev.503 → 2.1.0-dev.523
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/{builder-BHUnSQtB.js → builder-CJkMYxxc.js} +9 -3
- package/dist/compat/mod.d.cts +3 -3
- package/dist/compat/mod.d.ts +3 -3
- package/dist/compat/transformers.test.js +12 -12
- package/dist/{context-DZJhUmzF.d.ts → context--RwChtri.d.ts} +54 -2
- package/dist/{context-D3QkEtZd.d.cts → context-DL0cPpPV.d.cts} +54 -2
- package/dist/{deno-BYerLnry.js → deno-CQdJQjC5.js} +1 -1
- package/dist/{docloader-MSkogD2T.js → docloader-Cyl0-S8m.js} +2 -2
- package/dist/federation/builder.test.js +14 -3
- package/dist/federation/handler.test.js +97 -13
- package/dist/federation/idempotency.test.js +12 -12
- package/dist/federation/inbox.test.js +2 -2
- package/dist/federation/keycache.test.js +46 -2
- package/dist/federation/middleware.test.js +206 -12
- package/dist/federation/mod.cjs +4 -4
- package/dist/federation/mod.d.cts +4 -4
- package/dist/federation/mod.d.ts +4 -4
- package/dist/federation/mod.js +4 -4
- package/dist/federation/send.test.js +5 -5
- package/dist/federation/webfinger.test.js +12 -12
- package/dist/{http-DkHdFfrc.d.ts → http-BbfOqHGG.d.ts} +80 -8
- package/dist/{http-DJT6NciB.cjs → http-D6a6mMc0.cjs} +305 -99
- package/dist/{http-CSX1-Mgi.js → http-DJmytoC2.js} +295 -101
- package/dist/{http-S2U3qDwN.js → http-DK0CTomU.js} +153 -57
- package/dist/{http-Cz3MlXAZ.d.cts → http-DsqqmkXi.d.cts} +80 -8
- package/dist/{inbox-BaA0g5I_.js → inbox-CWa6sqsk.js} +1 -1
- package/dist/{key-DCdTVZiK.js → key-DRgvVevp.js} +145 -47
- package/dist/keycache-C7k8s1Bk.js +102 -0
- package/dist/{kv-cache-Vtxhbo1W.cjs → kv-cache-DPtsJ1sL.cjs} +1 -1
- package/dist/{kv-cache-CQPL_aGY.js → kv-cache-MPcS_mGG.js} +1 -1
- package/dist/{ld-CrX7pQda.js → ld-s9_8WfBc.js} +2 -2
- package/dist/{middleware-CfI9C9Xy.js → middleware-2XtoTBq0.js} +12 -12
- package/dist/{middleware-MlO5iUeZ.js → middleware-Ajnk9qHB.js} +158 -22
- package/dist/middleware-BgCIhb_C.cjs +12 -0
- package/dist/{middleware-D4S6i4A_.cjs → middleware-BoCzk7-G.cjs} +158 -22
- package/dist/{middleware-C8PKuPrm.js → middleware-DGUNDGCl.js} +4 -4
- package/dist/{middleware-BelSJK7m.js → middleware-Dn9UDJZP.js} +100 -24
- package/dist/{mod-CwZXZJ9d.d.ts → mod-BugwI0JN.d.ts} +1 -1
- package/dist/{mod-DPkRU3EK.d.cts → mod-CFBU2OT3.d.cts} +1 -1
- package/dist/{mod-DUWcVv49.d.ts → mod-CvxylbuV.d.ts} +1 -1
- package/dist/{mod-DVwHUI_x.d.cts → mod-DE8MYisy.d.cts} +1 -1
- package/dist/{mod-DXsQakeS.d.cts → mod-DKG0ovjR.d.cts} +1 -1
- package/dist/{mod-DnSsduJF.d.ts → mod-DcfFNgYf.d.ts} +1 -1
- package/dist/{mod-Di3W5OdP.d.cts → mod-Dp0kK0hO.d.cts} +1 -1
- package/dist/{mod-DosD6NsG.d.ts → mod-Z7lIaCfo.d.ts} +1 -1
- package/dist/mod.cjs +8 -4
- package/dist/mod.d.cts +8 -8
- package/dist/mod.d.ts +8 -8
- package/dist/mod.js +7 -5
- package/dist/nodeinfo/handler.test.js +12 -12
- package/dist/otel/exporter.test.js +43 -2
- package/dist/otel/mod.cjs +7 -1
- package/dist/otel/mod.d.cts +12 -0
- package/dist/otel/mod.d.ts +12 -0
- package/dist/otel/mod.js +7 -1
- package/dist/{owner-BAlnLKMO.js → owner-Cx8gV-j4.js} +1 -1
- package/dist/{proof-DMgHaXNJ.js → proof-CDr3NP3R.js} +2 -2
- package/dist/{proof-BgUVmaJz.js → proof-Le4DAkqb.js} +1 -1
- package/dist/{proof-CR5RUAmy.cjs → proof-qHcNgE5i.cjs} +1 -1
- package/dist/{send-B2aZYf9A.js → send-DreBSY1U.js} +2 -2
- package/dist/sig/http.test.js +85 -5
- package/dist/sig/key.test.js +70 -3
- package/dist/sig/ld.test.js +3 -3
- package/dist/sig/mod.cjs +4 -2
- package/dist/sig/mod.d.cts +3 -3
- package/dist/sig/mod.d.ts +3 -3
- package/dist/sig/mod.js +3 -3
- package/dist/sig/owner.test.js +3 -3
- package/dist/sig/proof.test.js +3 -3
- package/dist/testing/mod.d.ts +92 -0
- package/dist/utils/docloader.test.js +4 -4
- package/dist/utils/mod.cjs +2 -2
- package/dist/utils/mod.d.cts +2 -2
- package/dist/utils/mod.d.ts +2 -2
- package/dist/utils/mod.js +2 -2
- package/package.json +5 -5
- package/dist/keycache-DRxpZ5r9.js +0 -48
- package/dist/middleware-D4XcpSBG.cjs +0 -12
|
@@ -9,11 +9,11 @@ import { encodeHex } from "byte-encodings/hex";
|
|
|
9
9
|
import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
|
|
10
10
|
import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
|
|
11
11
|
import { Item, decodeDict, encodeItem } from "structured-field-values";
|
|
12
|
-
import { getDocumentLoader } from "@fedify/vocab-runtime";
|
|
12
|
+
import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
13
13
|
|
|
14
14
|
//#region deno.json
|
|
15
15
|
var name = "@fedify/fedify";
|
|
16
|
-
var version = "2.1.0-dev.
|
|
16
|
+
var version = "2.1.0-dev.523+150998a5";
|
|
17
17
|
var license = "MIT";
|
|
18
18
|
var exports = {
|
|
19
19
|
".": "./src/mod.ts",
|
|
@@ -207,24 +207,10 @@ async function importJwk(jwk, type) {
|
|
|
207
207
|
validateCryptoKey(key, type);
|
|
208
208
|
return key;
|
|
209
209
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
* If the given URL contains an {@link Actor} object, it tries to find
|
|
213
|
-
* the corresponding key in the `publicKey` or `assertionMethod` property.
|
|
214
|
-
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
215
|
-
* or {@link Multikey}.
|
|
216
|
-
* @param keyId The URL of the key.
|
|
217
|
-
* @param cls The class of the key to fetch. Either {@link CryptographicKey}
|
|
218
|
-
* or {@link Multikey}.
|
|
219
|
-
* @param options Options for fetching the key. See {@link FetchKeyOptions}.
|
|
220
|
-
* @returns The fetched key or `null` if the key is not found.
|
|
221
|
-
* @since 1.3.0
|
|
222
|
-
*/
|
|
223
|
-
function fetchKey(keyId, cls, options = {}) {
|
|
224
|
-
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
210
|
+
async function withFetchKeySpan(keyId, tracerProvider, fetcher) {
|
|
211
|
+
tracerProvider ??= trace.getTracerProvider();
|
|
225
212
|
const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
|
|
226
|
-
|
|
227
|
-
return tracer.startActiveSpan("activitypub.fetch_key", {
|
|
213
|
+
return await tracer.startActiveSpan("activitypub.fetch_key", {
|
|
228
214
|
kind: SpanKind.CLIENT,
|
|
229
215
|
attributes: {
|
|
230
216
|
"http.method": "GET",
|
|
@@ -237,7 +223,7 @@ function fetchKey(keyId, cls, options = {}) {
|
|
|
237
223
|
}
|
|
238
224
|
}, async (span) => {
|
|
239
225
|
try {
|
|
240
|
-
const result = await
|
|
226
|
+
const result = await fetcher();
|
|
241
227
|
span.setAttribute("activitypub.actor.key.cached", result.cached);
|
|
242
228
|
return result;
|
|
243
229
|
} catch (e) {
|
|
@@ -251,43 +237,105 @@ function fetchKey(keyId, cls, options = {}) {
|
|
|
251
237
|
}
|
|
252
238
|
});
|
|
253
239
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
240
|
+
/**
|
|
241
|
+
* Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL.
|
|
242
|
+
* If the given URL contains an {@link Actor} object, it tries to find
|
|
243
|
+
* the corresponding key in the `publicKey` or `assertionMethod` property.
|
|
244
|
+
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
245
|
+
* or {@link Multikey}.
|
|
246
|
+
* @param keyId The URL of the key.
|
|
247
|
+
* @param cls The class of the key to fetch. Either {@link CryptographicKey}
|
|
248
|
+
* or {@link Multikey}.
|
|
249
|
+
* @param options Options for fetching the key. See {@link FetchKeyOptions}.
|
|
250
|
+
* @returns The fetched key or `null` if the key is not found.
|
|
251
|
+
* @since 1.3.0
|
|
252
|
+
*/
|
|
253
|
+
function fetchKey(keyId, cls, options = {}) {
|
|
254
|
+
keyId = typeof keyId === "string" ? new URL(keyId) : keyId;
|
|
255
|
+
return withFetchKeySpan(keyId, options.tracerProvider, () => fetchKeyInternal(keyId, cls, options));
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL,
|
|
259
|
+
* preserving transport-level fetch failures for callers that need to inspect
|
|
260
|
+
* why the key could not be loaded.
|
|
261
|
+
*
|
|
262
|
+
* @template T The type of the key to fetch. Either {@link CryptographicKey}
|
|
263
|
+
* or {@link Multikey}.
|
|
264
|
+
* @param keyId The URL of the key.
|
|
265
|
+
* @param cls The class of the key to fetch. Either {@link CryptographicKey}
|
|
266
|
+
* or {@link Multikey}.
|
|
267
|
+
* @param options Options for fetching the key.
|
|
268
|
+
* @returns The fetched key, or detailed fetch failure information.
|
|
269
|
+
* @since 2.1.0
|
|
270
|
+
*/
|
|
271
|
+
async function fetchKeyDetailed(keyId, cls, options = {}) {
|
|
260
272
|
const cacheKey = typeof keyId === "string" ? new URL(keyId) : keyId;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
273
|
+
return await withFetchKeySpan(cacheKey, options.tracerProvider, async () => {
|
|
274
|
+
return await fetchKeyWithResult(cacheKey, cls, options, async (cacheKey$1, keyId$1, keyCache, logger) => {
|
|
275
|
+
const fetchError = await keyCache?.getFetchError?.(cacheKey$1);
|
|
276
|
+
if (fetchError != null) {
|
|
277
|
+
logger.debug("Entry {keyId} found in cache with preserved fetch failure details.", { keyId: keyId$1 });
|
|
278
|
+
return {
|
|
279
|
+
key: null,
|
|
280
|
+
cached: true,
|
|
281
|
+
fetchError
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
logger.debug("Entry {keyId} found in cache, but no fetch failure details are available.", { keyId: keyId$1 });
|
|
266
285
|
return {
|
|
267
|
-
key:
|
|
286
|
+
key: null,
|
|
268
287
|
cached: true
|
|
269
288
|
};
|
|
270
|
-
}
|
|
271
|
-
logger.debug("
|
|
289
|
+
}, async (error, cacheKey$1, keyId$1, keyCache, logger) => {
|
|
290
|
+
logger.debug("Failed to fetch key {keyId}.", {
|
|
291
|
+
keyId: keyId$1,
|
|
292
|
+
error
|
|
293
|
+
});
|
|
294
|
+
await keyCache?.set(cacheKey$1, null);
|
|
295
|
+
if (error instanceof FetchError && error.response != null) {
|
|
296
|
+
const fetchError$1 = {
|
|
297
|
+
status: error.response.status,
|
|
298
|
+
response: error.response.clone()
|
|
299
|
+
};
|
|
300
|
+
await keyCache?.setFetchError?.(cacheKey$1, fetchError$1);
|
|
301
|
+
return {
|
|
302
|
+
key: null,
|
|
303
|
+
cached: false,
|
|
304
|
+
fetchError: fetchError$1
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const fetchError = { error: error instanceof Error ? error : new Error(String(error)) };
|
|
308
|
+
await keyCache?.setFetchError?.(cacheKey$1, fetchError);
|
|
272
309
|
return {
|
|
273
310
|
key: null,
|
|
274
|
-
cached:
|
|
311
|
+
cached: false,
|
|
312
|
+
fetchError
|
|
275
313
|
};
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
async function getCachedFetchKey(cacheKey, keyId, cls, keyCache, logger) {
|
|
318
|
+
if (keyCache == null) return null;
|
|
319
|
+
const cachedKey = await keyCache.get(cacheKey);
|
|
320
|
+
if (cachedKey instanceof cls && cachedKey.publicKey != null) {
|
|
321
|
+
logger.debug("Key {keyId} found in cache.", { keyId });
|
|
322
|
+
return {
|
|
323
|
+
key: cachedKey,
|
|
324
|
+
cached: true
|
|
325
|
+
};
|
|
326
|
+
} else if (cachedKey === null) {
|
|
327
|
+
logger.debug("Entry {keyId} found in cache, but it is unavailable.", { keyId });
|
|
286
328
|
return {
|
|
287
329
|
key: null,
|
|
288
|
-
cached:
|
|
330
|
+
cached: true
|
|
289
331
|
};
|
|
290
332
|
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
async function clearFetchErrorMetadata(keyId, keyCache) {
|
|
336
|
+
await keyCache?.setFetchError?.(keyId, null);
|
|
337
|
+
}
|
|
338
|
+
async function resolveFetchedKey(document, cacheKey, keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider }, logger) {
|
|
291
339
|
let object;
|
|
292
340
|
try {
|
|
293
341
|
object = await Object$1.fromJsonLd(document, {
|
|
@@ -307,6 +355,7 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
|
|
|
307
355
|
if (e$1 instanceof TypeError) {
|
|
308
356
|
logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
|
|
309
357
|
await keyCache?.set(cacheKey, null);
|
|
358
|
+
await clearFetchErrorMetadata(cacheKey, keyCache);
|
|
310
359
|
return {
|
|
311
360
|
key: null,
|
|
312
361
|
cached: false
|
|
@@ -345,6 +394,7 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
|
|
|
345
394
|
actorType: object.constructor.name
|
|
346
395
|
});
|
|
347
396
|
await keyCache?.set(cacheKey, null);
|
|
397
|
+
await clearFetchErrorMetadata(cacheKey, keyCache);
|
|
348
398
|
return {
|
|
349
399
|
key: null,
|
|
350
400
|
cached: false
|
|
@@ -353,6 +403,7 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
|
|
|
353
403
|
} else {
|
|
354
404
|
logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
|
|
355
405
|
await keyCache?.set(cacheKey, null);
|
|
406
|
+
await clearFetchErrorMetadata(cacheKey, keyCache);
|
|
356
407
|
return {
|
|
357
408
|
key: null,
|
|
358
409
|
cached: false
|
|
@@ -361,6 +412,7 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
|
|
|
361
412
|
if (key.publicKey == null) {
|
|
362
413
|
logger.debug("Failed to verify; key {keyId} has no publicKeyPem field.", { keyId });
|
|
363
414
|
await keyCache?.set(cacheKey, null);
|
|
415
|
+
await clearFetchErrorMetadata(cacheKey, keyCache);
|
|
364
416
|
return {
|
|
365
417
|
key: null,
|
|
366
418
|
cached: false
|
|
@@ -370,11 +422,57 @@ async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, key
|
|
|
370
422
|
await keyCache.set(cacheKey, key);
|
|
371
423
|
logger.debug("Key {keyId} cached.", { keyId });
|
|
372
424
|
}
|
|
425
|
+
await clearFetchErrorMetadata(cacheKey, keyCache);
|
|
373
426
|
return {
|
|
374
427
|
key,
|
|
375
428
|
cached: false
|
|
376
429
|
};
|
|
377
430
|
}
|
|
431
|
+
async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, onFetchError) {
|
|
432
|
+
const logger = getLogger([
|
|
433
|
+
"fedify",
|
|
434
|
+
"sig",
|
|
435
|
+
"key"
|
|
436
|
+
]);
|
|
437
|
+
const keyId = cacheKey.href;
|
|
438
|
+
const keyCache = options.keyCache;
|
|
439
|
+
const cached = await getCachedFetchKey(cacheKey, keyId, cls, keyCache, logger);
|
|
440
|
+
if (cached?.key === null && cached.cached) return await onCachedUnavailable(cacheKey, keyId, keyCache, logger);
|
|
441
|
+
if (cached != null) return cached;
|
|
442
|
+
logger.debug("Fetching key {keyId} to verify signature...", { keyId });
|
|
443
|
+
let document;
|
|
444
|
+
try {
|
|
445
|
+
const remoteDocument = await (options.documentLoader ?? getDocumentLoader())(keyId);
|
|
446
|
+
document = remoteDocument.document;
|
|
447
|
+
} catch (error) {
|
|
448
|
+
return await onFetchError(error, cacheKey, keyId, keyCache, logger);
|
|
449
|
+
}
|
|
450
|
+
return await resolveFetchedKey(document, cacheKey, keyId, cls, options, logger);
|
|
451
|
+
}
|
|
452
|
+
async function fetchKeyInternal(keyId, cls, options = {}) {
|
|
453
|
+
const cacheKey = typeof keyId === "string" ? new URL(keyId) : keyId;
|
|
454
|
+
return await fetchKeyWithResult(cacheKey, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
|
|
455
|
+
return {
|
|
456
|
+
key: null,
|
|
457
|
+
cached: true
|
|
458
|
+
};
|
|
459
|
+
}, async (error, cacheKey$1, keyId$1, keyCache, logger) => {
|
|
460
|
+
logger.debug("Failed to fetch key {keyId}.", {
|
|
461
|
+
keyId: keyId$1,
|
|
462
|
+
error
|
|
463
|
+
});
|
|
464
|
+
await keyCache?.set(cacheKey$1, null);
|
|
465
|
+
if (error instanceof FetchError && error.response != null) await keyCache?.setFetchError?.(cacheKey$1, {
|
|
466
|
+
status: error.response.status,
|
|
467
|
+
response: error.response.clone()
|
|
468
|
+
});
|
|
469
|
+
else await keyCache?.setFetchError?.(cacheKey$1, { error: error instanceof Error ? error : new Error(String(error)) });
|
|
470
|
+
return {
|
|
471
|
+
key: null,
|
|
472
|
+
cached: false
|
|
473
|
+
};
|
|
474
|
+
});
|
|
475
|
+
}
|
|
378
476
|
|
|
379
477
|
//#endregion
|
|
380
478
|
//#region src/sig/http.ts
|
|
@@ -604,6 +702,55 @@ const supportedHashAlgorithms = {
|
|
|
604
702
|
"sha-256": "SHA-256",
|
|
605
703
|
"sha-512": "SHA-512"
|
|
606
704
|
};
|
|
705
|
+
function noSignatureResult() {
|
|
706
|
+
return {
|
|
707
|
+
verified: false,
|
|
708
|
+
reason: { type: "noSignature" }
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function invalidSignatureResult(keyId) {
|
|
712
|
+
return keyId == null ? {
|
|
713
|
+
verified: false,
|
|
714
|
+
reason: { type: "invalidSignature" }
|
|
715
|
+
} : {
|
|
716
|
+
verified: false,
|
|
717
|
+
reason: {
|
|
718
|
+
type: "invalidSignature",
|
|
719
|
+
keyId
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function keyFetchErrorResult(keyId, result) {
|
|
724
|
+
return {
|
|
725
|
+
verified: false,
|
|
726
|
+
reason: {
|
|
727
|
+
type: "keyFetchError",
|
|
728
|
+
keyId,
|
|
729
|
+
result
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
function parseKeyId(value) {
|
|
734
|
+
if (value == null) return null;
|
|
735
|
+
try {
|
|
736
|
+
return new URL(value);
|
|
737
|
+
} catch {
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
function getKeyFetchErrorName(error) {
|
|
742
|
+
return error.name || error.constructor.name || "Error";
|
|
743
|
+
}
|
|
744
|
+
function recordVerificationResult(span, result) {
|
|
745
|
+
span.setAttribute("http_signatures.verified", result.verified);
|
|
746
|
+
if (result.verified === true) return;
|
|
747
|
+
const reason = result.reason;
|
|
748
|
+
span.setAttribute("http_signatures.failure_reason", reason.type);
|
|
749
|
+
if ("keyId" in reason && reason.keyId != null) span.setAttribute("http_signatures.key_id", reason.keyId.href);
|
|
750
|
+
if (reason.type !== "keyFetchError") return;
|
|
751
|
+
if ("status" in reason.result) span.setAttribute("http_signatures.key_fetch_status", reason.result.status);
|
|
752
|
+
else span.setAttribute("http_signatures.key_fetch_error", getKeyFetchErrorName(reason.result.error));
|
|
753
|
+
}
|
|
607
754
|
/**
|
|
608
755
|
* Verifies the signature of a request.
|
|
609
756
|
*
|
|
@@ -618,6 +765,19 @@ const supportedHashAlgorithms = {
|
|
|
618
765
|
* could not be verified.
|
|
619
766
|
*/
|
|
620
767
|
async function verifyRequest(request, options = {}) {
|
|
768
|
+
const result = await verifyRequestDetailed(request, options);
|
|
769
|
+
return result.verified ? result.key : null;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Verifies the signature of a request and returns a structured failure reason
|
|
773
|
+
* when verification does not succeed.
|
|
774
|
+
*
|
|
775
|
+
* @param request The request to verify.
|
|
776
|
+
* @param options Options for verifying the request.
|
|
777
|
+
* @returns The verified public key, or a structured verification failure.
|
|
778
|
+
* @since 2.1.0
|
|
779
|
+
*/
|
|
780
|
+
async function verifyRequestDetailed(request, options = {}) {
|
|
621
781
|
const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
|
|
622
782
|
const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
|
|
623
783
|
return await tracer.startActiveSpan("http_signatures.verify", async (span) => {
|
|
@@ -629,11 +789,12 @@ async function verifyRequest(request, options = {}) {
|
|
|
629
789
|
try {
|
|
630
790
|
let spec = options.spec;
|
|
631
791
|
if (spec == null) spec = request.headers.has("Signature-Input") ? "rfc9421" : "draft-cavage-http-signatures-12";
|
|
632
|
-
let
|
|
633
|
-
if (spec === "rfc9421")
|
|
634
|
-
else
|
|
635
|
-
|
|
636
|
-
|
|
792
|
+
let result;
|
|
793
|
+
if (spec === "rfc9421") result = await verifyRequestRfc9421(request, span, options);
|
|
794
|
+
else result = await verifyRequestDraft(request, span, options);
|
|
795
|
+
recordVerificationResult(span, result);
|
|
796
|
+
if (!result.verified) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
797
|
+
return result;
|
|
637
798
|
} catch (error) {
|
|
638
799
|
span.setStatus({
|
|
639
800
|
code: SpanStatusCode.ERROR,
|
|
@@ -653,27 +814,29 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
653
814
|
]);
|
|
654
815
|
if (request.bodyUsed) {
|
|
655
816
|
logger.error("Failed to verify; the request body is already consumed.", { url: request.url });
|
|
656
|
-
return
|
|
817
|
+
return noSignatureResult();
|
|
657
818
|
} else if (request.body?.locked) {
|
|
658
819
|
logger.error("Failed to verify; the request body is locked.", { url: request.url });
|
|
659
|
-
return
|
|
820
|
+
return noSignatureResult();
|
|
660
821
|
}
|
|
661
822
|
const originalRequest = request;
|
|
662
823
|
request = request.clone();
|
|
663
|
-
const dateHeader = request.headers.get("Date");
|
|
664
|
-
if (dateHeader == null) {
|
|
665
|
-
logger.debug("Failed to verify; no Date header found.", { headers: Object.fromEntries(request.headers.entries()) });
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
824
|
const sigHeader = request.headers.get("Signature");
|
|
669
825
|
if (sigHeader == null) {
|
|
670
826
|
logger.debug("Failed to verify; no Signature header found.", { headers: Object.fromEntries(request.headers.entries()) });
|
|
671
|
-
return
|
|
827
|
+
return noSignatureResult();
|
|
828
|
+
}
|
|
829
|
+
const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)=(?:"([^"]*)"|(\d+))\s*$/)).filter((m) => m != null).map((m) => [m[1], m[2] ?? m[3]]));
|
|
830
|
+
const parsedKeyId = parseKeyId(sigValues.keyId);
|
|
831
|
+
const dateHeader = request.headers.get("Date");
|
|
832
|
+
if (dateHeader == null) {
|
|
833
|
+
logger.debug("Failed to verify; no Date header found.", { headers: Object.fromEntries(request.headers.entries()) });
|
|
834
|
+
return invalidSignatureResult(parsedKeyId);
|
|
672
835
|
}
|
|
673
836
|
const digestHeader = request.headers.get("Digest");
|
|
674
837
|
if (request.method !== "GET" && request.method !== "HEAD" && digestHeader == null) {
|
|
675
838
|
logger.debug("Failed to verify; no Digest header found.", { headers: Object.fromEntries(request.headers.entries()) });
|
|
676
|
-
return
|
|
839
|
+
return invalidSignatureResult(parsedKeyId);
|
|
677
840
|
}
|
|
678
841
|
let body = null;
|
|
679
842
|
if (digestHeader != null) {
|
|
@@ -691,7 +854,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
691
854
|
digest: digestBase64,
|
|
692
855
|
error
|
|
693
856
|
});
|
|
694
|
-
return
|
|
857
|
+
return invalidSignatureResult(parsedKeyId);
|
|
695
858
|
}
|
|
696
859
|
if (span.isRecording()) span.setAttribute(`http_signatures.digest.${algo}`, encodeHex(digest));
|
|
697
860
|
const expectedDigest = await crypto.subtle.digest(supportedHashAlgorithms[algo], body);
|
|
@@ -701,7 +864,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
701
864
|
digest: digestBase64,
|
|
702
865
|
expectedDigest: encodeBase64(expectedDigest)
|
|
703
866
|
});
|
|
704
|
-
return
|
|
867
|
+
return invalidSignatureResult(parsedKeyId);
|
|
705
868
|
}
|
|
706
869
|
matched = true;
|
|
707
870
|
}
|
|
@@ -710,7 +873,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
710
873
|
supportedAlgorithms: Object.keys(supportedHashAlgorithms),
|
|
711
874
|
algorithms: digests.map(([algo]) => algo)
|
|
712
875
|
});
|
|
713
|
-
return
|
|
876
|
+
return invalidSignatureResult(parsedKeyId);
|
|
714
877
|
}
|
|
715
878
|
}
|
|
716
879
|
const date = Temporal.Instant.from(new Date(dateHeader).toISOString());
|
|
@@ -722,25 +885,24 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
722
885
|
date: date.toString(),
|
|
723
886
|
now: now.toString()
|
|
724
887
|
});
|
|
725
|
-
return
|
|
888
|
+
return invalidSignatureResult(parsedKeyId);
|
|
726
889
|
} else if (Temporal.Instant.compare(date, now.subtract(tw)) < 0) {
|
|
727
890
|
logger.debug("Failed to verify; Date is too far in the past.", {
|
|
728
891
|
date: date.toString(),
|
|
729
892
|
now: now.toString()
|
|
730
893
|
});
|
|
731
|
-
return
|
|
894
|
+
return invalidSignatureResult(parsedKeyId);
|
|
732
895
|
}
|
|
733
896
|
}
|
|
734
|
-
const sigValues = Object.fromEntries(sigHeader.split(",").map((pair) => pair.match(/^\s*([A-Za-z]+)=(?:"([^"]*)"|(\d+))\s*$/)).filter((m) => m != null).map((m) => [m[1], m[2] ?? m[3]]));
|
|
735
897
|
if (!("keyId" in sigValues)) {
|
|
736
898
|
logger.debug("Failed to verify; no keyId field found in the Signature header.", { signature: sigHeader });
|
|
737
|
-
return null;
|
|
899
|
+
return invalidSignatureResult(null);
|
|
738
900
|
} else if (!("headers" in sigValues)) {
|
|
739
901
|
logger.debug("Failed to verify; no headers field found in the Signature header.", { signature: sigHeader });
|
|
740
|
-
return
|
|
902
|
+
return invalidSignatureResult(parsedKeyId);
|
|
741
903
|
} else if (!("signature" in sigValues)) {
|
|
742
904
|
logger.debug("Failed to verify; no signature field found in the Signature header.", { signature: sigHeader });
|
|
743
|
-
return
|
|
905
|
+
return invalidSignatureResult(parsedKeyId);
|
|
744
906
|
}
|
|
745
907
|
if ("expires" in sigValues) {
|
|
746
908
|
const expiresSeconds = parseInt(sigValues.expires);
|
|
@@ -749,7 +911,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
749
911
|
expires: sigValues.expires,
|
|
750
912
|
signature: sigHeader
|
|
751
913
|
});
|
|
752
|
-
return
|
|
914
|
+
return invalidSignatureResult(parsedKeyId);
|
|
753
915
|
}
|
|
754
916
|
const expires = Temporal.Instant.fromEpochMilliseconds(expiresSeconds * 1e3);
|
|
755
917
|
if (Temporal.Instant.compare(now, expires) > 0) {
|
|
@@ -758,7 +920,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
758
920
|
now: now.toString(),
|
|
759
921
|
signature: sigHeader
|
|
760
922
|
});
|
|
761
|
-
return
|
|
923
|
+
return invalidSignatureResult(parsedKeyId);
|
|
762
924
|
}
|
|
763
925
|
}
|
|
764
926
|
if ("created" in sigValues) {
|
|
@@ -768,7 +930,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
768
930
|
created: sigValues.created,
|
|
769
931
|
signature: sigHeader
|
|
770
932
|
});
|
|
771
|
-
return
|
|
933
|
+
return invalidSignatureResult(parsedKeyId);
|
|
772
934
|
}
|
|
773
935
|
if (timeWindow !== false) {
|
|
774
936
|
const created = Temporal.Instant.fromEpochMilliseconds(createdSeconds * 1e3);
|
|
@@ -778,34 +940,37 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
778
940
|
created: created.toString(),
|
|
779
941
|
now: now.toString()
|
|
780
942
|
});
|
|
781
|
-
return
|
|
943
|
+
return invalidSignatureResult(parsedKeyId);
|
|
782
944
|
} else if (Temporal.Instant.compare(created, now.subtract(tw)) < 0) {
|
|
783
945
|
logger.debug("Failed to verify; created is too far in the past.", {
|
|
784
946
|
created: created.toString(),
|
|
785
947
|
now: now.toString()
|
|
786
948
|
});
|
|
787
|
-
return
|
|
949
|
+
return invalidSignatureResult(parsedKeyId);
|
|
788
950
|
}
|
|
789
951
|
}
|
|
790
952
|
}
|
|
791
953
|
const { keyId, headers, signature } = sigValues;
|
|
954
|
+
const keyIdUrl = parseKeyId(keyId);
|
|
955
|
+
if (keyIdUrl == null) return invalidSignatureResult(null);
|
|
792
956
|
span?.setAttribute("http_signatures.key_id", keyId);
|
|
793
957
|
if ("algorithm" in sigValues) span?.setAttribute("http_signatures.algorithm", sigValues.algorithm);
|
|
794
|
-
const { key, cached } = await
|
|
958
|
+
const { key, cached, fetchError } = await fetchKeyDetailed(keyIdUrl, CryptographicKey, {
|
|
795
959
|
documentLoader,
|
|
796
960
|
contextLoader,
|
|
797
961
|
keyCache,
|
|
798
962
|
tracerProvider
|
|
799
963
|
});
|
|
800
|
-
if (
|
|
964
|
+
if (fetchError != null) return keyFetchErrorResult(keyIdUrl, fetchError);
|
|
965
|
+
if (key == null) return invalidSignatureResult(keyIdUrl);
|
|
801
966
|
const headerNames = headers.split(/\s+/g);
|
|
802
967
|
if (!headerNames.includes("(request-target)") || !headerNames.includes("date")) {
|
|
803
968
|
logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
|
|
804
|
-
return
|
|
969
|
+
return invalidSignatureResult(keyIdUrl);
|
|
805
970
|
}
|
|
806
971
|
if (body != null && !headerNames.includes("digest")) {
|
|
807
972
|
logger.debug("Failed to verify; required headers missing in the Signature header: {headers}.", { headers });
|
|
808
|
-
return
|
|
973
|
+
return invalidSignatureResult(keyIdUrl);
|
|
809
974
|
}
|
|
810
975
|
const message = headerNames.map((name$1) => `${name$1}: ` + (name$1 === "(request-target)" ? `${request.method.toLowerCase()} ${new URL(request.url).pathname}` : name$1 === "(created)" ? sigValues.created ?? "" : name$1 === "(expires)" ? sigValues.expires ?? "" : name$1 === "host" ? request.headers.get("host") ?? new URL(request.url).host : request.headers.get(name$1))).join("\n");
|
|
811
976
|
const sig = decodeBase64(signature);
|
|
@@ -818,7 +983,7 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
818
983
|
signature,
|
|
819
984
|
message
|
|
820
985
|
});
|
|
821
|
-
return await
|
|
986
|
+
return await verifyRequestDetailed(originalRequest, {
|
|
822
987
|
documentLoader,
|
|
823
988
|
contextLoader,
|
|
824
989
|
timeWindow,
|
|
@@ -834,9 +999,12 @@ async function verifyRequestDraft(request, span, { documentLoader, contextLoader
|
|
|
834
999
|
signature,
|
|
835
1000
|
message
|
|
836
1001
|
});
|
|
837
|
-
return
|
|
1002
|
+
return invalidSignatureResult(keyIdUrl);
|
|
838
1003
|
}
|
|
839
|
-
return
|
|
1004
|
+
return {
|
|
1005
|
+
verified: true,
|
|
1006
|
+
key
|
|
1007
|
+
};
|
|
840
1008
|
}
|
|
841
1009
|
/**
|
|
842
1010
|
* RFC 9421 map of algorithm identifiers to WebCrypto algorithms
|
|
@@ -907,22 +1075,22 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
|
|
|
907
1075
|
]);
|
|
908
1076
|
if (request.bodyUsed) {
|
|
909
1077
|
logger.error("Failed to verify; the request body is already consumed.", { url: request.url });
|
|
910
|
-
return
|
|
1078
|
+
return noSignatureResult();
|
|
911
1079
|
} else if (request.body?.locked) {
|
|
912
1080
|
logger.error("Failed to verify; the request body is locked.", { url: request.url });
|
|
913
|
-
return
|
|
1081
|
+
return noSignatureResult();
|
|
914
1082
|
}
|
|
915
1083
|
const originalRequest = request;
|
|
916
1084
|
request = request.clone();
|
|
917
1085
|
const signatureInputHeader = request.headers.get("Signature-Input");
|
|
918
1086
|
if (!signatureInputHeader) {
|
|
919
1087
|
logger.debug("Failed to verify; no Signature-Input header found.", { headers: Object.fromEntries(request.headers.entries()) });
|
|
920
|
-
return
|
|
1088
|
+
return noSignatureResult();
|
|
921
1089
|
}
|
|
922
1090
|
const signatureHeader = request.headers.get("Signature");
|
|
923
1091
|
if (!signatureHeader) {
|
|
924
1092
|
logger.debug("Failed to verify; no Signature header found.", { headers: Object.fromEntries(request.headers.entries()) });
|
|
925
|
-
return
|
|
1093
|
+
return noSignatureResult();
|
|
926
1094
|
}
|
|
927
1095
|
const signatureInputs = parseRfc9421SignatureInput(signatureInputHeader);
|
|
928
1096
|
logger.debug("Parsed Signature-Input header: {signatureInputs}", { signatureInputs });
|
|
@@ -930,18 +1098,23 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
|
|
|
930
1098
|
const signatureNames = Object.keys(signatureInputs);
|
|
931
1099
|
if (signatureNames.length === 0) {
|
|
932
1100
|
logger.debug("Failed to verify; no valid signatures found in Signature-Input header.", { header: signatureInputHeader });
|
|
933
|
-
return null;
|
|
1101
|
+
return invalidSignatureResult(null);
|
|
934
1102
|
}
|
|
935
|
-
let
|
|
1103
|
+
let failure = noSignatureResult();
|
|
936
1104
|
for (const sigName of signatureNames) {
|
|
937
|
-
if (!signatures[sigName])
|
|
1105
|
+
if (!signatures[sigName]) {
|
|
1106
|
+
failure = invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId));
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
938
1109
|
const sigInput = signatureInputs[sigName];
|
|
939
1110
|
const sigBytes = signatures[sigName];
|
|
1111
|
+
const keyId = parseKeyId(sigInput.keyId);
|
|
940
1112
|
if (!sigInput.keyId) {
|
|
941
1113
|
logger.debug("Failed to verify; missing keyId in signature {signatureName}.", {
|
|
942
1114
|
signatureName: sigName,
|
|
943
1115
|
signatureInput: signatureInputHeader
|
|
944
1116
|
});
|
|
1117
|
+
failure = invalidSignatureResult(null);
|
|
945
1118
|
continue;
|
|
946
1119
|
}
|
|
947
1120
|
if (!sigInput.created) {
|
|
@@ -949,6 +1122,7 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
|
|
|
949
1122
|
signatureName: sigName,
|
|
950
1123
|
signatureInput: signatureInputHeader
|
|
951
1124
|
});
|
|
1125
|
+
failure = invalidSignatureResult(keyId);
|
|
952
1126
|
continue;
|
|
953
1127
|
}
|
|
954
1128
|
const signatureCreated = Temporal.Instant.fromEpochMilliseconds(sigInput.created * 1e3);
|
|
@@ -960,12 +1134,14 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
|
|
|
960
1134
|
created: signatureCreated.toString(),
|
|
961
1135
|
now: now.toString()
|
|
962
1136
|
});
|
|
1137
|
+
failure = invalidSignatureResult(keyId);
|
|
963
1138
|
continue;
|
|
964
1139
|
} else if (Temporal.Instant.compare(signatureCreated, now.subtract(tw)) < 0) {
|
|
965
1140
|
logger.debug("Failed to verify; signature created time is too far in the past.", {
|
|
966
1141
|
created: signatureCreated.toString(),
|
|
967
1142
|
now: now.toString()
|
|
968
1143
|
});
|
|
1144
|
+
failure = invalidSignatureResult(keyId);
|
|
969
1145
|
continue;
|
|
970
1146
|
}
|
|
971
1147
|
}
|
|
@@ -973,25 +1149,36 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
|
|
|
973
1149
|
const contentDigestHeader = request.headers.get("Content-Digest");
|
|
974
1150
|
if (!contentDigestHeader) {
|
|
975
1151
|
logger.debug("Failed to verify; Content-Digest header required but not found.", { components: sigInput.components });
|
|
1152
|
+
failure = invalidSignatureResult(keyId);
|
|
976
1153
|
continue;
|
|
977
1154
|
}
|
|
978
1155
|
const body = await request.arrayBuffer();
|
|
979
1156
|
const digestValid = await verifyRfc9421ContentDigest(contentDigestHeader, body);
|
|
980
1157
|
if (!digestValid) {
|
|
981
1158
|
logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
|
|
1159
|
+
failure = invalidSignatureResult(keyId);
|
|
982
1160
|
continue;
|
|
983
1161
|
}
|
|
984
1162
|
}
|
|
985
1163
|
span?.setAttribute("http_signatures.key_id", sigInput.keyId);
|
|
986
1164
|
span?.setAttribute("http_signatures.created", sigInput.created.toString());
|
|
987
|
-
|
|
1165
|
+
if (keyId == null) {
|
|
1166
|
+
failure = invalidSignatureResult(null);
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
const { key, cached, fetchError } = await fetchKeyDetailed(keyId, CryptographicKey, {
|
|
988
1170
|
documentLoader,
|
|
989
1171
|
contextLoader,
|
|
990
1172
|
keyCache,
|
|
991
1173
|
tracerProvider
|
|
992
1174
|
});
|
|
1175
|
+
if (fetchError != null) {
|
|
1176
|
+
failure = keyFetchErrorResult(keyId, fetchError);
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
993
1179
|
if (!key) {
|
|
994
1180
|
logger.debug("Failed to fetch key: {keyId}", { keyId: sigInput.keyId });
|
|
1181
|
+
failure = invalidSignatureResult(keyId);
|
|
995
1182
|
continue;
|
|
996
1183
|
}
|
|
997
1184
|
let alg = sigInput.alg?.toLowerCase();
|
|
@@ -1008,6 +1195,7 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
|
|
|
1008
1195
|
algorithm: sigInput.alg,
|
|
1009
1196
|
supported: Object.keys(rfc9421AlgorithmMap)
|
|
1010
1197
|
});
|
|
1198
|
+
failure = invalidSignatureResult(keyId);
|
|
1011
1199
|
continue;
|
|
1012
1200
|
}
|
|
1013
1201
|
let signatureBase;
|
|
@@ -1018,41 +1206,47 @@ async function verifyRequestRfc9421(request, span, { documentLoader, contextLoad
|
|
|
1018
1206
|
error,
|
|
1019
1207
|
signatureInput: sigInput
|
|
1020
1208
|
});
|
|
1209
|
+
failure = invalidSignatureResult(keyId);
|
|
1021
1210
|
continue;
|
|
1022
1211
|
}
|
|
1023
1212
|
const signatureBaseBytes = new TextEncoder().encode(signatureBase);
|
|
1024
1213
|
span?.setAttribute("http_signatures.signature", encodeHex(sigBytes));
|
|
1025
1214
|
try {
|
|
1026
1215
|
const verified = await crypto.subtle.verify(algorithm, key.publicKey, sigBytes.slice(), signatureBaseBytes);
|
|
1027
|
-
if (verified) {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
}
|
|
1216
|
+
if (verified) return {
|
|
1217
|
+
verified: true,
|
|
1218
|
+
key
|
|
1219
|
+
};
|
|
1220
|
+
else if (cached) {
|
|
1031
1221
|
logger.debug("Failed to verify with cached key {keyId}; retrying with fresh key...", { keyId: sigInput.keyId });
|
|
1032
|
-
return await
|
|
1222
|
+
return await verifyRequestDetailed(originalRequest, {
|
|
1033
1223
|
documentLoader,
|
|
1034
1224
|
contextLoader,
|
|
1035
1225
|
timeWindow,
|
|
1036
1226
|
currentTime,
|
|
1037
1227
|
keyCache: {
|
|
1038
1228
|
get: () => Promise.resolve(void 0),
|
|
1039
|
-
set: async (keyId, key$1) => await keyCache?.set(keyId, key$1)
|
|
1229
|
+
set: async (keyId$1, key$1) => await keyCache?.set(keyId$1, key$1)
|
|
1040
1230
|
},
|
|
1041
1231
|
spec: "rfc9421"
|
|
1042
1232
|
});
|
|
1043
|
-
} else
|
|
1044
|
-
keyId
|
|
1045
|
-
|
|
1046
|
-
|
|
1233
|
+
} else {
|
|
1234
|
+
logger.debug("Failed to verify signature with fetched key {keyId}; signature invalid.", {
|
|
1235
|
+
keyId: sigInput.keyId,
|
|
1236
|
+
signatureBase
|
|
1237
|
+
});
|
|
1238
|
+
failure = invalidSignatureResult(keyId);
|
|
1239
|
+
}
|
|
1047
1240
|
} catch (error) {
|
|
1048
1241
|
logger.debug("Error during signature verification: {error}", {
|
|
1049
1242
|
error,
|
|
1050
1243
|
keyId: sigInput.keyId,
|
|
1051
1244
|
algorithm: sigInput.alg
|
|
1052
1245
|
});
|
|
1246
|
+
failure = invalidSignatureResult(keyId);
|
|
1053
1247
|
}
|
|
1054
1248
|
}
|
|
1055
|
-
return
|
|
1249
|
+
return failure;
|
|
1056
1250
|
}
|
|
1057
1251
|
/**
|
|
1058
1252
|
* Helper function to create a new Request for redirect handling.
|
|
@@ -1170,4 +1364,4 @@ function timingSafeEqual(a, b) {
|
|
|
1170
1364
|
}
|
|
1171
1365
|
|
|
1172
1366
|
//#endregion
|
|
1173
|
-
export { deno_default, doubleKnock, exportJwk, fetchKey, generateCryptoKeyPair, importJwk, signRequest, validateCryptoKey, verifyRequest };
|
|
1367
|
+
export { deno_default, doubleKnock, exportJwk, fetchKey, fetchKeyDetailed, generateCryptoKeyPair, importJwk, signRequest, validateCryptoKey, verifyRequest, verifyRequestDetailed };
|