@fedify/fedify 2.4.0-dev.2077 → 2.4.0-dev.2093

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 (75) hide show
  1. package/dist/{builder-i3jxVe9m.mjs → builder-Ld4Y9ymR.mjs} +34 -3
  2. package/dist/compat/transformers.test.mjs +1 -1
  3. package/dist/{compound-proof-Cayy-ECj.mjs → compound-proof-CTx_lewQ.mjs} +191 -13
  4. package/dist/{deno-Djkzd-G_.mjs → deno-8t8k_6Yv.mjs} +1 -1
  5. package/dist/{docloader-dA0RswUV.mjs → docloader-DJKcsAiF.mjs} +2 -2
  6. package/dist/federation/builder.test.mjs +1 -1
  7. package/dist/federation/compound-fanout.test.mjs +3 -3
  8. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  9. package/dist/federation/handler.test.mjs +5 -5
  10. package/dist/federation/hashlink-media.test.mjs +2 -2
  11. package/dist/federation/idempotency.test.mjs +2 -2
  12. package/dist/federation/metrics.test.mjs +1 -1
  13. package/dist/federation/middleware.test.mjs +7 -7
  14. package/dist/federation/mod.cjs +1 -1
  15. package/dist/federation/mod.js +1 -1
  16. package/dist/federation/outgoing-proof.test.mjs +254 -5
  17. package/dist/federation/portable-gateway-keys.test.mjs +72 -11
  18. package/dist/federation/portable-inbox.test.mjs +513 -6
  19. package/dist/federation/portable.test.mjs +182 -4
  20. package/dist/federation/send.test.mjs +3 -3
  21. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  22. package/dist/federation/tasks/tasks.test.mjs +2 -2
  23. package/dist/federation/temporal.test.mjs +1 -1
  24. package/dist/federation/webfinger.test.mjs +89 -1
  25. package/dist/{federation-CFd0Jhhv.cjs → federation-aSHO04ob.cjs} +472 -74
  26. package/dist/{http-CCJ97Jql.mjs → http-B2ftY0Ha.mjs} +4 -4
  27. package/dist/{http-Dvy_IwRa.cjs → http-DSaSAN7X.cjs} +1609 -1457
  28. package/dist/{http-aqh3yFdM.js → http-DyT7DP1Y.js} +1518 -1390
  29. package/dist/{key-IxTTC8Yf.mjs → key-Dwm4VswT.mjs} +39 -99
  30. package/dist/{kv-cache-BP69NV0P.cjs → kv-cache-BSJAlP4C.cjs} +1 -1
  31. package/dist/{kv-cache-Bwwbh-xR.mjs → kv-cache-D0yYukqk.mjs} +1 -1
  32. package/dist/{kv-cache-Y6SDRSX6.js → kv-cache-DmroAz3M.js} +1 -1
  33. package/dist/{ld-l0B9nREb.mjs → ld-BYZOoDzZ.mjs} +3 -3
  34. package/dist/{metrics-D1p6hv4z.mjs → metrics-B7YwKTFr.mjs} +1 -1
  35. package/dist/{middleware-BDjCUHMS.mjs → middleware-BNeqKSsL.mjs} +1 -1
  36. package/dist/{middleware-SSfryVzY.js → middleware-Blq0882d.js} +480 -82
  37. package/dist/{middleware-BMgzqBtr.mjs → middleware-E_0IH72m.mjs} +278 -80
  38. package/dist/{mod-HDSvS-ww.d.cts → mod-C8LieBzF.d.cts} +16 -3
  39. package/dist/{mod-C9WSpZWm.d.ts → mod-Coacostr.d.ts} +16 -3
  40. package/dist/mod.cjs +5 -5
  41. package/dist/mod.d.cts +1 -1
  42. package/dist/mod.d.ts +1 -1
  43. package/dist/mod.js +5 -5
  44. package/dist/nodeinfo/handler.test.mjs +1 -1
  45. package/dist/{owner-DGbYmXqU.mjs → owner-Cgx2Dc7I.mjs} +5 -4
  46. package/dist/{owner-jH1z_YfD.js → owner-DAGSC5gX.js} +2 -2
  47. package/dist/{owner-BpOxL5PG.cjs → owner-szIZl4qI.cjs} +2 -2
  48. package/dist/{portable-key-CCwJGSsU.mjs → portable-key-Cn1WJSN1.mjs} +60 -41
  49. package/dist/portable-key-id-Bva5iT3W.mjs +136 -0
  50. package/dist/{proof-CVL7JnZd.mjs → proof-DVXC6nYz.mjs} +59 -18
  51. package/dist/{send-Ejy1jOz9.mjs → send-D-hOvYKy.mjs} +3 -3
  52. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  53. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  54. package/dist/sig/compound-proof-verification.test.mjs +160 -3
  55. package/dist/sig/compound-proof.test.mjs +2 -2
  56. package/dist/sig/http.test.mjs +2 -2
  57. package/dist/sig/key.test.mjs +1 -1
  58. package/dist/sig/ld.test.mjs +2 -2
  59. package/dist/sig/mod.cjs +3 -3
  60. package/dist/sig/mod.d.cts +1 -1
  61. package/dist/sig/mod.d.ts +1 -1
  62. package/dist/sig/mod.js +3 -3
  63. package/dist/sig/owner.test.mjs +1 -1
  64. package/dist/sig/portable-collection.test.mjs +69 -5
  65. package/dist/sig/portable-key.test.mjs +176 -10
  66. package/dist/sig/proof.test.mjs +107 -4
  67. package/dist/{sig-Dpy1B1Ad.js → sig-B5Ell7pO.js} +19 -3
  68. package/dist/{sig-xoqy3ATq.cjs → sig-CXOKN4Br.cjs} +19 -3
  69. package/dist/{temporal-DNJ1cNpr.mjs → temporal-CqpKGEPk.mjs} +1 -1
  70. package/dist/testing/kv.test.mjs +1 -1
  71. package/dist/utils/docloader.test.mjs +2 -2
  72. package/dist/utils/kv-cache.test.mjs +1 -1
  73. package/dist/utils/mod.cjs +1 -1
  74. package/dist/utils/mod.js +1 -1
  75. package/package.json +7 -7
