@fedify/fedify 2.1.0 → 2.1.2

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 (207) hide show
  1. package/dist/{accept-D7sAxyNa.js → accept-Dd__NiUL.mjs} +10 -8
  2. package/dist/{assert-MZs1qjMx.js → assert-ddO5KLpe.mjs} +5 -9
  3. package/dist/{assert_equals-DSbWqCm3.js → assert_equals-Ew3jOFa3.mjs} +55 -69
  4. package/dist/{assert_instance_of-DHz7EHNU.js → assert_instance_of-C4Ri6VuN.mjs} +5 -9
  5. package/dist/{assert_not_equals-f3m3epl3.js → assert_not_equals--wG9hV7u.mjs} +6 -13
  6. package/dist/{assert_rejects-0h7I2Esa.js → assert_rejects-B-qJtC9Z.mjs} +6 -11
  7. package/dist/{assert_throws-rjdMBf31.js → assert_throws-4NwKEy2q.mjs} +5 -10
  8. package/dist/{builder-WiHhZvjW.js → builder-DkJDAzes.mjs} +32 -41
  9. package/dist/{chunk-CGaQZ11T.cjs → chunk-DDcVe30Y.cjs} +23 -24
  10. package/dist/{chunk-DJNbSFdH.js → chunk-nlSIicah.js} +8 -8
  11. package/dist/{client-BxMZiQaD.d.ts → client-AtlibPOU.d.ts} +1 -1
  12. package/dist/{client-CoCIaTNO.js → client-DEpOVgY1.mjs} +9 -13
  13. package/dist/{client-C97KOq3x.d.cts → client-z-8dc-e1.d.cts} +1 -1
  14. package/dist/{collection-CSzG2j1P.js → collection-BD6-SZ6O.mjs} +7 -12
  15. package/dist/compat/mod.cjs +5 -8
  16. package/dist/compat/mod.d.cts +78 -6
  17. package/dist/compat/mod.d.ts +78 -6
  18. package/dist/compat/mod.js +4 -8
  19. package/dist/compat/transformers.test.mjs +62 -0
  20. package/dist/{context-DyJjQQ_H.d.ts → context-BOiMZBu5.d.ts} +9 -18
  21. package/dist/{context-BcqA-0BL.d.cts → context-BhZVy7RB.d.cts} +9 -18
  22. package/dist/{context-Aqenou7c.js → context-Juj6bdHC.mjs} +7 -11
  23. package/dist/deno-C5VMwnFV.mjs +8 -0
  24. package/dist/{docloader-bVO2EvL9.js → docloader-X9mcJ9Tz.mjs} +8 -14
  25. package/dist/{esm-nLm00z9V.js → esm-DVILvP5e.mjs} +50 -89
  26. package/dist/federation/builder.test.d.mts +2 -0
  27. package/dist/federation/{builder.test.js → builder.test.mjs} +21 -44
  28. package/dist/federation/collection.test.d.mts +2 -0
  29. package/dist/federation/collection.test.mjs +21 -0
  30. package/dist/federation/handler.test.d.mts +2 -0
  31. package/dist/federation/{handler.test.js → handler.test.mjs} +69 -131
  32. package/dist/federation/idempotency.test.d.mts +2 -0
  33. package/dist/federation/{idempotency.test.js → idempotency.test.mjs} +31 -63
  34. package/dist/federation/inbox.test.d.mts +2 -0
  35. package/dist/federation/{inbox.test.js → inbox.test.mjs} +8 -12
  36. package/dist/federation/keycache.test.d.mts +2 -0
  37. package/dist/federation/{keycache.test.js → keycache.test.mjs} +13 -19
  38. package/dist/federation/kv.test.d.mts +2 -0
  39. package/dist/federation/{kv.test.js → kv.test.mjs} +11 -22
  40. package/dist/federation/middleware.test.d.mts +2 -0
  41. package/dist/federation/{middleware.test.js → middleware.test.mjs} +173 -262
  42. package/dist/federation/mod.cjs +327 -16
  43. package/dist/federation/mod.d.cts +3 -6
  44. package/dist/federation/mod.d.ts +3 -6
  45. package/dist/federation/mod.js +322 -13
  46. package/dist/federation/mq.test.d.mts +2 -0
  47. package/dist/federation/{mq.test.js → mq.test.mjs} +21 -35
  48. package/dist/federation/negotiation.test.d.mts +2 -0
  49. package/dist/federation/{negotiation.test.js → negotiation.test.mjs} +9 -16
  50. package/dist/federation/retry.test.d.mts +2 -0
  51. package/dist/federation/{retry.test.js → retry.test.mjs} +8 -11
  52. package/dist/federation/router.test.d.mts +2 -0
  53. package/dist/federation/{router.test.js → router.test.mjs} +11 -16
  54. package/dist/federation/send.test.d.mts +2 -0
  55. package/dist/federation/{send.test.js → send.test.mjs} +22 -30
  56. package/dist/federation/webfinger.test.d.mts +2 -0
  57. package/dist/federation/{webfinger.test.js → webfinger.test.mjs} +22 -56
  58. package/dist/{http-DhH623ma.js → http-BLZWcpzg.js} +67 -187
  59. package/dist/{http-CKDim8Tw.js → http-BTLPIzFa.mjs} +37 -45
  60. package/dist/{http-BudnHZE2.d.cts → http-CrGuipxe.d.cts} +1 -6
  61. package/dist/{http-gvnJbMS1.cjs → http-CxodXLwi.cjs} +186 -300
  62. package/dist/{http-Dax_FIBo.d.ts → http-aQzN9Ayi.d.ts} +1 -6
  63. package/dist/{inbox-CA9AUEGa.js → inbox-mcbmhjTW.mjs} +18 -26
  64. package/dist/{key-BsSCz8Z_.js → key-1MaItIGc.mjs} +29 -37
  65. package/dist/{keycache-CpGWAUbj.js → keycache-CCSwkQcY.mjs} +5 -10
  66. package/dist/{keys-BFve7QQv.js → keys-BAK-tUlf.mjs} +5 -9
  67. package/dist/{kv-BL4nlICN.d.cts → kv-CbLNp3zQ.d.cts} +1 -1
  68. package/dist/{kv-DXEUEP6z.d.ts → kv-GFYnFoOl.d.ts} +1 -1
  69. package/dist/{kv-cache-Bw2F2ABq.js → kv-cache-B01V7s3h.mjs} +4 -8
  70. package/dist/{kv-cache-DK4GFVWx.cjs → kv-cache-DjC82_4n.cjs} +27 -34
  71. package/dist/{kv-cache-CxoHCR44.js → kv-cache-GIDK1oLs.js} +6 -13
  72. package/dist/{kv-QzKcOQgP.js → kv-tL2TOE9X.mjs} +6 -10
  73. package/dist/{ld-Bo_Rx0Fc.js → ld-94uHZ1eO.mjs} +17 -31
  74. package/dist/{middleware-BkrUA3da.js → middleware-B5Er10wE.js} +336 -383
  75. package/dist/middleware-CDuHbSVE.mjs +5 -0
  76. package/dist/middleware-CTyq5KB0.cjs +4 -0
  77. package/dist/{middleware-CpAnWzjC.cjs → middleware-CqDJSLoG.cjs} +532 -587
  78. package/dist/{middleware-CZ8jOOa3.js → middleware-DMZGXHm3.mjs} +282 -317
  79. package/dist/{mod-Bx9jcLB8.d.cts → mod-B505FZBC.d.cts} +3 -3
  80. package/dist/{mod-em2Il1eD.d.cts → mod-Bp_CzKd4.d.cts} +2 -2
  81. package/dist/{mod-Cs2dYEwI.d.ts → mod-D7PAuO6k.d.ts} +3 -3
  82. package/dist/{mod-D6MdymW7.d.ts → mod-DKOAow7a.d.ts} +2 -2
  83. package/dist/{mod-Coe7KEgX.d.cts → mod-DoJBjjnO.d.cts} +2 -2
  84. package/dist/{mod-D6dOd--H.d.ts → mod-DvxszxXC.d.ts} +2 -2
  85. package/dist/mod.cjs +29 -74
  86. package/dist/mod.d.cts +11 -14
  87. package/dist/mod.d.ts +11 -15
  88. package/dist/mod.js +17 -71
  89. package/dist/{negotiation-BlAuS_nr.js → negotiation-DnsfFF8I.mjs} +7 -11
  90. package/dist/nodeinfo/client.test.d.mts +2 -0
  91. package/dist/nodeinfo/{client.test.js → client.test.mjs} +22 -40
  92. package/dist/nodeinfo/handler.test.d.mts +2 -0
  93. package/dist/nodeinfo/{handler.test.js → handler.test.mjs} +13 -43
  94. package/dist/nodeinfo/mod.cjs +5 -8
  95. package/dist/nodeinfo/mod.d.cts +2 -3
  96. package/dist/nodeinfo/mod.d.ts +2 -3
  97. package/dist/nodeinfo/mod.js +4 -8
  98. package/dist/nodeinfo/types.test.d.mts +2 -0
  99. package/dist/nodeinfo/{types.test.js → types.test.mjs} +9 -16
  100. package/dist/otel/exporter.test.d.mts +2 -0
  101. package/dist/otel/{exporter.test.js → exporter.test.mjs} +124 -178
  102. package/dist/otel/mod.cjs +15 -20
  103. package/dist/otel/mod.d.cts +2 -2
  104. package/dist/otel/mod.d.ts +2 -2
  105. package/dist/otel/mod.js +8 -14
  106. package/dist/{owner-gd0Q9FuU.d.ts → owner-74ARJ5TL.d.ts} +1 -1
  107. package/dist/{owner-Bj_IbwIT.js → owner-B4aIDhg_.mjs} +11 -16
  108. package/dist/{owner-1AbPBOOZ.d.cts → owner-CptqhsOy.d.cts} +1 -1
  109. package/dist/{proof-u6Y358J-.js → proof-DYZWMWOC.mjs} +21 -33
  110. package/dist/{proof-BhFF_JVj.cjs → proof-DqCjiFwb.cjs} +133 -157
  111. package/dist/{proof-D5BQTIcU.js → proof-j-of9m5W.js} +33 -59
  112. package/dist/{retry-mqLf4b-R.js → retry-B_E3V_Dx.mjs} +4 -7
  113. package/dist/{router-D9eI0s4b.js → router-CrMLXoOr.mjs} +4 -8
  114. package/dist/runtime/mod.cjs +11 -13
  115. package/dist/runtime/mod.d.cts +6 -2
  116. package/dist/runtime/mod.d.ts +0 -1
  117. package/dist/runtime/mod.js +4 -7
  118. package/dist/{send-CE8h59oe.js → send-uLjD0uAe.mjs} +8 -13
  119. package/dist/sig/accept.test.d.mts +2 -0
  120. package/dist/sig/{accept.test.js → accept.test.mjs} +35 -70
  121. package/dist/sig/http.test.d.mts +2 -0
  122. package/dist/sig/{http.test.js → http.test.mjs} +209 -280
  123. package/dist/sig/key.test.d.mts +2 -0
  124. package/dist/sig/{key.test.js → key.test.mjs} +11 -18
  125. package/dist/sig/ld.test.d.mts +2 -0
  126. package/dist/sig/{ld.test.js → ld.test.mjs} +22 -35
  127. package/dist/sig/mod.cjs +6 -9
  128. package/dist/sig/mod.d.cts +3 -3
  129. package/dist/sig/mod.d.ts +3 -3
  130. package/dist/sig/mod.js +5 -9
  131. package/dist/sig/owner.test.d.mts +2 -0
  132. package/dist/sig/{owner.test.js → owner.test.mjs} +19 -34
  133. package/dist/sig/proof.test.d.mts +2 -0
  134. package/dist/sig/{proof.test.js → proof.test.mjs} +16 -27
  135. package/dist/{std__assert-X-_kMxKM.js → std__assert-Duiq_YC9.mjs} +12 -24
  136. package/dist/testing/{mod.d.ts → mod.d.mts} +26 -90
  137. package/dist/testing/mod.mjs +6 -0
  138. package/dist/{transformers-3g8GZwkZ.cjs → transformers-NeAONrAq.cjs} +20 -25
  139. package/dist/{transformers-C3FLHUd6.js → transformers-ve6e2xcg.js} +3 -7
  140. package/dist/{types-CPz01LGH.js → types-DCP0WLdt.mjs} +4 -7
  141. package/dist/{types-Cd_hszr_.cjs → types-KC4QAoxe.cjs} +29 -34
  142. package/dist/{types-C93Ob9cU.js → types-hvL8ElAs.js} +8 -13
  143. package/dist/utils/docloader.test.d.mts +2 -0
  144. package/dist/utils/{docloader.test.js → docloader.test.mjs} +14 -25
  145. package/dist/utils/kv-cache.test.d.mts +2 -0
  146. package/dist/utils/{kv-cache.test.js → kv-cache.test.mjs} +25 -40
  147. package/dist/utils/mod.cjs +5 -9
  148. package/dist/utils/mod.d.cts +1 -3
  149. package/dist/utils/mod.d.ts +1 -3
  150. package/dist/utils/mod.js +4 -9
  151. package/dist/vocab/cjs.test.d.mts +2 -0
  152. package/dist/vocab/cjs.test.mjs +14 -0
  153. package/dist/vocab/mod.cjs +10 -12
  154. package/dist/vocab/mod.js +3 -5
  155. package/package.json +7 -7
  156. package/dist/compat/transformers.test.d.ts +0 -3
  157. package/dist/compat/transformers.test.js +0 -88
  158. package/dist/compat-Bb4NuTUO.js +0 -4
  159. package/dist/compat-DmDDELst.cjs +0 -4
  160. package/dist/deno-BRMCYThi.js +0 -121
  161. package/dist/federation/builder.test.d.ts +0 -3
  162. package/dist/federation/collection.test.d.ts +0 -3
  163. package/dist/federation/collection.test.js +0 -32
  164. package/dist/federation/handler.test.d.ts +0 -3
  165. package/dist/federation/idempotency.test.d.ts +0 -3
  166. package/dist/federation/inbox.test.d.ts +0 -3
  167. package/dist/federation/keycache.test.d.ts +0 -3
  168. package/dist/federation/kv.test.d.ts +0 -3
  169. package/dist/federation/middleware.test.d.ts +0 -3
  170. package/dist/federation/mq.test.d.ts +0 -3
  171. package/dist/federation/negotiation.test.d.ts +0 -3
  172. package/dist/federation/retry.test.d.ts +0 -3
  173. package/dist/federation/router.test.d.ts +0 -3
  174. package/dist/federation/send.test.d.ts +0 -3
  175. package/dist/federation/webfinger.test.d.ts +0 -3
  176. package/dist/federation-Bp3HI26G.cjs +0 -350
  177. package/dist/federation-DaMfqRm4.js +0 -332
  178. package/dist/middleware-BtT_mKsB.cjs +0 -12
  179. package/dist/middleware-CUMoHNCA.js +0 -12
  180. package/dist/middleware-CzeVJTA1.js +0 -27
  181. package/dist/mod-B7QkWzrL.d.cts +0 -80
  182. package/dist/mod-Bh8mqlYw.d.cts +0 -9
  183. package/dist/mod-D6HodEq7.d.ts +0 -7
  184. package/dist/mod-SMHOMNpZ.d.ts +0 -82
  185. package/dist/mod-gq_Xfdz8.d.cts +0 -1
  186. package/dist/nodeinfo/client.test.d.ts +0 -3
  187. package/dist/nodeinfo/handler.test.d.ts +0 -3
  188. package/dist/nodeinfo/types.test.d.ts +0 -3
  189. package/dist/nodeinfo-DoESQxq5.js +0 -4
  190. package/dist/nodeinfo-DuMYTpbZ.cjs +0 -4
  191. package/dist/otel/exporter.test.d.ts +0 -3
  192. package/dist/runtime-c2Njxsry.cjs +0 -17
  193. package/dist/runtime-poamPCMb.js +0 -13
  194. package/dist/sig/accept.test.d.ts +0 -3
  195. package/dist/sig/http.test.d.ts +0 -3
  196. package/dist/sig/key.test.d.ts +0 -3
  197. package/dist/sig/ld.test.d.ts +0 -3
  198. package/dist/sig/owner.test.d.ts +0 -3
  199. package/dist/sig/proof.test.d.ts +0 -3
  200. package/dist/sig-BNhspNOf.js +0 -4
  201. package/dist/sig-vX39WyWI.cjs +0 -4
  202. package/dist/testing/mod.js +0 -10
  203. package/dist/utils/docloader.test.d.ts +0 -3
  204. package/dist/utils/kv-cache.test.d.ts +0 -3
  205. package/dist/utils-BQ9KqEK9.cjs +0 -4
  206. package/dist/utils-Dn5OPdSW.js +0 -4
  207. /package/dist/{mod-AGjRfPjT.d.ts → compat/transformers.test.d.mts} +0 -0
