@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.
- 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-B24i8eYp.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-OR506Yti.js} +1 -2
- package/dist/{docloader-CL1QPJzN.js → docloader-BG_pP2fW.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-Dm9n1mRe.js → http-BUCxbGks.js} +144 -49
- package/dist/{http-DsqqmkXi.d.cts → http-BudnHZE2.d.cts} +229 -1
- package/dist/{http-iDlaLy8a.cjs → http-CaXARmaJ.cjs} +307 -50
- package/dist/{http-BbfOqHGG.d.ts → http-Dax_FIBo.d.ts} +229 -1
- package/dist/{http-VpqmUjje.js → http-DePHjWKP.js} +278 -51
- package/dist/{inbox-CMtnW0RE.js → inbox-D_LU1opv.js} +1 -1
- package/dist/{key-B0yADkL8.js → key-Cx3Tx_In.js} +1 -1
- package/dist/{kv-cache-551Om14-.cjs → kv-cache-CYTDBChd.cjs} +1 -1
- package/dist/{kv-cache-BSATpUtX.js → kv-cache-DizRqYX4.js} +1 -1
- package/dist/{ld-BBmbv1nb.js → ld-CLMJw_iX.js} +3 -3
- package/dist/{middleware-Cx0tTbX1.js → middleware--uATyG9i.js} +95 -18
- package/dist/{middleware-DpdPMZII.js → middleware-4fo4pEtA.js} +4 -4
- package/dist/{middleware-D11GYoP-.cjs → middleware-9YDezkYJ.cjs} +94 -17
- package/dist/middleware-C2PqSUaA.js +27 -0
- package/dist/middleware-DNY45l5T.cjs +12 -0
- package/dist/{middleware-Cldp2YSv.js → middleware-DzICTgdC.js} +113 -34
- 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-D5J299vd.js} +1 -1
- package/dist/{proof-wclcUq0C.js → proof-BBLHhWMC.js} +2 -2
- package/dist/{proof-CgK60TcQ.cjs → proof-BVl5IgbN.cjs} +3 -3
- package/dist/{proof-DnRq8s8f.js → proof-CiCp_mCG.js} +2 -2
- package/dist/{send-DNJyYRVU.js → send-2b0Fn9cn.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,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-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-
|
|
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-
|
|
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
|
|
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
|
});
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import { URLPattern } from "urlpattern-polyfill";
|
|
4
4
|
|
|
5
5
|
import "./transformers-C3FLHUd6.js";
|
|
6
|
-
import "./http-
|
|
7
|
-
import { ContextImpl, FederationImpl, InboxContextImpl, KvSpecDeterminer, createFederation } from "./middleware
|
|
8
|
-
import "./proof-
|
|
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-
|
|
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-
|
|
8
|
-
const require_proof = require('./proof-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
});
|
|
@@ -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;
|