@fedify/fedify 2.1.0-dev.565 → 2.1.0-dev.592

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.
Files changed (96) hide show
  1. package/dist/accept-D7sAxyNa.js +143 -0
  2. package/dist/{assert_rejects-Ce45JcFg.js → assert_rejects-0h7I2Esa.js} +1 -1
  3. package/dist/{builder-Deoi2N2z.js → builder-B24i8eYp.js} +3 -3
  4. package/dist/compat/mod.d.cts +3 -3
  5. package/dist/compat/mod.d.ts +3 -3
  6. package/dist/compat/transformers.test.js +17 -16
  7. package/dist/{context-DL0cPpPV.d.cts → context-BcqA-0BL.d.cts} +52 -2
  8. package/dist/{context--RwChtri.d.ts → context-DyJjQQ_H.d.ts} +52 -2
  9. package/dist/{deno-CEdy89j9.js → deno-OR506Yti.js} +1 -2
  10. package/dist/{docloader-CL1QPJzN.js → docloader-BG_pP2fW.js} +2 -2
  11. package/dist/federation/builder.test.js +7 -7
  12. package/dist/federation/collection.test.js +5 -5
  13. package/dist/federation/handler.test.js +806 -26
  14. package/dist/federation/idempotency.test.js +22 -21
  15. package/dist/federation/inbox.test.js +3 -3
  16. package/dist/federation/keycache.test.js +1 -1
  17. package/dist/federation/kv.test.js +4 -4
  18. package/dist/federation/middleware.test.js +22 -21
  19. package/dist/federation/mod.cjs +4 -4
  20. package/dist/federation/mod.d.cts +4 -4
  21. package/dist/federation/mod.d.ts +4 -4
  22. package/dist/federation/mod.js +4 -4
  23. package/dist/federation/mq.test.js +4 -4
  24. package/dist/federation/negotiation.test.js +5 -5
  25. package/dist/federation/retry.test.js +2 -2
  26. package/dist/federation/router.test.js +4 -4
  27. package/dist/federation/send.test.js +11 -10
  28. package/dist/federation/webfinger.test.js +22 -21
  29. package/dist/{http-Dm9n1mRe.js → http-BUCxbGks.js} +144 -49
  30. package/dist/{http-DsqqmkXi.d.cts → http-BudnHZE2.d.cts} +229 -1
  31. package/dist/{http-iDlaLy8a.cjs → http-CaXARmaJ.cjs} +307 -50
  32. package/dist/{http-BbfOqHGG.d.ts → http-Dax_FIBo.d.ts} +229 -1
  33. package/dist/{http-VpqmUjje.js → http-DePHjWKP.js} +278 -51
  34. package/dist/{inbox-CMtnW0RE.js → inbox-D_LU1opv.js} +1 -1
  35. package/dist/{key-B0yADkL8.js → key-Cx3Tx_In.js} +1 -1
  36. package/dist/{kv-cache-551Om14-.cjs → kv-cache-CYTDBChd.cjs} +1 -1
  37. package/dist/{kv-cache-BSATpUtX.js → kv-cache-DizRqYX4.js} +1 -1
  38. package/dist/{ld-BBmbv1nb.js → ld-CLMJw_iX.js} +3 -3
  39. package/dist/{middleware-Cx0tTbX1.js → middleware--uATyG9i.js} +95 -18
  40. package/dist/{middleware-DpdPMZII.js → middleware-4fo4pEtA.js} +4 -4
  41. package/dist/{middleware-D11GYoP-.cjs → middleware-9YDezkYJ.cjs} +94 -17
  42. package/dist/middleware-C2PqSUaA.js +27 -0
  43. package/dist/middleware-DNY45l5T.cjs +12 -0
  44. package/dist/{middleware-Cldp2YSv.js → middleware-DzICTgdC.js} +113 -34
  45. package/dist/{mod-DE8MYisy.d.cts → mod-B7QkWzrL.d.cts} +1 -1
  46. package/dist/{mod-DKG0ovjR.d.cts → mod-Bx9jcLB8.d.cts} +1 -1
  47. package/dist/{mod-CFBU2OT3.d.cts → mod-Coe7KEgX.d.cts} +1 -1
  48. package/dist/{mod-BugwI0JN.d.ts → mod-Cs2dYEwI.d.ts} +1 -1
  49. package/dist/{mod-DcfFNgYf.d.ts → mod-D6MdymW7.d.ts} +1 -1
  50. package/dist/{mod-CvxylbuV.d.ts → mod-D6dOd--H.d.ts} +1 -1
  51. package/dist/{mod-Z7lIaCfo.d.ts → mod-SMHOMNpZ.d.ts} +1 -1
  52. package/dist/{mod-Dp0kK0hO.d.cts → mod-em2Il1eD.d.cts} +1 -1
  53. package/dist/mod.cjs +12 -4
  54. package/dist/mod.d.cts +8 -8
  55. package/dist/mod.d.ts +8 -8
  56. package/dist/mod.js +9 -5
  57. package/dist/nodeinfo/client.test.js +4 -4
  58. package/dist/nodeinfo/handler.test.js +22 -21
  59. package/dist/nodeinfo/types.test.js +4 -4
  60. package/dist/otel/exporter.test.js +4 -4
  61. package/dist/{owner-C1ZyG4NL.js → owner-D5J299vd.js} +1 -1
  62. package/dist/{proof-wclcUq0C.js → proof-BBLHhWMC.js} +2 -2
  63. package/dist/{proof-CgK60TcQ.cjs → proof-BVl5IgbN.cjs} +3 -3
  64. package/dist/{proof-DnRq8s8f.js → proof-CiCp_mCG.js} +2 -2
  65. package/dist/{send-DNJyYRVU.js → send-2b0Fn9cn.js} +2 -2
  66. package/dist/sig/accept.test.d.ts +3 -0
  67. package/dist/sig/accept.test.js +451 -0
  68. package/dist/sig/http.test.js +452 -27
  69. package/dist/sig/key.test.js +7 -7
  70. package/dist/sig/ld.test.js +6 -6
  71. package/dist/sig/mod.cjs +6 -2
  72. package/dist/sig/mod.d.cts +3 -3
  73. package/dist/sig/mod.d.ts +3 -3
  74. package/dist/sig/mod.js +3 -3
  75. package/dist/sig/owner.test.js +8 -8
  76. package/dist/sig/proof.test.js +8 -8
  77. package/dist/testing/mod.js +1 -1
  78. package/dist/utils/docloader.test.js +10 -9
  79. package/dist/utils/kv-cache.test.js +1 -1
  80. package/dist/utils/mod.cjs +2 -2
  81. package/dist/utils/mod.d.cts +2 -2
  82. package/dist/utils/mod.d.ts +2 -2
  83. package/dist/utils/mod.js +2 -2
  84. package/package.json +6 -7
  85. package/dist/middleware-BDr0P6dx.cjs +0 -12
  86. package/dist/middleware-BZ8WpBo6.js +0 -26
  87. /package/dist/{assert_not_equals-C80BG-_5.js → assert_not_equals-f3m3epl3.js} +0 -0
  88. /package/dist/{assert_throws-BNXdRGWP.js → assert_throws-rjdMBf31.js} +0 -0
  89. /package/dist/{collection-CcnIw1qY.js → collection-CSzG2j1P.js} +0 -0
  90. /package/dist/{context-pa9aIrwp.js → context-Aqenou7c.js} +0 -0
  91. /package/dist/{keycache-C7k8s1Bk.js → keycache-CpGWAUbj.js} +0 -0
  92. /package/dist/{keys-ZbcByPg9.js → keys-BFve7QQv.js} +0 -0
  93. /package/dist/{kv-cache-El7We5sy.js → kv-cache-Bw2F2ABq.js} +0 -0
  94. /package/dist/{negotiation-5NPJL6zp.js → negotiation-BlAuS_nr.js} +0 -0
  95. /package/dist/{retry-D4GJ670a.js → retry-mqLf4b-R.js} +0 -0
  96. /package/dist/{std__assert-DWivtrGR.js → std__assert-X-_kMxKM.js} +0 -0