@@ -1,37 +1,28 @@
1
-
2
- import { Temporal } from "@js-temporal/polyfill";
3
- import { URLPattern } from "urlpattern-polyfill";
4
- globalThis.addEventListener = () => {};
5
-
6
- import { esm_default } from "../esm-nLm00z9V.js";
7
- import { assertEquals } from "../assert_equals-DSbWqCm3.js";
8
- import { assert } from "../assert-MZs1qjMx.js";
9
- import "../assert_instance_of-DHz7EHNU.js";
10
- import "../deno-BRMCYThi.js";
11
- import "../accept-D7sAxyNa.js";
12
- import { exportJwk } from "../key-BsSCz8Z_.js";
13
- import { createRfc9421SignatureBase, doubleKnock, formatRfc9421Signature, formatRfc9421SignatureParameters, parseRfc9421Signature, parseRfc9421SignatureInput, signRequest, timingSafeEqual, verifyRequest, verifyRequestDetailed } from "../http-CKDim8Tw.js";
14
- import { assertExists, assertStringIncludes } from "../std__assert-X-_kMxKM.js";
15
- import { assertFalse, assertRejects } from "../assert_rejects-0h7I2Esa.js";
16
- import { assertThrows } from "../assert_throws-rjdMBf31.js";
17
- import "../assert_not_equals-f3m3epl3.js";
18
- import { rsaPrivateKey2, rsaPublicKey1, rsaPublicKey2, rsaPublicKey5 } from "../keys-BFve7QQv.js";
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as esm_default } from "../esm-DVILvP5e.mjs";
5
+ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
+ import { a as assertExists, t as assertStringIncludes } from "../std__assert-Duiq_YC9.mjs";
7
+ import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
+ import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
9
+ import { t as assert } from "../assert-ddO5KLpe.mjs";
10
+ import { t as exportJwk } from "../key-1MaItIGc.mjs";
11
+ import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase, u as verifyRequestDetailed } from "../http-BTLPIzFa.mjs";
12
+ import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-BAK-tUlf.mjs";
19
13
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
20
14
  import { FetchError, exportSpki } from "@fedify/vocab-runtime";
21
15
  import { encodeBase64 } from "byte-encodings/base64";
22
-
23
16
  //#region src/sig/http.test.ts