@@ -289,7 +289,11 @@ interface VerifyPortableObjectProofOptions extends VerifyProofOptions {}
289
289
  * @since 2.4.0
290
290
  */
291
291
  type VerifyPortableObjectProofFailureReason = {
292
- /** The document does not have a portable `ap:` or `ap+ef61:` ID. */readonly type: "notPortableObject";
292
+ /**
293
+ * The document has neither a portable `ap:` or `ap+ef61:` ID nor an
294
+ * FEP-ef61 compatible identifier.
295
+ */
296
+ readonly type: "notPortableObject";
293
297
  } | {
294
298
  /**
295
299
  * The document is a portable collection without an Object Integrity
@@ -347,15 +351,24 @@ declare function verifyProof(jsonLd: unknown, proof: DataIntegrityProof, options
347
351
  * collection without a proof is reported separately so a caller can apply a
348
352
  * gateway trust policy. Embedded portable objects are not traversed.
349
353
  *
354
+ * A portable object is a document whose ID is an `ap:` or `ap+ef61:` URI, or
355
+ * an [FEP-ef61] compatible identifier such as
356
+ * `https://gw.example/.well-known/apgateway/did:key:z6Mk…/actor`, which
357
+ * stands for the portable ID `ap+ef61://did:key:z6Mk…/actor`.
358
+ *
350
359
  * Every proof must use a DID URL whose DID matches the portable object's
351
- * authority, and every proof must pass {@link verifyProof}.
360
+ * authority, i.e., the DID of its canonical portable ID, and every proof must
361
+ * pass {@link verifyProof}. The proofs are verified over the document as
362
+ * given; a compatible identifier is never rewritten into a portable ID.
363
+ *
364
+ * [FEP-ef61]: https://w3id.org/fep/ef61
352
365
  *
353
366
  * @param jsonLd The JSON-LD document to verify.
354
367
  * @param options Additional options. See also
355
368
  * {@link VerifyPortableObjectProofOptions}.
356
369
  * @returns The detailed portable proof-policy result.
357
370
  * @throws {TypeError} If the input is not a single JSON-LD object or has a
358
- * malformed portable ID.
371
+ * malformed portable ID or compatible identifier.
359
372
  * @since 2.4.0
360
373
  */
361
374
  declare function verifyPortableObjectProof(jsonLd: unknown, options?: VerifyPortableObjectProofOptions): Promise<VerifyPortableObjectProofResult>;
@@ -289,7 +289,11 @@ interface VerifyPortableObjectProofOptions extends VerifyProofOptions {}
289
289
  * @since 2.4.0
290
290
  */
291
291
  type VerifyPortableObjectProofFailureReason = {
292
- /** The document does not have a portable `ap:` or `ap+ef61:` ID. */readonly type: "notPortableObject";
292
+ /**
293
+ * The document has neither a portable `ap:` or `ap+ef61:` ID nor an
294
+ * FEP-ef61 compatible identifier.
295
+ */
296
+ readonly type: "notPortableObject";
293
297
  } | {
294
298
  /**
295
299
  * The document is a portable collection without an Object Integrity
@@ -347,15 +351,24 @@ declare function verifyProof(jsonLd: unknown, proof: DataIntegrityProof, options
347
351
  * collection without a proof is reported separately so a caller can apply a
348
352
  * gateway trust policy. Embedded portable objects are not traversed.
349
353
  *
354
+ * A portable object is a document whose ID is an `ap:` or `ap+ef61:` URI, or
355
+ * an [FEP-ef61] compatible identifier such as
356
+ * `https://gw.example/.well-known/apgateway/did:key:z6Mk…/actor`, which
357
+ * stands for the portable ID `ap+ef61://did:key:z6Mk…/actor`.
358
+ *
350
359
  * Every proof must use a DID URL whose DID matches the portable object's
351
- * authority, and every proof must pass {@link verifyProof}.
360
+ * authority, i.e., the DID of its canonical portable ID, and every proof must
361
+ * pass {@link verifyProof}. The proofs are verified over the document as
362
+ * given; a compatible identifier is never rewritten into a portable ID.
363
+ *
364
+ * [FEP-ef61]: https://w3id.org/fep/ef61
352
365
  *
353
366
  * @param jsonLd The JSON-LD document to verify.
354
367
  * @param options Additional options. See also
355
368
  * {@link VerifyPortableObjectProofOptions}.
356
369
  * @returns The detailed portable proof-policy result.
357
370
  * @throws {TypeError} If the input is not a single JSON-LD object or has a
358
- * malformed portable ID.
371
+ * malformed portable ID or compatible identifier.
359
372
  * @since 2.4.0
360
373
  */
361
374
  declare function verifyPortableObjectProof(jsonLd: unknown, options?: VerifyPortableObjectProofOptions): Promise<VerifyPortableObjectProofResult>;
package/dist/mod.cjs CHANGED
@@ -3,14 +3,14 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  require("./rolldown-runtime-Dmm3t92W.cjs");
4
4
  const require_transformers = require("./transformers-BalCrSsr.cjs");
5
5
  require("./compat/mod.cjs");
6
- const require_http = require("./http-Dvy_IwRa.cjs");
7
- const require_federation = require("./federation-CFd0Jhhv.cjs");
6
+ const require_http = require("./http-DSaSAN7X.cjs");
7
+ const require_federation = require("./federation-aSHO04ob.cjs");
8
8
  const require_types = require("./types-Be2zbyeG.cjs");
9
- const require_owner = require("./owner-BpOxL5PG.cjs");
10
- const require_kv_cache = require("./kv-cache-BP69NV0P.cjs");
9
+ const require_owner = require("./owner-szIZl4qI.cjs");
10
+ const require_kv_cache = require("./kv-cache-BSJAlP4C.cjs");
11
11
  require("./nodeinfo/mod.cjs");
12
12
  require("./runtime/mod.cjs");
13
- const require_sig = require("./sig-xoqy3ATq.cjs");
13
+ const require_sig = require("./sig-CXOKN4Br.cjs");
14
14
  require("./utils/mod.cjs");
15
15
  //#endregion
16
16
  exports.CircuitBreaker = require_federation.CircuitBreaker;
package/dist/mod.d.cts CHANGED
@@ -6,7 +6,7 @@ import { $ as InProcessMessageQueueOptions, A as ObjectCallbackSetters, At as In
6
6
  import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-BhNbN8Ed.cjs";
7
7
  import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.cjs";
8
8
  import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-JzkaEXsi.cjs";
9
- import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-HDSvS-ww.cjs";
9
+ import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-C8LieBzF.cjs";
10
10
  import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-xGFCHqq_.cjs";
11
11
  export * from "@fedify/vocab-runtime";
12
12
 
package/dist/mod.d.ts CHANGED
@@ -6,7 +6,7 @@ import { $ as InProcessMessageQueueOptions, A as ObjectCallbackSetters, At as In
6
6
  import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreListEntry, t as KvKey } from "./kv-BhNbN8Ed.js";
7
7
  import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.js";
8
8
  import { a as RouterOptions, i as RouterError, n as handleWebFinger, o as RouterRouteResult, r as Router, t as WebFingerHandlerParameters } from "./mod-B2h2KY0q.js";
9
- import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-C9WSpZWm.js";
9
+ import { C as createSignature, D as verifyJsonLd, E as signJsonLd, O as verifySignature, S as attachSignature, T as hasSignatureLike, _ as verifyProof, a as CreateProofOptions, b as VerifyJsonLdOptions, c as VerifyPortableObjectProofFailureReason, d as VerifyProofOptions, f as createProof, g as verifyPortableObjectProof, h as verifyObject, i as verifyPortableObject, l as VerifyPortableObjectProofOptions, m as signObject, n as VerifyPortableObjectOptions, o as SignObjectOptions, p as hasProofLike, r as VerifyPortableObjectResult, s as VerifyObjectOptions, t as VerifyPortableObjectFailureReason, u as VerifyPortableObjectProofResult, v as CreateSignatureOptions, w as detachSignature, x as VerifySignatureOptions, y as SignJsonLdOptions } from "./mod-Coacostr.js";
10
10
  import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-CVZrXSdm.js";
11
11
  export * from "@fedify/vocab-runtime";
12
12
 
package/dist/mod.js CHANGED
@@ -3,15 +3,15 @@ import { URLPattern } from "urlpattern-polyfill";
3
3
  import "./rolldown-runtime-BC-Eo5Mj.js";
4
4
  import { n as autoIdAssigner, r as getDefaultActivityTransformers, t as actorDehydrator } from "./transformers-DAtXl_Kc.js";
5
5
  import "./compat/mod.js";
6
- import { A as verifySignature, D as signJsonLd, F as fetchKeyDetailed, I as generateCryptoKeyPair, L as importJwk, M as exportJwk, P as fetchKey, a as verifyRequestDetailed, b as createSignature, c as hasProofLike, d as verifyObject, dt as fulfillAcceptSignature, f as verifyPortableObjectProof, ft as parseAcceptSignature, h as verifyProof, i as verifyRequest, k as verifyJsonLd, l as signObject, pt as validateAcceptSignature, r as signRequest, s as createProof, ut as formatAcceptSignature, v as attachSignature, w as hasSignatureLike, x as detachSignature } from "./http-aqh3yFdM.js";
7
- import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-SSfryVzY.js";
6
+ import { C as detachSignature, E as hasSignatureLike, I as fetchKey, L as fetchKeyDetailed, M as verifySignature, P as exportJwk, R as generateCryptoKeyPair, S as createSignature, a as verifyRequestDetailed, b as attachSignature, c as createProof, ct as parseAcceptSignature, f as verifyObject, g as verifyProof, i as verifyRequest, j as verifyJsonLd, k as signJsonLd, l as hasProofLike, lt as validateAcceptSignature, ot as formatAcceptSignature, p as verifyPortableObjectProof, r as signRequest, st as fulfillAcceptSignature, u as signObject, z as importJwk } from "./http-DyT7DP1Y.js";
7
+ import { a as respondWithObject, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as SendActivityError, l as CircuitBreaker, n as handleWebFinger, o as respondWithObjectIfAcceptable, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "./middleware-Blq0882d.js";
8
8
  import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-C_cNlNVK.js";
9
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-jH1z_YfD.js";
10
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-Y6SDRSX6.js";
9
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DAGSC5gX.js";
10
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-DmroAz3M.js";
11
11
  import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue, Router, RouterError } from "./federation/mod.js";
12
12
  import "./nodeinfo/mod.js";
13
13
  import "./runtime/mod.js";
14
- import { t as verifyPortableObject } from "./sig-Dpy1B1Ad.js";
14
+ import { t as verifyPortableObject } from "./sig-B5Ell7pO.js";
15
15
  import "./utils/mod.js";
16
16
  export * from "@fedify/vocab-runtime";
17
17
  //#endregion
@@ -5,7 +5,7 @@ import { r as createRequestContext } from "../context-DlZhxdFT.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
- import { S as handleNodeInfoJrd, o as createFederation, x as handleNodeInfo } from "../middleware-BMgzqBtr.mjs";
8
+ import { C as handleNodeInfoJrd, S as handleNodeInfo, o as createFederation } from "../middleware-E_0IH72m.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  //#region src/nodeinfo/handler.test.ts
11
11
  test("handleNodeInfo()", async () => {
@@ -1,9 +1,10 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-Djkzd-G_.mjs";
5
- import { c as verifyKeyOwnership, f as isCompatibleKeyId, h as isSamePublicKey$1, l as getCanonicalPortableId, n as fetchActorDocument, t as exportJwk } from "./key-IxTTC8Yf.mjs";
6
- import { t as fetchPortableGatewayKey } from "./portable-key-CCwJGSsU.mjs";
4
+ import { n as version, t as name } from "./deno-8t8k_6Yv.mjs";
5
+ import { a as isCompatibleKeyId, l as isSamePublicKey$1, s as isPortableId, t as getCanonicalPortableId } from "./portable-key-id-Bva5iT3W.mjs";
6
+ import { c as verifyKeyOwnership, n as fetchActorDocument, t as exportJwk } from "./key-Dwm4VswT.mjs";
7
+ import { t as fetchPortableGatewayKey } from "./portable-key-Cn1WJSN1.mjs";
7
8
  import { getLogger } from "@logtape/logtape";
8
9
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
9
10
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -176,7 +177,7 @@ async function getKeyOwner(keyId, options) {
176
177
  return await verifyKeyOwnership(object.id == null ? object.clone({ id: keyId }) : object, fetchOptions);
177
178
  }
178
179
  if (!isActor(object)) return null;
179
- if (object.id == null || object.id.origin !== documentUrl.origin) {
180
+ if (object.id == null || isPortableId(object.id) || object.id.origin !== documentUrl.origin) {
180
181
  logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as the owner of key {keyId}.", {
181
182
  documentUrl: documentUrl.href,
182
183
  actorId: object.id?.href,
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { B as getCanonicalPortableId, G as isSamePublicKey$1, M as exportJwk, N as fetchActorDocument, U as isCompatibleKeyId, ht as version, mt as name, o as fetchPortableGatewayKey, z as verifyKeyOwnership } from "./http-aqh3yFdM.js";
3
+ import { F as fetchActorDocument, P as exportJwk, V as verifyKeyOwnership, _t as isSamePublicKey$1, ht as isPortableId, mt as isCompatibleKeyId, o as fetchPortableGatewayKey, ut as getCanonicalPortableId, vt as name, yt as version } from "./http-DyT7DP1Y.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -173,7 +173,7 @@ async function getKeyOwner(keyId, options) {
173
173
  return await verifyKeyOwnership(object.id == null ? object.clone({ id: keyId }) : object, fetchOptions);
174
174
  }
175
175
  if (!isActor(object)) return null;
176
- if (object.id == null || object.id.origin !== documentUrl.origin) {
176
+ if (object.id == null || isPortableId(object.id) || object.id.origin !== documentUrl.origin) {
177
177
  logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as the owner of key {keyId}.", {
178
178
  documentUrl: documentUrl.href,
179
179
  actorId: object.id?.href,
@@ -1,5 +1,5 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-Dvy_IwRa.cjs");
2
+ const require_http = require("./http-DSaSAN7X.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
4
  let _fedify_vocab = require("@fedify/vocab");
5
5
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -172,7 +172,7 @@ async function getKeyOwner(keyId, options) {
172
172
  return await require_http.verifyKeyOwnership(object.id == null ? object.clone({ id: keyId }) : object, fetchOptions);
173
173
  }
174
174
  if (!(0, _fedify_vocab.isActor)(object)) return null;
175
- if (object.id == null || object.id.origin !== documentUrl.origin) {
175
+ if (object.id == null || require_http.isPortableId(object.id) || object.id.origin !== documentUrl.origin) {
176
176
  logger.debug("The document served at {documentUrl} claims to be the actor {actorId}, which belongs to another origin; refusing to treat it as the owner of key {keyId}.", {
177
177
  documentUrl: documentUrl.href,
178
178
  actorId: object.id?.href,
@@ -1,8 +1,8 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { h as isSamePublicKey, l as getCanonicalPortableId, p as isPortableActorDocument } from "./key-IxTTC8Yf.mjs";
5
- import { c as verifyPortableObjectProofWithRoot } from "./proof-CVL7JnZd.mjs";
4
+ import { l as isSamePublicKey, o as isPortableActorDocument, t as getCanonicalPortableId } from "./portable-key-id-Bva5iT3W.mjs";
5
+ import { l as verifyPortableObjectProofWithRoot } from "./proof-DVXC6nYz.mjs";
6
6
  import { getLogger } from "@logtape/logtape";
7
7
  import { CryptographicKey, Multikey, Object as Object$1 } from "@fedify/vocab";
8
8
  import { getDocumentLoader, isGatewayUrl } from "@fedify/vocab-runtime";
@@ -19,13 +19,19 @@ const SEC = "https://w3id.org/security#";
19
19
  *
20
20
  * A gateway key is accepted only if:
21
21
  *
22
- * - the document is an actor whose portable ID is the one the key ID is
23
- * the compatible identifier of;
22
+ * - the document is an actor whose portable ID, i.e., its `ap:` ID or
23
+ * the canonical portable ID of its compatible identifier, is the one
24
+ * the key ID is the compatible identifier of;
24
25
  * - the document has a valid [FEP-8b32] Object Integrity Proof made by
25
26
  * the DID of its ID, i.e., it satisfies the [FEP-ef61] proof policy;
26
27
  * - the document embeds a `Multikey` with that ID in its `assertionMethod`,
27
- * whose `controller` is the actor, as [FEP-521a] requires; a key
28
- * embedded in its `publicKey` with the same ID must agree with it; and
28
+ * whose `controller` is the actor, as [FEP-521a] requires, and a key
29
+ * embedded in its `publicKey` with the same ID agrees with it; or, if
30
+ * no `assertionMethod` entry has that ID, the document embeds
31
+ * a `CryptographicKey` with that ID in its `publicKey`, whose `owner` is
32
+ * the actor, as some publishers, e.g., tootik, list their RSA keys only
33
+ * there;
34
+ * - no more than one entry of either property has that ID; and
29
35
  * - the key ID's origin is one of the actor's `gateways`.
30
36
  *
31
37
  * [FEP-8b32]: https://w3id.org/fep/8b32
@@ -74,6 +80,21 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
74
80
  if (!result.verified) return reject("The actor document does not satisfy the FEP-ef61 proof policy: " + result.reason.type);
75
81
  if (root == null) return reject("The actor document could not be expanded.");
76
82
  const refuse = (url) => Promise.reject(/* @__PURE__ */ new Error(`Refusing to fetch ${url}.`));
83
+ const parseEmbeddedPublicKey = async (node) => {
84
+ if (isReference(node)) return null;
85
+ let key;
86
+ try {
87
+ key = await CryptographicKey.fromJsonLd(node, {
88
+ documentLoader: refuse,
89
+ contextLoader: options.contextLoader,
90
+ tracerProvider: options.tracerProvider
91
+ });
92
+ } catch (error) {
93
+ if (!(error instanceof TypeError)) throw error;
94
+ return null;
95
+ }
96
+ return key.publicKey == null ? null : key;
97
+ };
77
98
  let verifiedActor;
78
99
  try {
79
100
  verifiedActor = await Object$1.fromJsonLd(root, {
@@ -87,41 +108,42 @@ async function verifyPortableGatewayKeyDocument(document, actor, keyId, options
87
108
  }
88
109
  if (!isPortableActorDocument(verifiedActor) || getCanonicalPortableId(verifiedActor.id) !== claimedActorId) return reject("The key ID is not a compatible identifier of the actor whose document is signed.");
89
110
  actor = verifiedActor;
90
- const multikeyNode = findEmbeddedNode(root[`${SEC}assertionMethod`], keyId);
91
- if (multikeyNode == null) return reject("The actor document does not embed the key in its assertionMethod.");
92
- let multikey;
93
- try {
94
- multikey = await Multikey.fromJsonLd(multikeyNode, {
95
- documentLoader: refuse,
96
- contextLoader: options.contextLoader,
97
- tracerProvider: options.tracerProvider
98
- });
99
- } catch (error) {
100
- if (!(error instanceof TypeError)) throw error;
101
- return reject("The key in the assertionMethod is malformed.");
102
- }
103
- if (multikey.publicKey == null) return reject("The key in the assertionMethod has no public key.");
104
- if (multikey.controllerId == null || getCanonicalPortableId(multikey.controllerId) !== actorId) return reject("The controller of the key is not the actor.");
105
- const publicKeyNode = findEmbeddedNode(root[`${SEC}publicKey`], keyId);
106
- if (publicKeyNode != null) {
107
- let publicKey;
111
+ const assertionNodes = findNodes(root[`${SEC}assertionMethod`], keyId);
112
+ const publicKeyNodes = findNodes(root[`${SEC}publicKey`], keyId);
113
+ if (assertionNodes.length > 1 || publicKeyNodes.length > 1) return reject("The actor document has more than one key with the ID.");
114
+ let publicKey;
115
+ if (assertionNodes.length > 0) {
116
+ if (isReference(assertionNodes[0])) return reject("The actor document does not embed the key in its assertionMethod.");
117
+ let multikey;
108
118
  try {
109
- publicKey = await CryptographicKey.fromJsonLd(publicKeyNode, {
119
+ multikey = await Multikey.fromJsonLd(assertionNodes[0], {
110
120
  documentLoader: refuse,
111
121
  contextLoader: options.contextLoader,
112
122
  tracerProvider: options.tracerProvider
113
123
  });
114
124
  } catch (error) {
115
125
  if (!(error instanceof TypeError)) throw error;
116
- return reject("The key in the publicKey is malformed.");
126
+ return reject("The key in the assertionMethod is malformed.");
127
+ }
128
+ if (multikey.publicKey == null) return reject("The key in the assertionMethod has no public key.");
129
+ if (multikey.controllerId == null || getCanonicalPortableId(multikey.controllerId) !== actorId) return reject("The controller of the key is not the actor.");
130
+ publicKey = multikey.publicKey;
131
+ if (publicKeyNodes.length > 0 && !isReference(publicKeyNodes[0])) {
132
+ const embedded = await parseEmbeddedPublicKey(publicKeyNodes[0]);
133
+ if (embedded == null || !await isSamePublicKey(embedded.publicKey, publicKey) || embedded.ownerId != null && getCanonicalPortableId(embedded.ownerId) !== actorId) return reject("The key in the publicKey does not agree with the one in the assertionMethod.");
117
134
  }
118
- if (publicKey.publicKey == null || !await isSamePublicKey(publicKey.publicKey, multikey.publicKey) || publicKey.ownerId != null && getCanonicalPortableId(publicKey.ownerId) !== actorId) return reject("The key in the publicKey does not agree with the one in the assertionMethod.");
135
+ } else {
136
+ if (publicKeyNodes.length < 1) return reject("The actor document embeds the key in neither its assertionMethod nor its publicKey.");
137
+ const embedded = await parseEmbeddedPublicKey(publicKeyNodes[0]);
138
+ if (embedded == null) return reject("The key in the publicKey is not embedded or malformed.");
139
+ if (embedded.ownerId == null || getCanonicalPortableId(embedded.ownerId) !== actorId) return reject("The owner of the key is not the actor.");
140
+ publicKey = embedded.publicKey;
119
141
  }
120
142
  if (!actor.gateways.some((gateway) => isGatewayUrl(gateway) && gateway.origin === keyId.origin)) return reject(`The gateway ${keyId.origin} is not listed in the actor's gateways.`);
121
143
  const key = new CryptographicKey({
122
144
  id: keyId,
123
145
  owner: actor.id,
124
- publicKey: multikey.publicKey
146
+ publicKey
125
147
  });
126
148
  return {
127
149
  type: "verified",
@@ -171,24 +193,21 @@ async function fetchPortableGatewayKey(keyId, options = {}) {
171
193
  contextLoader
172
194
  });
173
195
  }
174
- function findEmbeddedNode(values, id) {
175
- if (!Array.isArray(values)) return null;
196
+ function findNodes(values, id) {
197
+ if (!Array.isArray(values)) return [];
198
+ const nodes = [];
176
199
  for (const value of values) {
177
200
  if (value == null || typeof value !== "object" || Array.isArray(value)) continue;
178
201
  const node = value;
179
202
  const nodeId = node["@id"];
180
- if (typeof nodeId !== "string") continue;
181
- let parsed;
182
- try {
183
- parsed = new URL(nodeId);
184
- } catch {
185
- continue;
186
- }
187
- if (parsed.href !== id.href) continue;
188
- if (Object.keys(node).every((key) => key === "@id")) continue;
189
- return node;
203
+ if (typeof nodeId !== "string" || !URL.canParse(nodeId)) continue;
204
+ if (new URL(nodeId).href === id.href) nodes.push(node);
190
205
  }
191
- return null;
206
+ return nodes;
207
+ }
208
+ /** A node with nothing but an `@id` is a reference, not an embedded key. */
209
+ function isReference(node) {
210
+ return Object.keys(node).every((key) => key === "@id");
192
211
  }
193
212
  //#endregion
194
213
  export { verifyPortableGatewayKeyDocument as n, fetchPortableGatewayKey as t };
@@ -0,0 +1,136 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { isActor } from "@fedify/vocab";
5
+ import { canonicalizePortableUri, formatIri, fromCompatibleEf61Id, getFe34Origin, toCompatibleEf61Id } from "@fedify/vocab-runtime";
6
+ import { isCompatibleEf61Iri } from "@fedify/vocab-runtime/internal/portable-dereference";
7
+ //#region src/sig/portable-key-id.ts
8
+ const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
9
+ /**
10
+ * Checks whether the URL is an `ap:` or `ap+ef61:` URI.
11
+ * @internal
12
+ */
13
+ function isPortableUri(url) {
14
+ return url.protocol === "ap:" || url.protocol === "ap+ef61:";
15
+ }
16
+ /**
17
+ * Checks whether an ID identifies an [FEP-ef61] portable object, i.e., it is
18
+ * an `ap:` or `ap+ef61:` URI, or looks like a compatible identifier.
19
+ * A malformed compatible identifier counts too, so that it is rejected as
20
+ * a portable object that no proof can match, rather than trusted by its web
21
+ * origin.
22
+ *
23
+ * [FEP-ef61]: https://w3id.org/fep/ef61
24
+ * @internal
25
+ */
26
+ function isPortableId(id) {
27
+ if (typeof id === "string") {
28
+ if (/^ap(?:\+ef61)?:\/\//i.test(id)) return true;
29
+ } else if (isPortableUri(id)) return true;
30
+ return isCompatibleEf61Iri(id);
31
+ }
32
+ /**
33
+ * Gets the DID, i.e., the FEP-fe34 cryptographic origin, of an `ap:` or
34
+ * `ap+ef61:` URI, a DID URL, or a compatible identifier.
35
+ * @returns The DID, or `null` if the ID is none of them, or is malformed.
36
+ * @internal
37
+ */
38
+ function getPortableDid(id) {
39
+ try {
40
+ const raw = typeof id === "string" ? id : id.href;
41
+ if (/^(?:did:|ap(?:\+ef61)?:\/\/)/i.test(raw)) return getFe34Origin(id);
42
+ const portable = fromCompatibleEf61Id(id);
43
+ return portable == null ? null : getFe34Origin(portable);
44
+ } catch (error) {
45
+ if (error instanceof TypeError) return null;
46
+ throw error;
47
+ }
48
+ }
49
+ /**
50
+ * Checks whether an activity is performed by an FEP-ef61 portable actor,
51
+ * i.e., any of its actors has an `ap:` or `ap+ef61:` ID, or a compatible
52
+ * identifier.
53
+ * @internal
54
+ */
55
+ function hasPortableActor(activity) {
56
+ return activity.actorIds.some((id) => isPortableId(id));
57
+ }
58
+ /**
59
+ * Checks whether a key ID looks like an [FEP-ef61] compatible identifier,
60
+ * i.e., an HTTP(S) URL under a gateway's `/.well-known/apgateway/did:` path.
61
+ * Such a key ID names a key that a gateway holds for a portable actor, so it
62
+ * is resolved only as a gateway key, and never cached in a key cache.
63
+ *
64
+ * [FEP-ef61]: https://w3id.org/fep/ef61
65
+ * @internal
66
+ */
67
+ function isCompatibleKeyId(keyId) {
68
+ return (keyId.protocol === "http:" || keyId.protocol === "https:") && COMPATIBLE_KEY_ID_PATH_PATTERN.test(keyId.pathname);
69
+ }
70
+ /**
71
+ * Gets the canonical portable ID of an actor ID that is either an `ap:` or
72
+ * `ap+ef61:` URI or a compatible identifier.
73
+ * @returns The canonical portable ID, or `null` if the ID is neither, or is
74
+ * malformed.
75
+ * @internal
76
+ */
77
+ function getCanonicalPortableId(id) {
78
+ try {
79
+ if (isPortableUri(id)) return canonicalizePortableUri(formatIri(id));
80
+ const portable = fromCompatibleEf61Id(id);
81
+ if (portable == null) return null;
82
+ return canonicalizePortableUri(formatIri(portable));
83
+ } catch (error) {
84
+ if (error instanceof TypeError) return null;
85
+ throw error;
86
+ }
87
+ }
88
+ /**
89
+ * Builds the base of the key IDs of a gateway's keys for a portable actor:
90
+ * the actor's compatible identifier on the gateway.
91
+ * @param actorId The portable actor's ID, either an `ap:` or `ap+ef61:` URI
92
+ * or a compatible identifier. It must not have a fragment.
93
+ * @param gateway The gateway's origin, e.g., `https://example.com`.
94
+ * @returns The compatible identifier without query and fragment.
95
+ * @throws {TypeError} If the actor ID is not a portable ID, or has
96
+ * a fragment.
97
+ * @internal
98
+ */
99
+ function getGatewayKeyBase(actorId, gateway) {
100
+ let portable;
101
+ if (isPortableUri(actorId)) portable = actorId;
102
+ else try {
103
+ portable = fromCompatibleEf61Id(actorId);
104
+ } catch (error) {
105
+ if (!(error instanceof TypeError)) throw error;
106
+ portable = null;
107
+ }
108
+ if (portable == null) throw new TypeError(`The portable actor ID ${actorId.href} must be an ap: or ap+ef61: URI, or an FEP-ef61 compatible identifier.`);
109
+ if (portable.hash !== "") throw new TypeError(`The portable actor ID ${actorId.href} must not have a fragment.`);
110
+ const base = toCompatibleEf61Id(portable, gateway);
111
+ base.search = "";
112
+ base.hash = "";
113
+ return base;
114
+ }
115
+ /**
116
+ * Tells whether a parsed document is a portable actor, i.e., an actor whose
117
+ * ID is an `ap:` or `ap+ef61:` URI or a compatible identifier. Such an actor
118
+ * is never authenticated by the web origin that served it; at a compatible
119
+ * key ID, it makes the key a gateway key that only
120
+ * {@link verifyPortableGatewayKeyDocument} can vouch for.
121
+ * @internal
122
+ */
123
+ function isPortableActorDocument(object) {
124
+ return isActor(object) && object.id != null && isPortableId(object.id);
125
+ }
126
+ /**
127
+ * Compares two public keys by their key material.
128
+ * @internal
129
+ */
130
+ async function isSamePublicKey(a, b) {
131
+ if (a.algorithm.name !== b.algorithm.name) return false;
132
+ const [x, y] = await Promise.all([crypto.subtle.exportKey("jwk", a), crypto.subtle.exportKey("jwk", b)]);
133
+ return x.kty === y.kty && x.crv === y.crv && x.n === y.n && x.e === y.e && x.x === y.x && x.y === y.y;
134
+ }
135
+ //#endregion
136
+ export { isCompatibleKeyId as a, isPortableUri as c, hasPortableActor as i, isSamePublicKey as l, getGatewayKeyBase as n, isPortableActorDocument as o, getPortableDid as r, isPortableId as s, getCanonicalPortableId as t };