@@ -3,14 +3,14 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
 
5
5
  import { getDefaultActivityTransformers } from "./transformers-C3FLHUd6.js";
6
- import { deno_default, doubleKnock, exportJwk, importJwk, validateCryptoKey, verifyRequest, verifyRequestDetailed } from "./http-VpqmUjje.js";
7
- import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-DnRq8s8f.js";
6
+ import { deno_default, doubleKnock, exportJwk, formatAcceptSignature, importJwk, parseRfc9421SignatureInput, validateCryptoKey, verifyRequest, verifyRequestDetailed } from "./http-DePHjWKP.js";
7
+ import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-CiCp_mCG.js";
8
8
  import { getNodeInfo, nodeInfoToJson } from "./types-C93Ob9cU.js";
9
- import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-BSATpUtX.js";
9
+ import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-DizRqYX4.js";
10
10
  import { getLogger, withContext } from "@logtape/logtape";
11
11
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
12
12
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
13
- import { cloneDeep } from "es-toolkit";
13
+ import { cloneDeep, uniq } from "es-toolkit";
14
14
  import { Router } from "uri-template-router";
15
15
  import { parseTemplate } from "url-template";
16
16
  import { encodeHex } from "byte-encodings/hex";
@@ -339,7 +339,7 @@ var FederationBuilderImpl = class {
339
339
  this.collectionTypeIds = {};
340
340
  }