24
17
  test("signRequest() [draft-cavage]", async () => {
25
- const request = new Request("https://example.com/", {
18
+ assertEquals(await verifyRequest(await signRequest(new Request("https://example.com/", {
26
19
  method: "POST",
27
20
  body: "Hello, world!",
28
21
  headers: {
29
22
  "Content-Type": "text/plain; charset=utf-8",
30
23
  Accept: "text/plain"
31
24
  }
32
- });
33
- const signed = await signRequest(request, rsaPrivateKey2, new URL("https://example.com/key2"));
34
- assertEquals(await verifyRequest(signed, {
25
+ }), rsaPrivateKey2, new URL("https://example.com/key2")), {
35
26
  contextLoader: mockDocumentLoader,
36
27
  documentLoader: mockDocumentLoader
37
28
  }), rsaPublicKey2);
@@ -57,8 +48,8 @@ test("verifyRequest() [draft-cavage]", async () => {
57
48
  get(keyId) {
58
49
  return Promise.resolve(cache[keyId.href]);
59
50
  },
60
- set(keyId, key$1) {
61
- cache[keyId.href] = key$1;
51
+ set(keyId, key) {
52
+ cache[keyId.href] = key;
62
53
  return Promise.resolve();
63
54
  }
64
55
  }
@@ -146,7 +137,7 @@ test("verifyRequest() [draft-cavage]", async () => {
146
137
  currentTime: Temporal.Instant.from("2025-01-01T00:00:00.0000Z"),
147
138
  timeWindow: false
148
139
  }), rsaPublicKey1);
149
- const request2 = new Request("https://c27a97f98d5f.ngrok.app/i/inbox", {
140
+ assert(await verifyRequest(new Request("https://c27a97f98d5f.ngrok.app/i/inbox", {
150
141
  method: "POST",
151
142
  body: "{\"@context\":[\"https://www.w3.org/ns/activitystreams\",\"https://w3id.org/security/v1\"],\"actor\":\"https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd\",\"object\":{\"actor\":\"https://c27a97f98d5f.ngrok.app/i\",\"object\":\"https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd\",\"type\":\"Follow\",\"id\":\"https://c27a97f98d5f.ngrok.app/i#follows/https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd\"},\"type\":\"Accept\",\"id\":\"https://oeee.cafe/objects/0fc2608f-5660-4b91-b8c7-63c0c2ac2e20\"}",
152
143
  headers: {
@@ -156,12 +147,10 @@ test("verifyRequest() [draft-cavage]", async () => {
156
147
  Digest: "SHA-256=YZyjeVQW5GwliJowASkteBJhFBTq3eQk/AMqRETc//A=",
157
148
  Signature: "keyId=\"https://oeee.cafe/ap/users/3609fd4e-d51d-4db8-9f04-4189815864dd#main-key\",algorithm=\"hs2019\",created=\"1756126694\",expires=\"1756130294\",headers=\"(request-target) (created) (expires) content-type date digest host\",signature=\"XFb0jl2uMhE7RhbneE9sK9Zls2qZec8iy6+9O8UgDQeBGJThORFLjXKlps4QO1WAf1YSVB/i5aV6yF+h73Lm3ZiuAJDx1h+00iLsxoYuIw1CZvF0V2jELoo3sQ2/ZzqeoO6H5TbK7tKnU+ulFAPTuJgjIvPwYl11OMRouVS34NiaHP9Yx9pU813TLv37thG/hUKanyq8kk0IJWtDWteY/zxDvzoe7VOkBXVBHslMyrNAI/5JGulVQAQp/E61dJAhTHHIyGxkc/7iutWFZuqFXIiPJ9KR2OuKDj/B32hEzlsf5xH/CjqOJPIg1qMK8FzDiALCq6zjiKIBEnW8HQc/hQ==\""
158
149
  }
159
- });
160
- const options2 = {
150
+ }), {
161
151
  ...options,
162
152
  currentTime: Temporal.Instant.from("2025-08-25T12:58:14Z")
163
- };
164
- assert(await verifyRequest(request2, options2) != null);
153
+ }) != null);
165
154
  });
166
155
  test("verifyRequestDetailed() classifies malformed signatures as invalid", async () => {
167
156
  const draftMissingKeyId = await verifyRequestDetailed(new Request("https://example.com/", {
@@ -214,7 +203,7 @@ test("verifyRequestDetailed() classifies malformed signatures as invalid", async
214
203
  test("verifyRequestDetailed() records failure details on span", async () => {
215
204
  const [tracerProvider, exporter] = createTestTracerProvider();
216
205
  const keyId = new URL("https://gone.example/actors/alice#main-key");
217
- const request = await signRequest(new Request("https://example.com/inbox", {
206
+ assertFalse((await verifyRequestDetailed(await signRequest(new Request("https://example.com/inbox", {
218
207
  method: "POST",
219
208
  headers: {
220
209
  "Content-Type": "application/activity+json",
@@ -225,16 +214,14 @@ test("verifyRequestDetailed() records failure details on span", async () => {
225
214
  type: "Create",
226
215
  actor: "https://gone.example/actors/alice"
227
216
  })
228
- }), rsaPrivateKey2, keyId);
229
- const result = await verifyRequestDetailed(request, {
217
+ }), rsaPrivateKey2, keyId), {
230
218
  tracerProvider,
231
219
  contextLoader: mockDocumentLoader,
232
220
  documentLoader(url) {
233
221
  if (url === keyId.href) throw new FetchError(keyId, `HTTP 410: ${keyId.href}`, new Response(null, { status: 410 }));
234
222
  return mockDocumentLoader(url);
235
223
  }
236
- });
237
- assertFalse(result.verified);
224
+ })).verified);
238
225
  const spans = exporter.getSpans("http_signatures.verify");
239
226
  assertEquals(spans.length, 1);
240
227
  const span = spans[0];
@@ -282,9 +269,7 @@ test("signRequest() and verifyRequest() [rfc9421] implementation", async () => {
282
269
  const contentDigest = signed.headers.get("Content-Digest");
283
270
  assertExists(contentDigest);
284
271
  assert(contentDigest.startsWith("sha-256=:"), "Content-Digest should use RFC 9421 format");
285
- const expectedDigest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(requestBody));
286
- const expectedDigestBase64 = encodeBase64(expectedDigest);
287
- assertEquals(contentDigest, `sha-256=:${expectedDigestBase64}:`, "Content-Digest should have correct value");
272
+ assertEquals(contentDigest, `sha-256=:${encodeBase64(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(requestBody)))}:`, "Content-Digest should have correct value");
288
273
  const signature = signed.headers.get("Signature");
289
274
  assertExists(signature);
290
275
  const sigFormat = /^sig1=:([A-Za-z0-9+/]+=*):/;
@@ -305,25 +290,22 @@ test("signRequest() and verifyRequest() [rfc9421] implementation", async () => {
305
290
  assertEquals(parsedSig.sig1.byteLength > 0, true, "Signature value should be a non-empty Uint8Array");
306
291
  const verifyHeaders = new Headers();
307
292
  for (const [name, value] of signed.headers.entries()) if (name !== "Signature" && name !== "Signature-Input") verifyHeaders.set(name, value);
308
- const reconstructedRequest = new Request(request.url, {
293
+ const reconstructedBase = createRfc9421SignatureBase(new Request(request.url, {
309
294
  method: request.method,
310
295
  headers: verifyHeaders
311
- });
312
- const reconstructedBase = createRfc9421SignatureBase(reconstructedRequest, parsedInput.sig1.components, parsedInput.sig1.parameters);
296
+ }), parsedInput.sig1.components, parsedInput.sig1.parameters);
313
297
  const signatureBytes = new Uint8Array(parsedSig.sig1);
314
- const signatureVerifies = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", rsaPublicKey2.publicKey, signatureBytes, new TextEncoder().encode(reconstructedBase));
315
- assert(signatureVerifies, "Manual verification of signature should succeed");
298
+ assert(await crypto.subtle.verify("RSASSA-PKCS1-v1_5", rsaPublicKey2.publicKey, signatureBytes, new TextEncoder().encode(reconstructedBase)), "Manual verification of signature should succeed");
316
299
  });
317
300
  test("createRfc9421SignatureBase()", () => {
318
- const request = new Request("https://example.com/path?query=value", {
301
+ assertEquals(createRfc9421SignatureBase(new Request("https://example.com/path?query=value", {
319
302
  method: "POST",
320
303
  headers: {
321
304
  Host: "example.com",
322
305
  Date: "Tue, 05 Mar 2024 07:49:44 GMT",
323
306
  "Content-Type": "text/plain"
324
307
  }
325
- });
326
- const components = [
308
+ }), [
327
309
  {
328
310
  value: "@method",
329
311
  params: {}
@@ -340,21 +322,17 @@ test("createRfc9421SignatureBase()", () => {
340
322
  value: "date",
341
323
  params: {}
342
324
  }
343
- ];
344
- const created = 1709626184;
345
- const signatureBase = createRfc9421SignatureBase(request, components, formatRfc9421SignatureParameters({
325
+ ], formatRfc9421SignatureParameters({
346
326
  algorithm: "rsa-v1_5-sha256",
347
327
  keyId: new URL("https://example.com/key"),
348
- created
349
- }));
350
- const expected = [
328
+ created: 1709626184
329
+ })), [
351
330
  `"@method": POST`,
352
331
  `"@target-uri": https://example.com/path?query=value`,
353
332
  `"host": example.com`,
354
333
  `"date": Tue, 05 Mar 2024 07:49:44 GMT`,
355
334
  `"@signature-params": ("@method" "@target-uri" "host" "date");alg="rsa-v1_5-sha256";keyid="https://example.com/key";created=1709626184`
356
- ].join("\n");
357
- assertEquals(signatureBase, expected);
335
+ ].join("\n"));
358
336
  });
359
337
  test("formatRfc9421Signature()", () => {
360
338
  const signature = new Uint8Array([
@@ -365,7 +343,7 @@ test("formatRfc9421Signature()", () => {
365
343
  ]);
366
344
  const keyId = new URL("https://example.com/key");
367
345
  const algorithm = "rsa-v1_5-sha256";
368
- const components = [
346
+ const [signatureInput, signatureHeader] = formatRfc9421Signature(signature, [
369
347
  {
370
348
  "value": "@method",
371
349
  params: {}
@@ -378,19 +356,16 @@ test("formatRfc9421Signature()", () => {
378
356
  "value": "host",
379
357
  params: {}
380
358
  }
381
- ];
382
- const created = 1709626184;
383
- const [signatureInput, signatureHeader] = formatRfc9421Signature(signature, components, formatRfc9421SignatureParameters({
359
+ ], formatRfc9421SignatureParameters({
384
360
  algorithm,
385
361
  keyId,
386
- created
362
+ created: 1709626184
387
363
  }));
388
364
  assertEquals(signatureInput, `sig1=("@method" "@target-uri" "host");alg="rsa-v1_5-sha256";keyid="https://example.com/key";created=1709626184`);
389
365
  assertEquals(signatureHeader, `sig1=:AQIDBA==:`);
390
366
  });
391
367
  test("parseRfc9421SignatureInput()", () => {
392
- const signatureInput = `sig1=("@method" "@target-uri" "host" "date");keyid="https://example.com/key";alg="rsa-v1_5-sha256";created=1709626184`;
393
- const parsed = parseRfc9421SignatureInput(signatureInput);
368
+ const parsed = parseRfc9421SignatureInput(`sig1=("@method" "@target-uri" "host" "date");keyid="https://example.com/key";alg="rsa-v1_5-sha256";created=1709626184`);
394
369
  assertEquals(parsed.sig1.keyId, "https://example.com/key");
395
370
  assertEquals(parsed.sig1.alg, "rsa-v1_5-sha256");
396
371
  assertEquals(parsed.sig1.created, 1709626184);
@@ -415,8 +390,7 @@ test("parseRfc9421SignatureInput()", () => {
415
390
  assertEquals(parsed.sig1.parameters, "keyid=\"https://example.com/key\";alg=\"rsa-v1_5-sha256\";created=1709626184");
416
391
  });
417
392
  test("parseRfc9421Signature()", () => {
418
- const signature = `sig1=:AQIDBA==:,sig2=:Zm9vYmFy:`;
419
- const parsed = parseRfc9421Signature(signature);
393
+ const parsed = parseRfc9421Signature(`sig1=:AQIDBA==:,sig2=:Zm9vYmFy:`);
420
394
  assertExists(parsed.sig1);
421
395
  assertExists(parsed.sig2);
422
396
  const sig1Bytes = new Uint8Array(parsed.sig1);
@@ -425,37 +399,33 @@ test("parseRfc9421Signature()", () => {
425
399
  assertEquals(sig1Bytes[1], 2);
426
400
  assertEquals(sig1Bytes[2], 3);
427
401
  assertEquals(sig1Bytes[3], 4);
428
- const sig2Text = new TextDecoder().decode(parsed.sig2);
429
- assertEquals(sig2Text, "foobar");
402
+ assertEquals(new TextDecoder().decode(parsed.sig2), "foobar");
430
403
  });
431
404
  test("verifyRequest() [rfc9421] successful GET verification", async () => {
432
405
  const currentTimestamp = 1709626184;
433
406
  const currentTime = Temporal.Instant.from("2024-03-05T08:09:44Z");
434
- const validRequest = new Request("https://example.com/api/resource", {
407
+ assertEquals(await verifyRequest(await signRequest(new Request("https://example.com/api/resource", {
435
408
  method: "GET",
436
409
  headers: {
437
410
  "Accept": "application/json",
438
411
  "Host": "example.com",
439
412
  "Date": "Tue, 05 Mar 2024 07:49:44 GMT"
440
413
  }
441
- });
442
- const signedRequest = await signRequest(validRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
414
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
443
415
  spec: "rfc9421",
444
416
  currentTime
445
- });
446
- const verifiedKey = await verifyRequest(signedRequest, {
417
+ }), {
447
418
  contextLoader: mockDocumentLoader,
448
419
  documentLoader: mockDocumentLoader,
449
420
  spec: "rfc9421",
450
421
  currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date(currentTimestamp * 1e3)).toISOString()}`)
451
- });
452
- assertEquals(verifiedKey, rsaPublicKey2, "Valid signature should verify to the correct public key");
422
+ }), rsaPublicKey2, "Valid signature should verify to the correct public key");
453
423
  });
454
424
  test("verifyRequest() [rfc9421] manual POST verification", async () => {
455
425
  const currentTimestamp = 1709626184;
456
426
  const currentTime = Temporal.Instant.from("2024-03-05T08:09:44Z");
457
427
  const postBody = "Test content for signature verification";
458
- const postRequest = new Request("https://example.com/api/resource", {
428
+ const signedPostRequest = await signRequest(new Request("https://example.com/api/resource", {
459
429
  method: "POST",
460
430
  body: postBody,
461
431
  headers: {
@@ -464,8 +434,7 @@ test("verifyRequest() [rfc9421] manual POST verification", async () => {
464
434
  "Host": "example.com",
465
435
  "Date": "Tue, 05 Mar 2024 07:49:44 GMT"
466
436
  }
467
- });
468
- const signedPostRequest = await signRequest(postRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
437
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
469
438
  spec: "rfc9421",
470
439
  currentTime
471
440
  });
@@ -487,61 +456,54 @@ test("verifyRequest() [rfc9421] manual POST verification", async () => {
487
456
  assertExists(parsedSignature.sig1, "Should have a valid signature value");
488
457
  assertEquals(parsedInput.sig1.keyId, "https://example.com/key2", "Signature should have the correct key ID");
489
458
  assertEquals(parsedInput.sig1.created, currentTimestamp, "Signature should have the correct timestamp");
490
- const manualRequest = new Request("https://example.com/api/resource", {
459
+ const signatureBase = createRfc9421SignatureBase(new Request("https://example.com/api/resource", {
491
460
  method: "POST",
492
461
  body: postBody,
493
462
  headers: new Headers(signedPostRequest.headers)
494
- });
495
- const signatureBase = createRfc9421SignatureBase(manualRequest, parsedInput.sig1.components, parsedInput.sig1.parameters);
496
- const signatureVerified = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", rsaPublicKey2.publicKey, parsedSignature.sig1.slice(), new TextEncoder().encode(signatureBase));
497
- assert(signatureVerified, "Manual verification of POST signature should succeed");
463
+ }), parsedInput.sig1.components, parsedInput.sig1.parameters);
464
+ assert(await crypto.subtle.verify("RSASSA-PKCS1-v1_5", rsaPublicKey2.publicKey, parsedSignature.sig1.slice(), new TextEncoder().encode(signatureBase)), "Manual verification of POST signature should succeed");
498
465
  });
499
466
  test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
500
467
  const currentTimestamp = 1709626184;
501
468
  const currentTime = Temporal.Instant.from("2024-03-05T08:09:44Z");
502
- const validRequest = new Request("https://example.com/api/resource", {
469
+ const signedRequest = await signRequest(new Request("https://example.com/api/resource", {
503
470
  method: "GET",
504
471
  headers: {
505
472
  "Accept": "application/json",
506
473
  "Host": "example.com",
507
474
  "Date": "Tue, 05 Mar 2024 07:49:44 GMT"
508
475
  }
509
- });
510
- const signedRequest = await signRequest(validRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
476
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
511
477
  spec: "rfc9421",
512
478
  currentTime
513
479
  });
514
480
  const validSignatureInput = signedRequest.headers.get("Signature-Input") || "";
515
481
  const validSignature = signedRequest.headers.get("Signature") || "";
516
- const missingInputHeader = new Request("https://example.com/api/resource", {
482
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
517
483
  method: "GET",
518
484
  headers: new Headers({
519
485
  "Accept": "application/json",
520
486
  "Host": "example.com",
521
487
  "Signature": validSignature
522
488
  })
523
- });
524
- const missingInputResult = await verifyRequest(missingInputHeader, {
489
+ }), {
525
490
  contextLoader: mockDocumentLoader,
526
491
  documentLoader: mockDocumentLoader,
527
492
  spec: "rfc9421"
528
- });
529
- assertEquals(missingInputResult, null, "Should fail verification when Signature-Input header is missing");
530
- const missingSignatureHeader = new Request("https://example.com/api/resource", {
493
+ }), null, "Should fail verification when Signature-Input header is missing");
494
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
531
495
  method: "GET",
532
496
  headers: new Headers({
533
497
  "Accept": "application/json",
534
498
  "Host": "example.com",
535
499
  "Signature-Input": validSignatureInput
536
500
  })
537
- });
538
- const missingSignatureResult = await verifyRequest(missingSignatureHeader, {
501
+ }), {
539
502
  contextLoader: mockDocumentLoader,
540
503
  documentLoader: mockDocumentLoader,
541
504
  spec: "rfc9421"
542
- });
543
- assertEquals(missingSignatureResult, null, "Should fail verification when Signature header is missing");
544
- const tamperedRequest = new Request("https://example.com/api/resource", {
505
+ }), null, "Should fail verification when Signature header is missing");
506
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
545
507
  method: "GET",
546
508
  headers: new Headers({
547
509
  "Accept": "application/json",
@@ -550,14 +512,12 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
550
512
  "Signature-Input": validSignatureInput,
551
513
  "Signature": "sig1=:AAAAAA==:"
552
514
  })
553
- });
554
- const tamperedResult = await verifyRequest(tamperedRequest, {
515
+ }), {
555
516
  contextLoader: mockDocumentLoader,
556
517
  documentLoader: mockDocumentLoader,
557
518
  spec: "rfc9421"
558
- });
559
- assertEquals(tamperedResult, null, "Should fail verification when signature is tampered");
560
- const expiredRequest = new Request("https://example.com/api/resource", {
519
+ }), null, "Should fail verification when signature is tampered");
520
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
561
521
  method: "GET",
562
522
  headers: new Headers({
563
523
  "Accept": "application/json",
@@ -566,16 +526,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
566
526
  "Signature-Input": validSignatureInput,
567
527
  "Signature": validSignature
568
528
  })
569
- });
570
- const expiredResult = await verifyRequest(expiredRequest, {
529
+ }), {
571
530
  contextLoader: mockDocumentLoader,
572
531
  documentLoader: mockDocumentLoader,
573
532
  spec: "rfc9421",
574
533
  currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date((currentTimestamp + 2592e3) * 1e3)).toISOString()}`),
575
534
  timeWindow: { hours: 1 }
576
- });
577
- assertEquals(expiredResult, null, "Should fail verification when signature timestamp is too old");
578
- const futureRequest = new Request("https://example.com/api/resource", {
535
+ }), null, "Should fail verification when signature timestamp is too old");
536
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
579
537
  method: "GET",
580
538
  headers: new Headers({
581
539
  "Accept": "application/json",
@@ -584,16 +542,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
584
542
  "Signature-Input": validSignatureInput,
585
543
  "Signature": validSignature
586
544
  })
587
- });
588
- const futureResult = await verifyRequest(futureRequest, {
545
+ }), {
589
546
  contextLoader: mockDocumentLoader,
590
547
  documentLoader: mockDocumentLoader,
591
548
  spec: "rfc9421",
592
549
  currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date((currentTimestamp - 2592e3) * 1e3)).toISOString()}`),
593
550
  timeWindow: { hours: 1 }
594
- });
595
- assertEquals(futureResult, null, "Should fail verification when signature timestamp is in the future");
596
- const timeCheckRequest = new Request("https://example.com/api/resource", {
551
+ }), null, "Should fail verification when signature timestamp is in the future");
552
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
597
553
  method: "GET",
598
554
  headers: new Headers({
599
555
  "Accept": "application/json",
@@ -602,16 +558,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
602
558
  "Signature-Input": validSignatureInput,
603
559
  "Signature": validSignature
604
560
  })
605
- });
606
- const timeDisabledResult = await verifyRequest(timeCheckRequest, {
561
+ }), {
607
562
  contextLoader: mockDocumentLoader,
608
563
  documentLoader: mockDocumentLoader,
609
564
  spec: "rfc9421",
610
565
  currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date((currentTimestamp + 31536e3) * 1e3)).toISOString()}`),
611
566
  timeWindow: false
612
- });
613
- assertEquals(timeDisabledResult, rsaPublicKey2, "Should verify signature when time checking is disabled");
614
- const postRequest = new Request("https://example.com/api/resource", {
567
+ }), rsaPublicKey2, "Should verify signature when time checking is disabled");
568
+ const freshSignedPostRequest = await signRequest(new Request("https://example.com/api/resource", {
615
569
  method: "POST",
616
570
  body: "Test content for signature verification",
617
571
  headers: {
@@ -620,15 +574,14 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
620
574
  "Host": "example.com",
621
575
  "Date": "Tue, 05 Mar 2024 07:49:44 GMT"
622
576
  }
623
- });
624
- const freshSignedPostRequest = await signRequest(postRequest, rsaPrivateKey2, new URL("https://example.com/key2"), {
577
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
625
578
  spec: "rfc9421",
626
579
  currentTime
627
580
  });
628
581
  const postSignatureInput = freshSignedPostRequest.headers.get("Signature-Input") || "";
629
582
  const postSignature = freshSignedPostRequest.headers.get("Signature") || "";
630
583
  const postContentDigest = freshSignedPostRequest.headers.get("Content-Digest") || "";
631
- const tamperDigestRequest = new Request("https://example.com/api/resource", {
584
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
632
585
  method: "POST",
633
586
  body: "This content won't match the digest",
634
587
  headers: new Headers({
@@ -639,22 +592,19 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
639
592
  "Signature": postSignature,
640
593
  "Content-Digest": postContentDigest
641
594
  })
642
- });
643
- const tamperDigestResult = await verifyRequest(tamperDigestRequest, {
595
+ }), {
644
596
  contextLoader: mockDocumentLoader,
645
597
  documentLoader: mockDocumentLoader,
646
598
  spec: "rfc9421",
647
599
  currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date(currentTimestamp * 1e3)).toISOString()}`)
648
- });
649
- assertEquals(tamperDigestResult, null, "Should fail verification with invalid Content-Digest");
600
+ }), null, "Should fail verification with invalid Content-Digest");
650
601
  const testRequest = new Request("https://example.com/", { headers: new Headers({
651
602
  "Date": "Tue, 05 Mar 2024 07:49:44 GMT",
652
603
  "Host": "example.com",
653
604
  "Signature-Input": `sig1=("@method" "@target-uri" "host" "date");keyid="https://example.com/key";alg="rsa-v1_5-sha256";created=1709626184`,
654
605
  "Signature": `sig1=:YXNkZmprc2RmaGprc2RoZmprc2hkZmtqaHNkZg==:`
655
606
  }) });
656
- const signatureInput = testRequest.headers.get("Signature-Input") || "";
657
- const parsedInput = parseRfc9421SignatureInput(signatureInput);
607
+ const parsedInput = parseRfc9421SignatureInput(testRequest.headers.get("Signature-Input") || "");
658
608
  assertExists(parsedInput.sig1);
659
609
  assertEquals(parsedInput.sig1.keyId, "https://example.com/key");
660
610
  assertEquals(parsedInput.sig1.alg, "rsa-v1_5-sha256");
@@ -677,12 +627,10 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
677
627
  params: {}
678
628
  }
679
629
  ]);
680
- const signature = testRequest.headers.get("Signature") || "";
681
- const parsedSig = parseRfc9421Signature(signature);
630
+ const parsedSig = parseRfc9421Signature(testRequest.headers.get("Signature") || "");
682
631
  assertExists(parsedSig.sig1);
683
632
  assert(new TextDecoder().decode(parsedSig.sig1).length > 0, "Signature base64 should decode to non-empty string");
684
- const complexSignatureInput = "sig1=(\"@method\" \"@target-uri\" \"host\" \"content-type\" \"value with \\\"quotes\\\" and spaces\");keyid=\"https://example.com/key with spaces\";alg=\"rsa-v1_5-sha256\";created=1709626184";
685
- const complexParsedInput = parseRfc9421SignatureInput(complexSignatureInput);
633
+ const complexParsedInput = parseRfc9421SignatureInput("sig1=(\"@method\" \"@target-uri\" \"host\" \"content-type\" \"value with \\\"quotes\\\" and spaces\");keyid=\"https://example.com/key with spaces\";alg=\"rsa-v1_5-sha256\";created=1709626184");
686
634
  assertExists(complexParsedInput.sig1);
687
635
  assertEquals(complexParsedInput.sig1.keyId, "https://example.com/key with spaces");
688
636
  assertEquals(complexParsedInput.sig1.alg, "rsa-v1_5-sha256");
@@ -701,16 +649,13 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
701
649
  assertEquals(multiParsedInput.sig2.alg, "rsa-pss-sha512");
702
650
  const multiParsedSig = parseRfc9421Signature(multiSigRequest.headers.get("Signature") || "");
703
651
  assertEquals(Object.keys(multiParsedSig).length, 2, "Should parse multiple signature values");
704
- const invalidInputFormat = "this is not a valid signature-input format";
705
- const parsedInvalidInput = parseRfc9421SignatureInput(invalidInputFormat);
652
+ const parsedInvalidInput = parseRfc9421SignatureInput("this is not a valid signature-input format");
706
653
  assertEquals(Object.keys(parsedInvalidInput).length, 0, "Should handle invalid Signature-Input format");
707
- const invalidSigFormat = "this is not a valid signature format";
708
- const parsedInvalidSig = parseRfc9421Signature(invalidSigFormat);
654
+ const parsedInvalidSig = parseRfc9421Signature("this is not a valid signature format");
709
655
  assertEquals(Object.keys(parsedInvalidSig).length, 0, "Should handle invalid Signature format");
710
- const invalidBase64Sig = "sig1=:!@#$%%^&*():";
711
- const parsedInvalidBase64 = parseRfc9421Signature(invalidBase64Sig);
656
+ const parsedInvalidBase64 = parseRfc9421Signature("sig1=:!@#$%%^&*():");
712
657
  assertEquals(Object.keys(parsedInvalidBase64).length, 0, "Should handle invalid base64 in signature");
713
- const mixedRequest = new Request("https://example.com/api/resource", {
658
+ assertEquals(await verifyRequest(new Request("https://example.com/api/resource", {
714
659
  method: "GET",
715
660
  headers: new Headers({
716
661
  "Accept": "application/json",
@@ -719,14 +664,12 @@ test("verifyRequest() [rfc9421] error cases and edge cases", async () => {
719
664
  "Signature-Input": `${validSignatureInput},sig2=("@method" "@target-uri" "host" "date");keyid="https://example.com/invalid-key";alg="rsa-v1_5-sha256";created=${currentTimestamp}`,
720
665
  "Signature": `${validSignature},sig2=:AAAAAA==:`
721
666
  })
722
- });
723
- const mixedResult = await verifyRequest(mixedRequest, {
667
+ }), {
724
668
  contextLoader: mockDocumentLoader,
725
669
  documentLoader: mockDocumentLoader,
726
670
  spec: "rfc9421",
727
671
  currentTime: Temporal.Instant.from(`${(/* @__PURE__ */ new Date(currentTimestamp * 1e3)).toISOString()}`)
728
- });
729
- assertEquals(mixedResult, rsaPublicKey2, "Should verify when at least one signature is valid");
672
+ }), rsaPublicKey2, "Should verify when at least one signature is valid");
730
673
  });
731
674
  test("verifyRequest() [rfc9421] test vector from Mastodon", async () => {
732
675
  const signedRequest = new Request("https://www.example.com/activitypub/success", {
@@ -789,14 +732,13 @@ test("doubleKnock() function with successful first attempt", async () => {
789
732
  const logFunction = (req) => {
790
733
  loggedRequest = req;
791
734
  };
792
- const response = await doubleKnock(request, {
735
+ assertEquals((await doubleKnock(request, {
793
736
  keyId: rsaPublicKey2.id,
794
737
  privateKey: rsaPrivateKey2
795
738
  }, {
796
739
  specDeterminer,
797
740
  log: logFunction
798
- });
799
- assertEquals(response.status, 202, "Response status should be 202 Accepted");
741
+ })).status, 202, "Response status should be 202 Accepted");
800
742
  assertEquals(requestCount, 1, "Only one request should have been made");
801
743
  assertEquals(firstRequestSpec, "rfc9421", "First attempt should use RFC 9421");
802
744
  assertEquals(specDeterminer.usedSpec, "rfc9421", "Spec should be remembered");
@@ -837,11 +779,10 @@ test("doubleKnock() function with fallback to draft-cavage", async () => {
837
779
  this.rememberedSpec = spec;
838
780
  }
839
781
  };
840
- const response = await doubleKnock(request, {
782
+ assertEquals((await doubleKnock(request, {
841
783
  keyId: rsaPublicKey2.id,
842
784
  privateKey: rsaPrivateKey2
843
- }, { specDeterminer });
844
- assertEquals(response.status, 202, "Response status should be 202 Accepted");
785
+ }, { specDeterminer })).status, 202, "Response status should be 202 Accepted");
845
786
  assertEquals(requestCount, 2, "Two requests should have been made");
846
787
  assertEquals(firstSpec, "rfc9421", "First attempt should use RFC 9421");
847
788
  assertEquals(secondSpec, "draft-cavage-http-signatures-12", "Second attempt should use draft-cavage");
@@ -863,16 +804,14 @@ test("doubleKnock() function with redirect handling", async () => {
863
804
  responseCodes.push(202);
864
805
  return new Response("", { status: 202 });
865
806
  });
866
- const request = new Request("https://example.com/redirect-endpoint", {
807
+ assertEquals((await doubleKnock(new Request("https://example.com/redirect-endpoint", {
867
808
  method: "POST",
868
809
  body: "Test message that will be redirected",
869
810
  headers: { "Content-Type": "text/plain" }
870
- });
871
- const response = await doubleKnock(request, {
811
+ }), {
872
812
  keyId: rsaPublicKey2.id,
873
813
  privateKey: rsaPrivateKey2
874
- });
875
- assertEquals(response.status, 202, "Final response status should be 202 Accepted");
814
+ })).status, 202, "Final response status should be 202 Accepted");
876
815
  assertEquals(requestedUrls.length, 2, "Two URLs should have been requested");
877
816
  assertEquals(requestedUrls[0], "https://example.com/redirect-endpoint", "First request should be to redirect-endpoint");
878
817
  assertEquals(requestedUrls[1], "https://example.com/final-endpoint", "Second request should be to final-endpoint");
@@ -891,16 +830,14 @@ test("doubleKnock() function with both specs rejected", async () => {
891
830
  else attempts.push("unknown");
892
831
  return new Response("Unauthorized", { status: 401 });
893
832
  });
894
- const request = new Request("https://example.com/inbox-rejects-all", {
833
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-rejects-all", {
895
834
  method: "POST",
896
835
  body: "Test message that will be rejected regardless of signature format",
897
836
  headers: { "Content-Type": "text/plain" }
898
- });
899
- const response = await doubleKnock(request, {
837
+ }), {
900
838
  keyId: rsaPublicKey2.id,
901
839
  privateKey: rsaPrivateKey2
902
- });
903
- assertEquals(response.status, 401, "Final response status should be 401 Unauthorized");
840
+ })).status, 401, "Final response status should be 401 Unauthorized");
904
841
  assertEquals(requestCount, 2, "Two requests should have been made");
905
842
  assertEquals(attempts.length, 2, "Two signature attempts should have been made");
906
843
  assertEquals(attempts[0], "rfc9421", "First attempt should use RFC 9421");
@@ -924,16 +861,14 @@ test("doubleKnock() function with specDeterminer choosing draft-cavage first", a
924
861
  },
925
862
  rememberSpec(_origin, _spec) {}
926
863
  };
927
- const request = new Request("https://example.com/inbox-accepts-any", {
864
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-accepts-any", {
928
865
  method: "POST",
929
866
  body: "Test message with draft-cavage preference",
930
867
  headers: { "Content-Type": "text/plain" }
931
- });
932
- const response = await doubleKnock(request, {
868
+ }), {
933
869
  keyId: rsaPublicKey2.id,
934
870
  privateKey: rsaPrivateKey2
935
- }, { specDeterminer });
936
- assertEquals(response.status, 202, "Response status should be 202 Accepted");
871
+ }, { specDeterminer })).status, 202, "Response status should be 202 Accepted");
937
872
  assertEquals(requestCount, 1, "Only one request should have been made");
938
873
  assertEquals(firstSpec, "draft-cavage", "First attempt should use draft-cavage");
939
874
  esm_default.hardReset();
@@ -981,6 +916,49 @@ test("doubleKnock() complex redirect chain test", async () => {
981
916
  for (const loggedReq of logs) assert(loggedReq.headers.has("Signature-Input") || loggedReq.headers.has("Signature"), "Each request should be signed with either RFC 9421 or draft-cavage");
982
917
  esm_default.hardReset();
983
918
  });
919
+ test("doubleKnock() throws on too many redirects", async () => {
920
+ esm_default.spyGlobal();
921
+ let requestCount = 0;
922
+ esm_default.post("begin:https://example.com/too-many-redirects/", (cl) => {
923
+ requestCount++;
924
+ const index = Number(cl.url.split("/").at(-1));
925
+ return Response.redirect(`https://example.com/too-many-redirects/${index + 1}`, 302);
926
+ });
927
+ const request = new Request("https://example.com/too-many-redirects/0", {
928
+ method: "POST",
929
+ body: "Redirect loop",
930
+ headers: { "Content-Type": "text/plain" }
931
+ });
932
+ await assertRejects(() => doubleKnock(request, {
933
+ keyId: rsaPublicKey2.id,
934
+ privateKey: rsaPrivateKey2
935
+ }), Error, "Too many redirections");
936
+ assertEquals(requestCount, 21);
937
+ esm_default.hardReset();
938
+ });
939
+ test("doubleKnock() detects redirect loops", async () => {
940
+ esm_default.spyGlobal();
941
+ let requestCount = 0;
942
+ esm_default.post("https://example.com/redirect-loop-a", () => {
943
+ requestCount++;
944
+ return Response.redirect("https://example.com/redirect-loop-b", 302);
945
+ });
946
+ esm_default.post("https://example.com/redirect-loop-b", () => {
947
+ requestCount++;
948
+ return Response.redirect("https://example.com/redirect-loop-a", 302);
949
+ });
950
+ const request = new Request("https://example.com/redirect-loop-a", {
951
+ method: "POST",
952
+ body: "Redirect loop",
953
+ headers: { "Content-Type": "text/plain" }
954
+ });
955
+ await assertRejects(() => doubleKnock(request, {
956
+ keyId: rsaPublicKey2.id,
957
+ privateKey: rsaPrivateKey2
958
+ }), Error, "Redirect loop detected");
959
+ assertEquals(requestCount, 2);
960
+ esm_default.hardReset();
961
+ });
984
962
  test("doubleKnock() async specDeterminer test", async () => {
985
963
  esm_default.spyGlobal();
986
964
  let requestCount = 0;
@@ -1001,25 +979,21 @@ test("doubleKnock() async specDeterminer test", async () => {
1001
979
  await new Promise((resolve) => setTimeout(resolve, 10));
1002
980
  }
1003
981
  };
1004
- const request = new Request("https://example.com/inbox-async-determiner", {
982
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-async-determiner", {
1005
983
  method: "POST",
1006
984
  body: "Test message with async spec determiner",
1007
985
  headers: { "Content-Type": "text/plain" }
1008
- });
1009
- const response = await doubleKnock(request, {
986
+ }), {
1010
987
  keyId: rsaPublicKey2.id,
1011
988
  privateKey: rsaPrivateKey2
1012
- }, { specDeterminer });
1013
- assertEquals(response.status, 202, "Response status should be 202 Accepted");
989
+ }, { specDeterminer })).status, 202, "Response status should be 202 Accepted");
1014
990
  assertEquals(requestCount, 1, "Only one request should have been made");
1015
991
  assertEquals(specUsed, "draft-cavage-http-signatures-12", "Should use spec from async determiner");
1016
992
  esm_default.hardReset();
1017
993
  });
1018
994
  test("timingSafeEqual()", async (t) => {
1019
995
  await t.step("should return true for equal empty arrays", () => {
1020
- const a = new Uint8Array([]);
1021
- const b = new Uint8Array([]);
1022
- assert(timingSafeEqual(a, b));
996
+ assert(timingSafeEqual(new Uint8Array([]), new Uint8Array([])));
1023
997
  });
1024
998
  await t.step("should return true for equal non-empty arrays", async (t2) => {
1025
999
  const testCases = [
@@ -1086,7 +1060,7 @@ test("timingSafeEqual()", async (t) => {
1086
1060
  assert(timingSafeEqual(arr, arr), "Array should be equal to itself by reference");
1087
1061
  });
1088
1062
  await t.step("should return false for arrays with same length but different content", async (t2) => {
1089
- const testCases = [
1063
+ for (const tc of [
1090
1064
  {
1091
1065
  a: [
1092
1066
  1,
@@ -1144,13 +1118,12 @@ test("timingSafeEqual()", async (t) => {
1144
1118
  ],
1145
1119
  name: "middle byte differs with edge values"
1146
1120
  }
1147
- ];
1148
- for (const tc of testCases) await t2.step(tc.name, () => {
1121
+ ]) await t2.step(tc.name, () => {
1149
1122
  assertFalse(timingSafeEqual(new Uint8Array(tc.a), new Uint8Array(tc.b)));
1150
1123
  });
1151
1124
  });
1152
1125
  await t.step("should return false for arrays with different lengths", async (t2) => {
1153
- const testCases = [
1126
+ for (const tc of [
1154
1127
  {
1155
1128
  a: [
1156
1129
  1,
@@ -1187,13 +1160,12 @@ test("timingSafeEqual()", async (t) => {
1187
1160
  b: [],
1188
1161
  name: "a non-empty, b empty"
1189
1162
  }
1190
- ];
1191
- for (const tc of testCases) await t2.step(tc.name, () => {
1163
+ ]) await t2.step(tc.name, () => {
1192
1164
  assertFalse(timingSafeEqual(new Uint8Array(tc.a), new Uint8Array(tc.b)));
1193
1165
  });
1194
1166
  });
1195
1167
  await t.step("should return false where content matches up to shorter length", async (t2) => {
1196
- const testCases = [
1168
+ for (const tc of [
1197
1169
  {
1198
1170
  a: [1, 2],
1199
1171
  b: [
@@ -1222,21 +1194,16 @@ test("timingSafeEqual()", async (t) => {
1222
1194
  b: [0],
1223
1195
  name: "two zeros vs single zero"
1224
1196
  }
1225
- ];
1226
- for (const tc of testCases) await t2.step(tc.name, () => {
1197
+ ]) await t2.step(tc.name, () => {
1227
1198
  assertFalse(timingSafeEqual(new Uint8Array(tc.a), new Uint8Array(tc.b)));
1228
1199
  });
1229
1200
  });
1230
1201
  await t.step("should correctly handle comparisons involving padding bytes", async (t2) => {
1231
1202
  await t2.step("a=[1], b=[1,0] (b longer with trailing zero)", () => {
1232
- const a1 = new Uint8Array([1]);
1233
- const b1 = new Uint8Array([1, 0]);
1234
- assertFalse(timingSafeEqual(a1, b1));
1203
+ assertFalse(timingSafeEqual(new Uint8Array([1]), new Uint8Array([1, 0])));
1235
1204
  });
1236
1205
  await t2.step("a=[1,0], b=[1] (a longer with trailing zero)", () => {
1237
- const a2 = new Uint8Array([1, 0]);
1238
- const b2 = new Uint8Array([1]);
1239
- assertFalse(timingSafeEqual(a2, b2));
1206
+ assertFalse(timingSafeEqual(new Uint8Array([1, 0]), new Uint8Array([1])));
1240
1207
  });
1241
1208
  });
1242
1209
  });
@@ -1256,20 +1223,18 @@ test("signRequest() [rfc9421] error handling for invalid signature base creation
1256
1223
  assertExists(signedRequest.headers.get("Signature"));
1257
1224
  });
1258
1225
  test("verifyRequest() [rfc9421] error handling for invalid signature base creation", async () => {
1259
- const request = new Request("https://example.com/test", {
1226
+ assertEquals(await verifyRequest(new Request("https://example.com/test", {
1260
1227
  method: "GET",
1261
1228
  headers: {
1262
1229
  "Accept": "application/json",
1263
1230
  "Signature-Input": "sig1=(\"@unsupported\");alg=\"rsa-pss-sha256\";keyid=\"https://example.com/key2\";created=1234567890",
1264
1231
  "Signature": "sig1=:invalid_signature_data:"
1265
1232
  }
1266
- });
1267
- const result = await verifyRequest(request, {
1233
+ }), {
1268
1234
  spec: "rfc9421",
1269
1235
  documentLoader: mockDocumentLoader,
1270
1236
  contextLoader: mockDocumentLoader
1271
- });
1272
- assertEquals(result, null, "Verification should fail gracefully for malformed signature inputs");
1237
+ }), null, "Verification should fail gracefully for malformed signature inputs");
1273
1238
  });
1274
1239
  test("doubleKnock() regression test for TypeError: unusable bug #294", async () => {
1275
1240
  esm_default.spyGlobal();
@@ -1283,16 +1248,14 @@ test("doubleKnock() regression test for TypeError: unusable bug #294", async ()
1283
1248
  esm_default.post("https://example.com/final-destination", () => {
1284
1249
  return new Response("Success", { status: 200 });
1285
1250
  });
1286
- const request = new Request("https://example.com/inbox-retry-redirect", {
1251
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-retry-redirect", {
1287
1252
  method: "POST",
1288
1253
  body: "Test activity content",
1289
1254
  headers: { "Content-Type": "application/activity+json" }
1290
- });
1291
- const response = await doubleKnock(request, {
1255
+ }), {
1292
1256
  keyId: rsaPublicKey2.id,
1293
1257
  privateKey: rsaPrivateKey2
1294
- });
1295
- assertEquals(response.status, 200);
1258
+ })).status, 200);
1296
1259
  assertEquals(requestCount, 2, "Should make 2 requests before redirect");
1297
1260
  esm_default.hardReset();
1298
1261
  });
@@ -1307,16 +1270,14 @@ test("doubleKnock() regression test for redirect handling bug", async () => {
1307
1270
  esm_default.post("https://example.com/final-destination", () => {
1308
1271
  return new Response("Success", { status: 200 });
1309
1272
  });
1310
- const request = new Request("https://example.com/inbox-retry-redirect", {
1273
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-retry-redirect", {
1311
1274
  method: "POST",
1312
1275
  body: "Test activity content",
1313
1276
  headers: { "Content-Type": "application/activity+json" }
1314
- });
1315
- const response = await doubleKnock(request, {
1277
+ }), {
1316
1278
  keyId: rsaPublicKey2.id,
1317
1279
  privateKey: rsaPrivateKey2
1318
- });
1319
- assertEquals(response.status, 200);
1280
+ })).status, 200);
1320
1281
  esm_default.hardReset();
1321
1282
  });
1322
1283
  test("signRequest() and verifyRequest() cancellation", {
@@ -1362,22 +1323,19 @@ test("signRequest() and verifyRequest() cancellation", {
1362
1323
  esm_default.hardReset();
1363
1324
  });
1364
1325
  test("signRequest() with custom label", async () => {
1365
- const request = new Request("https://example.com/api", {
1326
+ const signed = await signRequest(new Request("https://example.com/api", {
1366
1327
  method: "POST",
1367
1328
  body: "test",
1368
1329
  headers: { "Content-Type": "text/plain" }
1369
- });
1370
- const signed = await signRequest(request, rsaPrivateKey2, new URL("https://example.com/key2"), {
1330
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
1371
1331
  spec: "rfc9421",
1372
1332
  rfc9421: { label: "mysig" }
1373
1333
  });
1374
- const sigInput = signed.headers.get("Signature-Input");
1375
- assertStringIncludes(sigInput, "mysig=");
1376
- const sig = signed.headers.get("Signature");
1377
- assertStringIncludes(sig, "mysig=");
1334
+ assertStringIncludes(signed.headers.get("Signature-Input"), "mysig=");
1335
+ assertStringIncludes(signed.headers.get("Signature"), "mysig=");
1378
1336
  });
1379
1337
  test("signRequest() with custom components", async () => {
1380
- const request = new Request("https://example.com/api", {
1338
+ const sigInput = (await signRequest(new Request("https://example.com/api", {
1381
1339
  method: "POST",
1382
1340
  body: "test",
1383
1341
  headers: {
@@ -1385,8 +1343,7 @@ test("signRequest() with custom components", async () => {
1385
1343
  "Host": "example.com",
1386
1344
  "Date": "Tue, 05 Mar 2024 07:49:44 GMT"
1387
1345
  }
1388
- });
1389
- const signed = await signRequest(request, rsaPrivateKey2, new URL("https://example.com/key2"), {
1346
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
1390
1347
  spec: "rfc9421",
1391
1348
  rfc9421: { components: [
1392
1349
  {
@@ -1402,29 +1359,26 @@ test("signRequest() with custom components", async () => {
1402
1359
  params: {}
1403
1360
  }
1404
1361
  ] }
1405
- });
1406
- const sigInput = signed.headers.get("Signature-Input");
1362
+ })).headers.get("Signature-Input");
1407
1363
  assertStringIncludes(sigInput, "\"@method\"");
1408
1364
  assertStringIncludes(sigInput, "\"@target-uri\"");
1409
1365
  assertStringIncludes(sigInput, "\"@authority\"");
1410
1366
  assertStringIncludes(sigInput, "\"content-digest\"");
1411
1367
  });
1412
1368
  test("signRequest() with nonce and tag", async () => {
1413
- const request = new Request("https://example.com/api", {
1369
+ const sigInput = (await signRequest(new Request("https://example.com/api", {
1414
1370
  method: "GET",
1415
1371
  headers: {
1416
1372
  "Host": "example.com",
1417
1373
  "Date": "Tue, 05 Mar 2024 07:49:44 GMT"
1418
1374
  }
1419
- });
1420
- const signed = await signRequest(request, rsaPrivateKey2, new URL("https://example.com/key2"), {
1375
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
1421
1376
  spec: "rfc9421",
1422
1377
  rfc9421: {
1423
1378
  nonce: "test-nonce-123",
1424
1379
  tag: "app-v1"
1425
1380
  }
1426
- });
1427
- const sigInput = signed.headers.get("Signature-Input");
1381
+ })).headers.get("Signature-Input");
1428
1382
  assertStringIncludes(sigInput, "nonce=\"test-nonce-123\"");
1429
1383
  assertStringIncludes(sigInput, "tag=\"app-v1\"");
1430
1384
  });
@@ -1434,26 +1388,22 @@ test("formatRfc9421SignatureParameters() escapes nonce and tag", () => {
1434
1388
  keyId: new URL("https://example.com/key"),
1435
1389
  created: 1709626184
1436
1390
  };
1437
- const slashNonce = formatRfc9421SignatureParameters({
1391
+ assertStringIncludes(formatRfc9421SignatureParameters({
1438
1392
  ...commonParams,
1439
1393
  nonce: "x\\y"
1440
- });
1441
- assertStringIncludes(slashNonce, "nonce=\"x\\\\y\"");
1442
- const quoteNonce = formatRfc9421SignatureParameters({
1394
+ }), "nonce=\"x\\\\y\"");
1395
+ assertStringIncludes(formatRfc9421SignatureParameters({
1443
1396
  ...commonParams,
1444
1397
  nonce: "a\"b"
1445
- });
1446
- assertStringIncludes(quoteNonce, "nonce=\"a\\\"b\"");
1447
- const slashTag = formatRfc9421SignatureParameters({
1398
+ }), "nonce=\"a\\\"b\"");
1399
+ assertStringIncludes(formatRfc9421SignatureParameters({
1448
1400
  ...commonParams,
1449
1401
  tag: "x\\y"
1450
- });
1451
- assertStringIncludes(slashTag, "tag=\"x\\\\y\"");
1452
- const quoteTag = formatRfc9421SignatureParameters({
1402
+ }), "tag=\"x\\\\y\"");
1403
+ assertStringIncludes(formatRfc9421SignatureParameters({
1453
1404
  ...commonParams,
1454
1405
  tag: "a\"b"
1455
- });
1456
- assertStringIncludes(quoteTag, "tag=\"a\\\"b\"");
1406
+ }), "tag=\"a\\\"b\"");
1457
1407
  const mixed = formatRfc9421SignatureParameters({
1458
1408
  ...commonParams,
1459
1409
  nonce: "n\"o\\nce",
@@ -1463,12 +1413,11 @@ test("formatRfc9421SignatureParameters() escapes nonce and tag", () => {
1463
1413
  assertStringIncludes(mixed, "tag=\"t\\\"ag\\\\value\"");
1464
1414
  });
1465
1415
  test("signRequest() [rfc9421] accumulates multiple signatures when called sequentially", async () => {
1466
- const request = new Request("https://example.com/inbox", {
1416
+ const twiceSigned = await signRequest(await signRequest(new Request("https://example.com/inbox", {
1467
1417
  method: "POST",
1468
1418
  body: "Hello",
1469
1419
  headers: { "Content-Type": "text/plain" }
1470
- });
1471
- const onceSigned = await signRequest(request, rsaPrivateKey2, new URL("https://example.com/key2"), {
1420
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
1472
1421
  spec: "rfc9421",
1473
1422
  rfc9421: {
1474
1423
  label: "sig1",
@@ -1480,8 +1429,7 @@ test("signRequest() [rfc9421] accumulates multiple signatures when called sequen
1480
1429
  params: {}
1481
1430
  }]
1482
1431
  }
1483
- });
1484
- const twiceSigned = await signRequest(onceSigned, rsaPrivateKey2, new URL("https://example.com/key2"), {
1432
+ }), rsaPrivateKey2, new URL("https://example.com/key2"), {
1485
1433
  spec: "rfc9421",
1486
1434
  rfc9421: {
1487
1435
  label: "sig2",
@@ -1508,20 +1456,17 @@ test("doubleKnock(): Accept-Signature challenge retry succeeds", async () => {
1508
1456
  status: 401,
1509
1457
  headers: { "Accept-Signature": "sig1=(\"@method\" \"@target-uri\" \"@authority\" \"content-digest\");created;nonce=\"challenge-nonce-1\"" }
1510
1458
  });
1511
- const sigInput = req.headers.get("Signature-Input") ?? "";
1512
- if (sigInput.includes("challenge-nonce-1")) return new Response("", { status: 202 });
1459
+ if ((req.headers.get("Signature-Input") ?? "").includes("challenge-nonce-1")) return new Response("", { status: 202 });
1513
1460
  return new Response("Bad", { status: 400 });
1514
1461
  });
1515
- const request = new Request("https://example.com/inbox-challenge-ok", {
1462
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-challenge-ok", {
1516
1463
  method: "POST",
1517
1464
  body: "Test message",
1518
1465
  headers: { "Content-Type": "text/plain" }
1519
- });
1520
- const response = await doubleKnock(request, {
1466
+ }), {
1521
1467
  keyId: rsaPublicKey2.id,
1522
1468
  privateKey: rsaPrivateKey2
1523
- });
1524
- assertEquals(response.status, 202);
1469
+ })).status, 202);
1525
1470
  assertEquals(requestCount, 2);
1526
1471
  esm_default.hardReset();
1527
1472
  });
@@ -1538,16 +1483,14 @@ test("doubleKnock(): unfulfillable Accept-Signature falls to legacy fallback", a
1538
1483
  if (req.headers.has("Signature") && !req.headers.has("Signature-Input")) return new Response("", { status: 202 });
1539
1484
  return new Response("Bad", { status: 400 });
1540
1485
  });
1541
- const request = new Request("https://example.com/inbox-unfulfillable", {
1486
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-unfulfillable", {
1542
1487
  method: "POST",
1543
1488
  body: "Test message",
1544
1489
  headers: { "Content-Type": "text/plain" }
1545
- });
1546
- const response = await doubleKnock(request, {
1490
+ }), {
1547
1491
  keyId: rsaPublicKey2.id,
1548
1492
  privateKey: rsaPrivateKey2
1549
- });
1550
- assertEquals(response.status, 202);
1493
+ })).status, 202);
1551
1494
  assertEquals(requestCount, 2);
1552
1495
  esm_default.hardReset();
1553
1496
  });
@@ -1561,16 +1504,14 @@ test("doubleKnock(): no Accept-Signature falls to legacy fallback", async () =>
1561
1504
  if (req.headers.has("Signature")) return new Response("", { status: 202 });
1562
1505
  return new Response("Bad", { status: 400 });
1563
1506
  });
1564
- const request = new Request("https://example.com/inbox-no-challenge", {
1507
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-no-challenge", {
1565
1508
  method: "POST",
1566
1509
  body: "Test message",
1567
1510
  headers: { "Content-Type": "text/plain" }
1568
- });
1569
- const response = await doubleKnock(request, {
1511
+ }), {
1570
1512
  keyId: rsaPublicKey2.id,
1571
1513
  privateKey: rsaPrivateKey2
1572
- });
1573
- assertEquals(response.status, 202);
1514
+ })).status, 202);
1574
1515
  assertEquals(requestCount, 2);
1575
1516
  esm_default.hardReset();
1576
1517
  });
@@ -1588,16 +1529,14 @@ test("doubleKnock(): challenge retry also fails → legacy fallback attempted",
1588
1529
  if (req.headers.has("Signature") && !req.headers.has("Signature-Input")) return new Response("", { status: 202 });
1589
1530
  return new Response("Bad", { status: 400 });
1590
1531
  });
1591
- const request = new Request("https://example.com/inbox-challenge-fails", {
1532
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-challenge-fails", {
1592
1533
  method: "POST",
1593
1534
  body: "Test message",
1594
1535
  headers: { "Content-Type": "text/plain" }
1595
- });
1596
- const response = await doubleKnock(request, {
1536
+ }), {
1597
1537
  keyId: rsaPublicKey2.id,
1598
1538
  privateKey: rsaPrivateKey2
1599
- });
1600
- assertEquals(response.status, 202);
1539
+ })).status, 202);
1601
1540
  assertEquals(requestCount, 3);
1602
1541
  esm_default.hardReset();
1603
1542
  });
@@ -1618,16 +1557,14 @@ test("doubleKnock(): challenge retry returns another challenge → not followed"
1618
1557
  if (req.headers.has("Signature") && !req.headers.has("Signature-Input")) return new Response("", { status: 202 });
1619
1558
  return new Response("Bad", { status: 400 });
1620
1559
  });
1621
- const request = new Request("https://example.com/inbox-challenge-loop", {
1560
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-challenge-loop", {
1622
1561
  method: "POST",
1623
1562
  body: "Test message",
1624
1563
  headers: { "Content-Type": "text/plain" }
1625
- });
1626
- const response = await doubleKnock(request, {
1564
+ }), {
1627
1565
  keyId: rsaPublicKey2.id,
1628
1566
  privateKey: rsaPrivateKey2
1629
- });
1630
- assertEquals(response.status, 202);
1567
+ })).status, 202);
1631
1568
  assertEquals(requestCount, 3);
1632
1569
  esm_default.hardReset();
1633
1570
  });
@@ -1644,16 +1581,14 @@ test("doubleKnock(): Accept-Signature with unsupported component falls to legacy
1644
1581
  if (req.headers.has("Signature") && !req.headers.has("Signature-Input")) return new Response("", { status: 202 });
1645
1582
  return new Response("Bad", { status: 400 });
1646
1583
  });
