@fedify/fedify 2.1.0-dev.565 → 2.1.0-dev.599
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/accept-D7sAxyNa.js +143 -0
- package/dist/{assert_rejects-Ce45JcFg.js → assert_rejects-0h7I2Esa.js} +1 -1
- package/dist/{builder-Deoi2N2z.js → builder-rlJT9XsH.js} +3 -3
- package/dist/compat/mod.d.cts +3 -3
- package/dist/compat/mod.d.ts +3 -3
- package/dist/compat/transformers.test.js +17 -16
- package/dist/{context-DL0cPpPV.d.cts → context-BcqA-0BL.d.cts} +52 -2
- package/dist/{context--RwChtri.d.ts → context-DyJjQQ_H.d.ts} +52 -2
- package/dist/{deno-CEdy89j9.js → deno-BYv1FXyT.js} +1 -2
- package/dist/{docloader-CL1QPJzN.js → docloader-D3dGL8MN.js} +2 -2
- package/dist/federation/builder.test.js +7 -7
- package/dist/federation/collection.test.js +5 -5
- package/dist/federation/handler.test.js +806 -26
- package/dist/federation/idempotency.test.js +22 -21
- package/dist/federation/inbox.test.js +3 -3
- package/dist/federation/keycache.test.js +1 -1
- package/dist/federation/kv.test.js +4 -4
- package/dist/federation/middleware.test.js +22 -21
- 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/mq.test.js +4 -4
- package/dist/federation/negotiation.test.js +5 -5
- package/dist/federation/retry.test.js +2 -2
- package/dist/federation/router.test.js +4 -4
- package/dist/federation/send.test.js +11 -10
- package/dist/federation/webfinger.test.js +22 -21
- package/dist/{http-DsqqmkXi.d.cts → http-BudnHZE2.d.cts} +229 -1
- package/dist/{http-iDlaLy8a.cjs → http-Cjdbgipx.cjs} +307 -50
- package/dist/{http-BbfOqHGG.d.ts → http-Dax_FIBo.d.ts} +229 -1
- package/dist/{http-Dm9n1mRe.js → http-SDJbghtm.js} +144 -49
- package/dist/{http-VpqmUjje.js → http-WrV4DdQ1.js} +278 -51
- package/dist/{inbox-CMtnW0RE.js → inbox-gPJ0RaKj.js} +1 -1
- package/dist/{key-B0yADkL8.js → key-BSOrewQw.js} +1 -1
- package/dist/{kv-cache-BSATpUtX.js → kv-cache-D0a-g8yG.js} +1 -1
- package/dist/{kv-cache-551Om14-.cjs → kv-cache-sn8V-LU_.cjs} +1 -1
- package/dist/{ld-BBmbv1nb.js → ld-8UNDFIO0.js} +3 -3
- package/dist/middleware-74Kx7iWO.cjs +12 -0
- package/dist/{middleware-DpdPMZII.js → middleware-Brge9sYu.js} +4 -4
- package/dist/{middleware-Cldp2YSv.js → middleware-CWItYNUL.js} +113 -34
- package/dist/{middleware-Cx0tTbX1.js → middleware-C_89nqvv.js} +95 -18
- package/dist/{middleware-D11GYoP-.cjs → middleware-PGDxr3nC.cjs} +94 -17
- package/dist/middleware-nfE7By0g.js +27 -0
- package/dist/{mod-DE8MYisy.d.cts → mod-B7QkWzrL.d.cts} +1 -1
- package/dist/{mod-DKG0ovjR.d.cts → mod-Bx9jcLB8.d.cts} +1 -1
- package/dist/{mod-CFBU2OT3.d.cts → mod-Coe7KEgX.d.cts} +1 -1
- package/dist/{mod-BugwI0JN.d.ts → mod-Cs2dYEwI.d.ts} +1 -1
- package/dist/{mod-DcfFNgYf.d.ts → mod-D6MdymW7.d.ts} +1 -1
- package/dist/{mod-CvxylbuV.d.ts → mod-D6dOd--H.d.ts} +1 -1
- package/dist/{mod-Z7lIaCfo.d.ts → mod-SMHOMNpZ.d.ts} +1 -1
- package/dist/{mod-Dp0kK0hO.d.cts → mod-em2Il1eD.d.cts} +1 -1
- package/dist/mod.cjs +12 -4
- package/dist/mod.d.cts +8 -8
- package/dist/mod.d.ts +8 -8
- package/dist/mod.js +9 -5
- package/dist/nodeinfo/client.test.js +4 -4
- package/dist/nodeinfo/handler.test.js +22 -21
- package/dist/nodeinfo/types.test.js +4 -4
- package/dist/otel/exporter.test.js +4 -4
- package/dist/{owner-C1ZyG4NL.js → owner-D1i3Gz1q.js} +1 -1
- package/dist/{proof-wclcUq0C.js → proof-Blm7rPHe.js} +2 -2
- package/dist/{proof-CgK60TcQ.cjs → proof-DFqGzNZi.cjs} +3 -3
- package/dist/{proof-DnRq8s8f.js → proof-DgU0YpXY.js} +2 -2
- package/dist/{send-DNJyYRVU.js → send-Ban_thmx.js} +2 -2
- package/dist/sig/accept.test.d.ts +3 -0
- package/dist/sig/accept.test.js +451 -0
- package/dist/sig/http.test.js +452 -27
- package/dist/sig/key.test.js +7 -7
- package/dist/sig/ld.test.js +6 -6
- package/dist/sig/mod.cjs +6 -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 +8 -8
- package/dist/sig/proof.test.js +8 -8
- package/dist/testing/mod.js +1 -1
- package/dist/utils/docloader.test.js +10 -9
- package/dist/utils/kv-cache.test.js +1 -1
- 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 +6 -7
- package/dist/middleware-BDr0P6dx.cjs +0 -12
- package/dist/middleware-BZ8WpBo6.js +0 -26
- /package/dist/{assert_not_equals-C80BG-_5.js → assert_not_equals-f3m3epl3.js} +0 -0
- /package/dist/{assert_throws-BNXdRGWP.js → assert_throws-rjdMBf31.js} +0 -0
- /package/dist/{collection-CcnIw1qY.js → collection-CSzG2j1P.js} +0 -0
- /package/dist/{context-pa9aIrwp.js → context-Aqenou7c.js} +0 -0
- /package/dist/{keycache-C7k8s1Bk.js → keycache-CpGWAUbj.js} +0 -0
- /package/dist/{keys-ZbcByPg9.js → keys-BFve7QQv.js} +0 -0
- /package/dist/{kv-cache-El7We5sy.js → kv-cache-Bw2F2ABq.js} +0 -0
- /package/dist/{negotiation-5NPJL6zp.js → negotiation-BlAuS_nr.js} +0 -0
- /package/dist/{retry-D4GJ670a.js → retry-mqLf4b-R.js} +0 -0
- /package/dist/{std__assert-DWivtrGR.js → std__assert-X-_kMxKM.js} +0 -0
|
@@ -3,25 +3,27 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
globalThis.addEventListener = () => {};
|
|
5
5
|
|
|
6
|
-
import { deno_default } from "./deno-
|
|
6
|
+
import { deno_default } from "./deno-BYv1FXyT.js";
|
|
7
7
|
import { getNodeInfo } from "./client-CoCIaTNO.js";
|
|
8
8
|
import { RouterError } from "./router-D9eI0s4b.js";
|
|
9
9
|
import { nodeInfoToJson } from "./types-CPz01LGH.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
10
|
+
import { formatAcceptSignature } from "./accept-D7sAxyNa.js";
|
|
11
|
+
import { exportJwk, importJwk, validateCryptoKey } from "./key-BSOrewQw.js";
|
|
12
|
+
import { parseRfc9421SignatureInput, verifyRequest, verifyRequestDetailed } from "./http-SDJbghtm.js";
|
|
13
|
+
import { detachSignature, hasSignature, signJsonLd, verifyJsonLd } from "./ld-8UNDFIO0.js";
|
|
14
|
+
import { doesActorOwnKey, getKeyOwner } from "./owner-D1i3Gz1q.js";
|
|
15
|
+
import { signObject, verifyObject } from "./proof-Blm7rPHe.js";
|
|
16
|
+
import { getAuthenticatedDocumentLoader } from "./docloader-D3dGL8MN.js";
|
|
17
|
+
import { kvCache } from "./kv-cache-Bw2F2ABq.js";
|
|
18
|
+
import { routeActivity } from "./inbox-gPJ0RaKj.js";
|
|
19
|
+
import { FederationBuilderImpl } from "./builder-rlJT9XsH.js";
|
|
20
|
+
import { buildCollectionSynchronizationHeader } from "./collection-CSzG2j1P.js";
|
|
21
|
+
import { KvKeyCache } from "./keycache-CpGWAUbj.js";
|
|
22
|
+
import { acceptsJsonLd } from "./negotiation-BlAuS_nr.js";
|
|
23
|
+
import { createExponentialBackoffPolicy } from "./retry-mqLf4b-R.js";
|
|
24
|
+
import { SendActivityError, extractInboxes, sendActivity } from "./send-Ban_thmx.js";
|
|
24
25
|
import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
26
|
+
import { uniq } from "es-toolkit";
|
|
25
27
|
import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
|
|
26
28
|
import { lookupWebFinger } from "@fedify/webfinger";
|
|
27
29
|
import { getLogger, withContext } from "@logtape/logtape";
|
|
@@ -410,7 +412,7 @@ async function handleInbox(request, options) {
|
|
|
410
412
|
* @returns A promise that resolves to an HTTP response.
|
|
411
413
|
*/
|
|
412
414
|
async function handleInboxInternal(request, parameters, span) {
|
|
413
|
-
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider } = parameters;
|
|
415
|
+
const { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, unverifiedActivityHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, inboxChallengePolicy, tracerProvider } = parameters;
|
|
414
416
|
const logger$2 = getLogger([
|
|
415
417
|
"fedify",
|
|
416
418
|
"federation",
|
|
@@ -562,6 +564,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
562
564
|
}
|
|
563
565
|
}
|
|
564
566
|
let httpSigKey = null;
|
|
567
|
+
let pendingNonceLabel;
|
|
565
568
|
if (activity == null) {
|
|
566
569
|
if (!skipSignatureVerification) {
|
|
567
570
|
const verification = await verifyRequestDetailed(request, {
|
|
@@ -582,10 +585,7 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
582
585
|
code: SpanStatusCode.ERROR,
|
|
583
586
|
message: `Failed to verify the request's HTTP Signatures.`
|
|
584
587
|
});
|
|
585
|
-
if (unverifiedActivityHandler == null) return
|
|
586
|
-
status: 401,
|
|
587
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
588
|
-
});
|
|
588
|
+
if (unverifiedActivityHandler == null) return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
589
589
|
try {
|
|
590
590
|
activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
|
|
591
591
|
} catch (error) {
|
|
@@ -639,17 +639,12 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
639
639
|
recipient
|
|
640
640
|
});
|
|
641
641
|
}
|
|
642
|
-
return
|
|
643
|
-
status: 401,
|
|
644
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
645
|
-
});
|
|
642
|
+
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
646
643
|
}
|
|
647
644
|
if (response instanceof Response) return response;
|
|
648
|
-
return
|
|
649
|
-
status: 401,
|
|
650
|
-
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
651
|
-
});
|
|
645
|
+
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
652
646
|
} else {
|
|
647
|
+
if (inboxChallengePolicy?.enabled && inboxChallengePolicy.requestNonce) pendingNonceLabel = verification.signatureLabel;
|
|
653
648
|
logger$2.debug("HTTP Signatures are verified.", { recipient });
|
|
654
649
|
activityVerified = true;
|
|
655
650
|
}
|
|
@@ -682,6 +677,13 @@ async function handleInboxInternal(request, parameters, span) {
|
|
|
682
677
|
headers: { "Content-Type": "text/plain; charset=utf-8" }
|
|
683
678
|
});
|
|
684
679
|
}
|
|
680
|
+
if (pendingNonceLabel != null) {
|
|
681
|
+
const nonceValid = await verifySignatureNonce(request, kv, kvPrefixes.acceptSignatureNonce, pendingNonceLabel);
|
|
682
|
+
if (!nonceValid) {
|
|
683
|
+
logger$2.error("Signature nonce verification failed (missing, expired, or replayed).", { recipient });
|
|
684
|
+
return await getFailedSignatureResponse(inboxChallengePolicy, kv, kvPrefixes);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
685
687
|
const routeResult = await routeActivity({
|
|
686
688
|
context: ctx,
|
|
687
689
|
json,
|
|
@@ -1146,6 +1148,79 @@ async function respondWithObjectIfAcceptable(object, request, options) {
|
|
|
1146
1148
|
response.headers.set("Vary", "Accept");
|
|
1147
1149
|
return response;
|
|
1148
1150
|
}
|
|
1151
|
+
function generateNonce() {
|
|
1152
|
+
const bytes = new Uint8Array(16);
|
|
1153
|
+
crypto.getRandomValues(bytes);
|
|
1154
|
+
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1155
|
+
}
|
|
1156
|
+
async function verifySignatureNonce(request, kv, noncePrefix, verifiedLabel) {
|
|
1157
|
+
const signatureInput = request.headers.get("Signature-Input");
|
|
1158
|
+
if (signatureInput == null) return false;
|
|
1159
|
+
const parsed = parseRfc9421SignatureInput(signatureInput);
|
|
1160
|
+
if (verifiedLabel == null) return false;
|
|
1161
|
+
const sig = parsed[verifiedLabel];
|
|
1162
|
+
if (sig == null) return false;
|
|
1163
|
+
const nonce = sig.nonce;
|
|
1164
|
+
if (nonce == null) return false;
|
|
1165
|
+
const key = [...noncePrefix, nonce];
|
|
1166
|
+
if (kv.cas != null) return await kv.cas(key, true, void 0);
|
|
1167
|
+
const stored = await kv.get(key);
|
|
1168
|
+
if (stored != null) {
|
|
1169
|
+
await kv.delete(key);
|
|
1170
|
+
return true;
|
|
1171
|
+
}
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
const getFailedSignatureResponse = async (policy, kv, kvPrefixes) => {
|
|
1175
|
+
const headers = await getFailedSignatureHeaders(policy, kv, kvPrefixes);
|
|
1176
|
+
return new Response("Failed to verify the request signature.", {
|
|
1177
|
+
status: 401,
|
|
1178
|
+
headers
|
|
1179
|
+
});
|
|
1180
|
+
};
|
|
1181
|
+
const getFailedSignatureHeaders = async (policy, kv, kvPrefixes) => ({
|
|
1182
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
1183
|
+
...policy?.enabled && {
|
|
1184
|
+
"Accept-Signature": await buildAcceptSignatureHeader(policy, kv, kvPrefixes.acceptSignatureNonce),
|
|
1185
|
+
"Cache-Control": "no-store",
|
|
1186
|
+
"Vary": "Accept, Signature"
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
async function buildAcceptSignatureHeader(policy, kv, noncePrefix) {
|
|
1190
|
+
const parameters = { created: true };
|
|
1191
|
+
if (policy.requestNonce) {
|
|
1192
|
+
const nonce = generateNonce();
|
|
1193
|
+
const key = [...noncePrefix, nonce];
|
|
1194
|
+
await setKey(kv, key, policy);
|
|
1195
|
+
parameters.nonce = nonce;
|
|
1196
|
+
}
|
|
1197
|
+
const baseComponents = policy.components ?? DEF_COMPONENTS;
|
|
1198
|
+
const components = uniq(MIN_COMPONENTS.concat(baseComponents)).filter((c) => c !== "@status").map((v) => ({
|
|
1199
|
+
value: v,
|
|
1200
|
+
params: {}
|
|
1201
|
+
}));
|
|
1202
|
+
return formatAcceptSignature([{
|
|
1203
|
+
label: "sig1",
|
|
1204
|
+
components,
|
|
1205
|
+
parameters
|
|
1206
|
+
}]);
|
|
1207
|
+
}
|
|
1208
|
+
async function setKey(kv, key, policy) {
|
|
1209
|
+
const seconds = policy.nonceTtlSeconds ?? 300;
|
|
1210
|
+
const ttl = Temporal.Duration.from({ seconds });
|
|
1211
|
+
await kv.set(key, true, { ttl });
|
|
1212
|
+
}
|
|
1213
|
+
const DEF_COMPONENTS = [
|
|
1214
|
+
"@method",
|
|
1215
|
+
"@target-uri",
|
|
1216
|
+
"@authority",
|
|
1217
|
+
"content-digest"
|
|
1218
|
+
];
|
|
1219
|
+
const MIN_COMPONENTS = [
|
|
1220
|
+
"@method",
|
|
1221
|
+
"@target-uri",
|
|
1222
|
+
"@authority"
|
|
1223
|
+
];
|
|
1149
1224
|
|
|
1150
1225
|
//#endregion
|
|
1151
1226
|
//#region src/federation/webfinger.ts
|
|
@@ -1315,6 +1390,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1315
1390
|
activityTransformers;
|
|
1316
1391
|
_tracerProvider;
|
|
1317
1392
|
firstKnock;
|
|
1393
|
+
inboxChallengePolicy;
|
|
1318
1394
|
constructor(options) {
|
|
1319
1395
|
super();
|
|
1320
1396
|
this.kv = options.kv;
|
|
@@ -1323,6 +1399,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1323
1399
|
remoteDocument: ["_fedify", "remoteDocument"],
|
|
1324
1400
|
publicKey: ["_fedify", "publicKey"],
|
|
1325
1401
|
httpMessageSignaturesSpec: ["_fedify", "httpMessageSignaturesSpec"],
|
|
1402
|
+
acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"],
|
|
1326
1403
|
...options.kvPrefixes ?? {}
|
|
1327
1404
|
};
|
|
1328
1405
|
if (options.queue == null) {
|
|
@@ -1392,6 +1469,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1392
1469
|
this.permanentFailureStatusCodes = options.permanentFailureStatusCodes ?? [404, 410];
|
|
1393
1470
|
this.signatureTimeWindow = options.signatureTimeWindow ?? { hours: 1 };
|
|
1394
1471
|
this.skipSignatureVerification = options.skipSignatureVerification ?? false;
|
|
1472
|
+
this.inboxChallengePolicy = options.inboxChallengePolicy;
|
|
1395
1473
|
this.outboxRetryPolicy = options.outboxRetryPolicy ?? createExponentialBackoffPolicy();
|
|
1396
1474
|
this.inboxRetryPolicy = options.inboxRetryPolicy ?? createExponentialBackoffPolicy();
|
|
1397
1475
|
this.activityTransformers = options.activityTransformers ?? getDefaultActivityTransformers();
|
|
@@ -1638,11 +1716,11 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1638
1716
|
});
|
|
1639
1717
|
throw error;
|
|
1640
1718
|
}
|
|
1641
|
-
const delay = this.outboxRetryPolicy({
|
|
1719
|
+
const delay$1 = this.outboxRetryPolicy({
|
|
1642
1720
|
elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
|
|
1643
1721
|
attempts: message.attempt
|
|
1644
1722
|
});
|
|
1645
|
-
if (delay != null) {
|
|
1723
|
+
if (delay$1 != null) {
|
|
1646
1724
|
logger$2.error("Failed to send activity {activityId} to {inbox} (attempt #{attempt}); retry...:\n{error}", {
|
|
1647
1725
|
...logData,
|
|
1648
1726
|
error
|
|
@@ -1650,7 +1728,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1650
1728
|
await this.outboxQueue?.enqueue({
|
|
1651
1729
|
...message,
|
|
1652
1730
|
attempt: message.attempt + 1
|
|
1653
|
-
}, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
|
|
1731
|
+
}, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
|
|
1654
1732
|
} else logger$2.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
|
|
1655
1733
|
...logData,
|
|
1656
1734
|
error
|
|
@@ -1737,11 +1815,11 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1737
1815
|
span$1.end();
|
|
1738
1816
|
throw error;
|
|
1739
1817
|
}
|
|
1740
|
-
const delay = this.inboxRetryPolicy({
|
|
1818
|
+
const delay$1 = this.inboxRetryPolicy({
|
|
1741
1819
|
elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
|
|
1742
1820
|
attempts: message.attempt
|
|
1743
1821
|
});
|
|
1744
|
-
if (delay != null) {
|
|
1822
|
+
if (delay$1 != null) {
|
|
1745
1823
|
logger$2.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
|
|
1746
1824
|
error,
|
|
1747
1825
|
attempt: message.attempt,
|
|
@@ -1752,7 +1830,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
1752
1830
|
await this.inboxQueue?.enqueue({
|
|
1753
1831
|
...message,
|
|
1754
1832
|
attempt: message.attempt + 1
|
|
1755
|
-
}, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
|
|
1833
|
+
}, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
|
|
1756
1834
|
} else logger$2.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
|
|
1757
1835
|
error,
|
|
1758
1836
|
activityId: activity.id?.href,
|
|
@@ -2134,6 +2212,7 @@ var FederationImpl = class extends FederationBuilderImpl {
|
|
|
2134
2212
|
onNotFound,
|
|
2135
2213
|
signatureTimeWindow: this.signatureTimeWindow,
|
|
2136
2214
|
skipSignatureVerification: this.skipSignatureVerification,
|
|
2215
|
+
inboxChallengePolicy: this.inboxChallengePolicy,
|
|
2137
2216
|
tracerProvider: this.tracerProvider,
|
|
2138
2217
|
idempotencyStrategy: this.idempotencyStrategy
|
|
2139
2218
|
});
|
|
@@ -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-
|
|
7
|
-
import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-
|
|
6
|
+
import { deno_default, doubleKnock, exportJwk, formatAcceptSignature, importJwk, parseRfc9421SignatureInput, validateCryptoKey, verifyRequest, verifyRequestDetailed } from "./http-WrV4DdQ1.js";
|
|
7
|
+
import { detachSignature, doesActorOwnKey, getKeyOwner, hasSignature, signJsonLd, signObject, verifyJsonLd, verifyObject } from "./proof-DgU0YpXY.js";
|
|
8
8
|
import { getNodeInfo, nodeInfoToJson } from "./types-C93Ob9cU.js";
|
|
9
|
-
import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-
|
|
9
|
+
import { getAuthenticatedDocumentLoader, kvCache } from "./kv-cache-D0a-g8yG.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-
|
|
342
|
+
const { FederationImpl: FederationImpl$1 } = await import("./middleware-Brge9sYu.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
|
|
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
|
|
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
|
|
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
|
});
|
|
@@ -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-
|
|
8
|
-
const require_proof = require('./proof-
|
|
7
|
+
const require_http = require('./http-Cjdbgipx.cjs');
|
|
8
|
+
const require_proof = require('./proof-DFqGzNZi.cjs');
|
|
9
9
|
const require_types = require('./types-Cd_hszr_.cjs');
|
|
10
|
-
const require_kv_cache = require('./kv-cache-
|
|
10
|
+
const require_kv_cache = require('./kv-cache-sn8V-LU_.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-
|
|
343
|
+
const { FederationImpl: FederationImpl$1 } = await Promise.resolve().then(() => require("./middleware-74Kx7iWO.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
|
|
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
|
|
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
|
|
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
|
});
|