341
341
  async build(options) {
342
- const { FederationImpl: FederationImpl$1 } = await import("./middleware-DpdPMZII.js");
342
+ const { FederationImpl: FederationImpl$1 } = await import("./middleware-4fo4pEtA.js");
343
343
  const f = new FederationImpl$1(options);
344
344
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
345
345
  f.router = this.router.clone();
@@ -1281,7 +1281,7 @@ async function handleInbox(request, options) {
1281
1281
  * @returns A promise that resolves to an HTTP response.
1282
1282
  */
1283
1283
  async function handleInboxInternal(request, parameters, span) {
1284
- const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider } = parameters;
1284
+ const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, tracerProvider } = parameters;
1285
1285
  const logger$1 = getLogger([
1286
1286
  "fedify",
1287
1287
  "federation",
@@ -1433,6 +1433,7 @@ async function handleInboxInternal(request, parameters, span) {
1433
1433
  }
1434
1434
  }
1435
1435
  let httpSigKey = null;
1436
+ let pendingNonceLabel;
1436
1437
  if (activity == null) {
1437
1438
  if (!skipSignatureVerification) {
1438
1439
  const verification = await verifyRequestDetailed(request, {
@@ -1453,10 +1454,7 @@ async function handleInboxInternal(request, parameters, span) {
1453
1454
  code: SpanStatusCode.ERROR,
1454
1455
  message: `Failed to verify the request's HTTP Signatures.`
1455
1456
  });
1456
- if (unverifiedActivityHandler == null) return new Response("Failed to verify the request signature.", {
1457
- status: 401,
1458
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1459
- });
1457
+ if (unverifiedActivityHandler == null) return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1460
1458
  try {
1461
1459
  activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
1462
1460
  } catch (error) {
@@ -1510,17 +1508,12 @@ async function handleInboxInternal(request, parameters, span) {
1510
1508
  recipient
1511
1509
  });
1512
1510
  }
1513
- return new Response("Failed to verify the request signature.", {
1514
- status: 401,
1515
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1516
- });
1511
+ return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1517
1512
  }
1518
1513
  if (response instanceof Response) return response;
1519
- return new Response("Failed to verify the request signature.", {
1520
- status: 401,
1521
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1522
- });
1514
+ return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1523
1515
  } else {
1516
+ if (inboxChallengePolicy?.enabled && inboxChallengePolicy.requestNonce) pendingNonceLabel = verification.signatureLabel;
1524
1517
  logger$1.debug("HTTP Signatures are verified.", { recipient });
1525
1518
  activityVerified = true;
1526
1519
  }
@@ -1553,6 +1546,13 @@ async function handleInboxInternal(request, parameters, span) {
1553
1546
  headers: { "Content-Type": "text/plain; charset=utf-8" }
1554
1547
  });
1555
1548
  }