1647
- const request = new Request("https://example.com/inbox-bad-challenge", {
1584
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-bad-challenge", {
1648
1585
  method: "POST",
1649
1586
  body: "Test message",
1650
1587
  headers: { "Content-Type": "text/plain" }
1651
- });
1652
- const response = await doubleKnock(request, {
1588
+ }), {
1653
1589
  keyId: rsaPublicKey2.id,
1654
1590
  privateKey: rsaPrivateKey2
1655
- });
1656
- assertEquals(response.status, 202);
1591
+ })).status, 202);
1657
1592
  assertEquals(requestCount, 2);
1658
1593
  esm_default.hardReset();
1659
1594
  });
@@ -1670,16 +1605,14 @@ test("doubleKnock(): Accept-Signature with unsupported derived component falls t
1670
1605
  if (req.headers.has("Signature") && !req.headers.has("Signature-Input")) return new Response("", { status: 202 });
1671
1606
  return new Response("Bad", { status: 400 });
1672
1607
  });
1673
- const request = new Request("https://example.com/inbox-bad-derived", {
1608
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-bad-derived", {
1674
1609
  method: "POST",
1675
1610
  body: "Test message",
1676
1611
  headers: { "Content-Type": "text/plain" }
1677
- });
1678
- const response = await doubleKnock(request, {
1612
+ }), {
1679
1613
  keyId: rsaPublicKey2.id,
1680
1614
  privateKey: rsaPrivateKey2
1681
- });
1682
- assertEquals(response.status, 202);
1615
+ })).status, 202);
1683
1616
  assertEquals(requestCount, 2);
1684
1617
  esm_default.hardReset();
1685
1618
  });
@@ -1696,16 +1629,14 @@ test("doubleKnock(): Accept-Signature with multiple entries where first throws f
1696
1629
  if (req.headers.has("Signature-Input")) return new Response("", { status: 202 });
1697
1630
  return new Response("Bad", { status: 400 });
1698
1631
  });
1699
- const request = new Request("https://example.com/inbox-multi-challenge", {
1632
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-multi-challenge", {
1700
1633
  method: "POST",
1701
1634
  body: "Test message",
1702
1635
  headers: { "Content-Type": "text/plain" }
1703
- });
1704
- const response = await doubleKnock(request, {
1636
+ }), {
1705
1637
  keyId: rsaPublicKey2.id,
1706
1638
  privateKey: rsaPrivateKey2
1707
- });
1708
- assertEquals(response.status, 202);
1639
+ })).status, 202);
1709
1640
  assertEquals(requestCount, 2);
1710
1641
  esm_default.hardReset();
1711
1642
  });
@@ -1724,18 +1655,16 @@ test("doubleKnock(): Accept-Signature with multiple compatible entries fulfills
1724
1655
  if (sigInput.includes("sig1=") && sigInput.includes("sig2=") && sig.includes("sig1=") && sig.includes("sig2=")) return new Response("", { status: 202 });
1725
1656
  return new Response("Missing signatures", { status: 400 });
1726
1657
  });
1727
- const request = new Request("https://example.com/inbox-multi-compat", {
1658
+ assertEquals((await doubleKnock(new Request("https://example.com/inbox-multi-compat", {
1728
1659
  method: "POST",
1729
1660
  body: "Test message",
1730
1661
  headers: { "Content-Type": "text/plain" }
1731
- });
1732
- const response = await doubleKnock(request, {
1662
+ }), {
1733
1663
  keyId: rsaPublicKey2.id,
1734
1664
  privateKey: rsaPrivateKey2
1735
- });
1736
- assertEquals(response.status, 202);
1665
+ })).status, 202);
1737
1666
  assertEquals(requestCount, 2);
1738
1667
  esm_default.hardReset();
1739
1668
  });
1740
-
1741
- //#endregion
1669
+ //#endregion
1670
+ export {};