1549
+ if (pendingNonceLabel != null) {
1550
+ const nonceValid = await verifySignatureNonce(request, kv, kvPrefixes.acceptSignatureNonce, pendingNonceLabel);
1551
+ if (!nonceValid) {
1552
+ logger$1.error("Signature nonce verification failed (missing, expired, or replayed).", { recipient });
1553
+ return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1554
+ }
1555
+ }
1556
1556
  const routeResult = await routeActivity({
1557
1557
  context: ctx,
1558
1558
  json,
@@ -2017,6 +2017,79 @@ async function respondWithObjectIfAcceptable(object, request, options) {
2017
2017
  response.headers.set("Vary", "Accept");
2018
2018
  return response;
2019
2019
  }
2020
+ function generateNonce() {
2021
+ const bytes = new Uint8Array(16);
2022
+ crypto.getRandomValues(bytes);
2023
+ return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2024
+ }
2025
+ async function verifySignatureNonce(request, kv, noncePrefix, verifiedLabel) {
2026
+ const signatureInput = request.headers.get("Signature-Input");
2027
+ if (signatureInput == null) return false;
2028
+ const parsed = parseRfc9421SignatureInput(signatureInput);
2029
+ if (verifiedLabel == null) return false;
2030
+ const sig = parsed[verifiedLabel];
2031
+ if (sig == null) return false;
2032
+ const nonce = sig.nonce;
2033
+ if (nonce == null) return false;
2034
+ const key = [...noncePrefix, nonce];
2035
+ if (kv.cas != null) return await kv.cas(key, true, void 0);
2036
+ const stored = await kv.get(key);
2037
+ if (stored != null) {
2038
+ await kv.delete(key);
2039
+ return true;
2040
+ }
2041
+ return false;
2042
+ }
2043
+ const getFailedSignatureResponse = async (policy, kv, kvPrefixes) => {
2044
+ const headers = await getFailedSignatureHeaders(policy, kv, kvPrefixes);
2045
+ return new Response("Failed to verify the request signature.", {
2046
+ status: 401,
2047
+ headers
2048
+ });
2049
+ };
2050
+ const getFailedSignatureHeaders = async (policy, kv, kvPrefixes) => ({
2051
+ "Content-Type": "text/plain; charset=utf-8",
2052
+ ...policy?.enabled && {
2053
+ "Accept-Signature": await buildAcceptSignatureHeader(policy, kv, kvPrefixes.acceptSignatureNonce),
2054
+ "Cache-Control": "no-store",
2055
+ "Vary": "Accept, Signature"
2056
+ }
2057
+ });
2058
+ async function buildAcceptSignatureHeader(policy, kv, noncePrefix) {
2059
+ const parameters = { created: true };
2060
+ if (policy.requestNonce) {
2061
+ const nonce = generateNonce();
2062
+ const key = [...noncePrefix, nonce];
2063
+ await setKey(kv, key, policy);
2064
+ parameters.nonce = nonce;
2065
+ }
2066
+ const baseComponents = policy.components ?? DEF_COMPONENTS;
2067
+ const components = uniq(MIN_COMPONENTS.concat(baseComponents)).filter((c) => c !== "@status").map((v) => ({
2068
+ value: v,
2069
+ params: {}
2070
+ }));
2071
+ return formatAcceptSignature([{
2072
+ label: "sig1",
2073
+ components,
2074
+ parameters
2075
+ }]);
2076
+ }
2077
+ async function setKey(kv, key, policy) {
2078
+ const seconds = policy.nonceTtlSeconds ?? 300;
2079
+ const ttl = Temporal.Duration.from({ seconds });
2080
+ await kv.set(key, true, { ttl });
2081
+ }
2082
+ const DEF_COMPONENTS = [
2083
+ "@method",
2084
+ "@target-uri",
2085
+ "@authority",
2086
+ "content-digest"
2087
+ ];
2088
+ const MIN_COMPONENTS = [
2089
+ "@method",
2090
+ "@target-uri",
2091
+ "@authority"
2092
+ ];
2020
2093
 
2021
2094
  //#endregion
2022
2095
  //#region src/nodeinfo/handler.ts
@@ -2442,6 +2515,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2442
2515
  activityTransformers;
2443
2516
  _tracerProvider;
2444
2517
  firstKnock;
2518
+ inboxChallengePolicy;
2445
2519
  constructor(options) {
2446
2520
  super();
2447
2521
  this.kv = options.kv;
@@ -2450,6 +2524,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2450
2524
  remoteDocument: ["_fedify", "remoteDocument"],
2451
2525
  publicKey: ["_fedify", "publicKey"],
2452
2526
  httpMessageSignaturesSpec: ["_fedify", "httpMessageSignaturesSpec"],
2527
+ acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"],
2453
2528
  ...options.kvPrefixes ?? {}
2454
2529
  };
2455
2530
  if (options.queue == null) {
@@ -2519,6 +2594,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2519
2594
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
2520
2595
  this.signatureTimeWindow = options.signatureTimeWindow ?? { hours: 1 };
2521
2596
  this.skipSignatureVerification = options.skipSignatureVerification ?? false;
2597
+ this.inboxChallengePolicy = options.inboxChallengePolicy;
2522
2598
  this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
2523
2599
  this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
2524
2600
  this.activityTransformers = options.activityTransformers ?? getDefaultActivityTransformers();
@@ -3261,6 +3337,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3261
3337
  onNotFound,
3262
3338
  signatureTimeWindow: this.signatureTimeWindow,
3263
3339
  skipSignatureVerification: this.skipSignatureVerification,
3340
+ inboxChallengePolicy: this.inboxChallengePolicy,
3264
3341
  tracerProvider: this.tracerProvider,
3265
3342
  idempotencyStrategy: this.idempotencyStrategy
3266
3343
  });
@@ -3,10 +3,10 @@
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
4
 
5
5
  import "./transformers-C3FLHUd6.js";
6
- import "./http-VpqmUjje.js";
7
- import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "./middleware-Cx0tTbX1.js";
8
- import "./proof-DnRq8s8f.js";
6
+ import "./http-DePHjWKP.js";
7
+ import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "./middleware--uATyG9i.js";
8
+ import "./proof-CiCp_mCG.js";
9
9
  import "./types-C93Ob9cU.js";
10
- import "./kv-cache-BSATpUtX.js";
10
+ import "./kv-cache-DizRqYX4.js";
11
11
 
12
12
  export { FederationImpl };
@@ -4,10 +4,10 @@
4
4
 
5
5
  const require_chunk = require('./chunk-CGaQZ11T.cjs');
6
6
  const require_transformers = require('./transformers-3g8GZwkZ.cjs');
7
- const require_http = require('./http-iDlaLy8a.cjs');
8
- const require_proof = require('./proof-CgK60TcQ.cjs');
7
+ const require_http = require('./http-CaXARmaJ.cjs');
8
+ const require_proof = require('./proof-BVl5IgbN.cjs');
9
9
  const require_types = require('./types-Cd_hszr_.cjs');
10
- const require_kv_cache = require('./kv-cache-551Om14-.cjs');
10
+ const require_kv_cache = require('./kv-cache-CYTDBChd.cjs');
11
11
  const __logtape_logtape = require_chunk.__toESM(require("@logtape/logtape"));
12
12
  const __fedify_vocab = require_chunk.__toESM(require("@fedify/vocab"));
13
13
  const __opentelemetry_api = require_chunk.__toESM(require("@opentelemetry/api"));
@@ -340,7 +340,7 @@ var FederationBuilderImpl = class {
340
340
  this.collectionTypeIds = {};
341
341
  }
342
342
  async build(options) {
343
- const { FederationImpl: FederationImpl$1 } = await Promise.resolve().then(() => require("./middleware-BDr0P6dx.cjs"));
343
+ const { FederationImpl: FederationImpl$1 } = await Promise.resolve().then(() => require("./middleware-DNY45l5T.cjs"));
344
344
  const f = new FederationImpl$1(options);
345
345
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
346
346
  f.router = this.router.clone();
@@ -1282,7 +1282,7 @@ async function handleInbox(request, options) {
1282
1282
  * @returns A promise that resolves to an HTTP response.
1283
1283
  */
1284
1284
  async function handleInboxInternal(request, parameters, span) {
1285
- const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider } = parameters;
1285
+ const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, tracerProvider } = parameters;
1286
1286
  const logger$1 = (0, __logtape_logtape.getLogger)([
1287
1287
  "fedify",
1288
1288
  "federation",
@@ -1434,6 +1434,7 @@ async function handleInboxInternal(request, parameters, span) {
1434
1434
  }
1435
1435
  }
1436
1436
  let httpSigKey = null;
1437
+ let pendingNonceLabel;
1437
1438
  if (activity == null) {
1438
1439
  if (!skipSignatureVerification) {
1439
1440
  const verification = await require_http.verifyRequestDetailed(request, {
@@ -1454,10 +1455,7 @@ async function handleInboxInternal(request, parameters, span) {
1454
1455
  code: __opentelemetry_api.SpanStatusCode.ERROR,
1455
1456
  message: `Failed to verify the request's HTTP Signatures.`
1456
1457
  });
1457
- if (unverifiedActivityHandler == null) return new Response("Failed to verify the request signature.", {
1458
- status: 401,
1459
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1460
- });
1458
+ if (unverifiedActivityHandler == null) return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1461
1459
  try {
1462
1460
  activity = await __fedify_vocab.Activity.fromJsonLd(jsonWithoutSig, ctx);
1463
1461
  } catch (error) {
@@ -1511,17 +1509,12 @@ async function handleInboxInternal(request, parameters, span) {
1511
1509
  recipient
1512
1510
  });
1513
1511
  }
1514
- return new Response("Failed to verify the request signature.", {
1515
- status: 401,
1516
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1517
- });
1512
+ return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1518
1513
  }
1519
1514
  if (response instanceof Response) return response;
1520
- return new Response("Failed to verify the request signature.", {
1521
- status: 401,
1522
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1523
- });
1515
+ return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1524
1516
  } else {
1517
+ if (inboxChallengePolicy?.enabled && inboxChallengePolicy.requestNonce) pendingNonceLabel = verification.signatureLabel;
1525
1518
  logger$1.debug("HTTP Signatures are verified.", { recipient });
1526
1519
  activityVerified = true;
1527
1520
  }
@@ -1554,6 +1547,13 @@ async function handleInboxInternal(request, parameters, span) {
1554
1547
  headers: { "Content-Type": "text/plain; charset=utf-8" }
1555
1548
  });
1556
1549
  }
1550
+ if (pendingNonceLabel != null) {
1551
+ const nonceValid = await verifySignatureNonce(request, kv, kvPrefixes.acceptSignatureNonce, pendingNonceLabel);
1552
+ if (!nonceValid) {
1553
+ logger$1.error("Signature nonce verification failed (missing, expired, or replayed).", { recipient });
1554
+ return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
1555
+ }
1556
+ }
1557
1557
  const routeResult = await routeActivity({
1558
1558
  context: ctx,
1559
1559
  json,
@@ -2018,6 +2018,79 @@ async function respondWithObjectIfAcceptable(object, request, options) {
2018
2018
  response.headers.set("Vary", "Accept");
2019
2019
  return response;
2020
2020
  }
2021
+ function generateNonce() {
2022
+ const bytes = new Uint8Array(16);
2023
+ crypto.getRandomValues(bytes);
2024
+ return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2025
+ }
2026
+ async function verifySignatureNonce(request, kv, noncePrefix, verifiedLabel) {
2027
+ const signatureInput = request.headers.get("Signature-Input");
2028
+ if (signatureInput == null) return false;
2029
+ const parsed = require_http.parseRfc9421SignatureInput(signatureInput);
2030
+ if (verifiedLabel == null) return false;
2031
+ const sig = parsed[verifiedLabel];
2032
+ if (sig == null) return false;
2033
+ const nonce = sig.nonce;
2034
+ if (nonce == null) return false;
2035
+ const key = [...noncePrefix, nonce];
2036
+ if (kv.cas != null) return await kv.cas(key, true, void 0);
2037
+ const stored = await kv.get(key);
2038
+ if (stored != null) {
2039
+ await kv.delete(key);
2040
+ return true;
2041
+ }
2042
+ return false;
2043
+ }
2044
+ const getFailedSignatureResponse = async (policy, kv, kvPrefixes) => {
2045
+ const headers = await getFailedSignatureHeaders(policy, kv, kvPrefixes);
2046
+ return new Response("Failed to verify the request signature.", {
2047
+ status: 401,
2048
+ headers
2049
+ });
2050
+ };
2051
+ const getFailedSignatureHeaders = async (policy, kv, kvPrefixes) => ({
2052
+ "Content-Type": "text/plain; charset=utf-8",
2053
+ ...policy?.enabled && {
2054
+ "Accept-Signature": await buildAcceptSignatureHeader(policy, kv, kvPrefixes.acceptSignatureNonce),
2055
+ "Cache-Control": "no-store",
2056
+ "Vary": "Accept, Signature"
2057
+ }
2058
+ });
2059
+ async function buildAcceptSignatureHeader(policy, kv, noncePrefix) {
2060
+ const parameters = { created: true };
2061
+ if (policy.requestNonce) {
2062
+ const nonce = generateNonce();
2063
+ const key = [...noncePrefix, nonce];
2064
+ await setKey(kv, key, policy);
2065
+ parameters.nonce = nonce;
2066
+ }
2067
+ const baseComponents = policy.components ?? DEF_COMPONENTS;
2068
+ const components = (0, es_toolkit.uniq)(MIN_COMPONENTS.concat(baseComponents)).filter((c) => c !== "@status").map((v) => ({
2069
+ value: v,
2070
+ params: {}
2071
+ }));
2072
+ return require_http.formatAcceptSignature([{
2073
+ label: "sig1",
2074
+ components,
2075
+ parameters
2076
+ }]);
2077
+ }
2078
+ async function setKey(kv, key, policy) {
2079
+ const seconds = policy.nonceTtlSeconds ?? 300;
2080
+ const ttl = Temporal.Duration.from({ seconds });
2081
+ await kv.set(key, true, { ttl });
2082
+ }
2083
+ const DEF_COMPONENTS = [
2084
+ "@method",
2085
+ "@target-uri",
2086
+ "@authority",
2087
+ "content-digest"
2088
+ ];
2089
+ const MIN_COMPONENTS = [
2090
+ "@method",
2091
+ "@target-uri",
2092
+ "@authority"
2093
+ ];
2021
2094
 
2022
2095
  //#endregion
2023
2096
  //#region src/nodeinfo/handler.ts
@@ -2443,6 +2516,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2443
2516
  activityTransformers;
2444
2517
  _tracerProvider;
2445
2518
  firstKnock;
2519
+ inboxChallengePolicy;
2446
2520
  constructor(options) {
2447
2521
  super();
2448
2522
  this.kv = options.kv;
@@ -2451,6 +2525,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2451
2525
  remoteDocument: ["_fedify", "remoteDocument"],
2452
2526
  publicKey: ["_fedify", "publicKey"],
2453
2527
  httpMessageSignaturesSpec: ["_fedify", "httpMessageSignaturesSpec"],
2528
+ acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"],
2454
2529
  ...options.kvPrefixes ?? {}
2455
2530
  };
2456
2531
  if (options.queue == null) {
@@ -2520,6 +2595,7 @@ var FederationImpl = class extends FederationBuilderImpl {
2520
2595
  this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
2521
2596
  this.signatureTimeWindow = options.signatureTimeWindow ?? { hours: 1 };
2522
2597
  this.skipSignatureVerification = options.skipSignatureVerification ?? false;
2598
+ this.inboxChallengePolicy = options.inboxChallengePolicy;
2523
2599
  this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
2524
2600
  this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
2525
2601
  this.activityTransformers = options.activityTransformers ?? require_transformers.getDefaultActivityTransformers();
@@ -3262,6 +3338,7 @@ var FederationImpl = class extends FederationBuilderImpl {
3262
3338
  onNotFound,
3263
3339
  signatureTimeWindow: this.signatureTimeWindow,
3264
3340
  skipSignatureVerification: this.skipSignatureVerification,
3341
+ inboxChallengePolicy: this.inboxChallengePolicy,
3265
3342
  tracerProvider: this.tracerProvider,
3266
3343
  idempotencyStrategy: this.idempotencyStrategy
3267
3344
  });
@@ -0,0 +1,27 @@
1
+
2
+ import { Temporal } from "@js-temporal/polyfill";
3
+ import { URLPattern } from "urlpattern-polyfill";
4
+ globalThis.addEventListener = () => {};
5
+
6
+ import "./deno-OR506Yti.js";
7
+ import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "./middleware-DzICTgdC.js";
8
+ import "./client-CoCIaTNO.js";
9
+ import "./router-D9eI0s4b.js";
10
+ import "./types-CPz01LGH.js";
11
+ import "./accept-D7sAxyNa.js";
12
+ import "./key-Cx3Tx_In.js";
13
+ import "./http-BUCxbGks.js";
14
+ import "./ld-CLMJw_iX.js";
15
+ import "./owner-D5J299vd.js";
16
+ import "./proof-BBLHhWMC.js";
17
+ import "./docloader-BG_pP2fW.js";
18
+ import "./kv-cache-Bw2F2ABq.js";
19
+ import "./inbox-D_LU1opv.js";
20
+ import "./builder-B24i8eYp.js";
21
+ import "./collection-CSzG2j1P.js";
22
+ import "./keycache-CpGWAUbj.js";
23
+ import "./negotiation-BlAuS_nr.js";
24
+ import "./retry-mqLf4b-R.js";
25
+ import "./send-2b0Fn9cn.js";
26
+
27
+ export { FederationImpl };
@@ -0,0 +1,12 @@
1
+
2
+ const { Temporal } = require("@js-temporal/polyfill");
3
+ const { URLPattern } = require("urlpattern-polyfill");
4
+
5
+ require('./transformers-3g8GZwkZ.cjs');
6
+ require('./http-CaXARmaJ.cjs');
7
+ const require_middleware = require('./middleware-9YDezkYJ.cjs');
8
+ require('./proof-BVl5IgbN.cjs');
9
+ require('./types-Cd_hszr_.cjs');
10
+ require('./kv-cache-CYTDBChd.cjs');
11
+
12
+ exports.FederationImpl = require_middleware.FederationImpl;