@adcp/sdk 15.9.1 → 16.0.0-beta.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 (321) hide show
  1. package/bin/adcp-identity.js +74 -0
  2. package/bin/adcp.js +2 -0
  3. package/dist/lib/adapters/keyed-account-backend.d.mts +37 -0
  4. package/dist/lib/adapters/keyed-account-backend.d.ts +37 -0
  5. package/dist/lib/adapters/keyed-account-backend.js +121 -0
  6. package/dist/lib/adapters/keyed-account-backend.mjs +94 -0
  7. package/dist/lib/adapters/keyed-account-store.d.mts +28 -0
  8. package/dist/lib/adapters/keyed-account-store.d.ts +28 -0
  9. package/dist/lib/adapters/keyed-account-store.js +227 -0
  10. package/dist/lib/adapters/keyed-account-store.mjs +203 -0
  11. package/dist/lib/core/SingleAgentClient.d.mts +25 -2
  12. package/dist/lib/core/SingleAgentClient.d.ts +25 -2
  13. package/dist/lib/core/SingleAgentClient.js +280 -62
  14. package/dist/lib/core/SingleAgentClient.mjs +282 -57
  15. package/dist/lib/core/TaskExecutor.d.mts +6 -0
  16. package/dist/lib/core/TaskExecutor.d.ts +6 -0
  17. package/dist/lib/core/TaskExecutor.js +66 -5
  18. package/dist/lib/core/TaskExecutor.mjs +71 -5
  19. package/dist/lib/core/postgres-webhook-registration-store.js +32 -5
  20. package/dist/lib/core/postgres-webhook-registration-store.mjs +32 -5
  21. package/dist/lib/core/redis-webhook-registration-store.js +23 -1
  22. package/dist/lib/core/redis-webhook-registration-store.mjs +23 -1
  23. package/dist/lib/core/webhook-registration.d.mts +7 -1
  24. package/dist/lib/core/webhook-registration.d.ts +7 -1
  25. package/dist/lib/core/webhook-registration.js +24 -2
  26. package/dist/lib/core/webhook-registration.mjs +23 -2
  27. package/dist/lib/identity-3.3/generated-compat.d.mts +34 -0
  28. package/dist/lib/identity-3.3/generated-compat.d.ts +34 -0
  29. package/dist/lib/identity-3.3/generated-compat.js +16 -0
  30. package/dist/lib/identity-3.3/generated-compat.mjs +0 -0
  31. package/dist/lib/identity-3.3/index.d.mts +10 -0
  32. package/dist/lib/identity-3.3/index.d.ts +10 -0
  33. package/dist/lib/identity-3.3/index.js +79 -0
  34. package/dist/lib/identity-3.3/index.mjs +50 -0
  35. package/dist/lib/identity-3.3/lifecycle.d.mts +125 -0
  36. package/dist/lib/identity-3.3/lifecycle.d.ts +125 -0
  37. package/dist/lib/identity-3.3/lifecycle.js +294 -0
  38. package/dist/lib/identity-3.3/lifecycle.mjs +272 -0
  39. package/dist/lib/identity-3.3/revocation.d.mts +86 -0
  40. package/dist/lib/identity-3.3/revocation.d.ts +86 -0
  41. package/dist/lib/identity-3.3/revocation.js +308 -0
  42. package/dist/lib/identity-3.3/revocation.mjs +286 -0
  43. package/dist/lib/identity-3.3/schemas.draft.d.mts +2472 -0
  44. package/dist/lib/identity-3.3/schemas.draft.d.ts +2472 -0
  45. package/dist/lib/identity-3.3/schemas.draft.js +1448 -0
  46. package/dist/lib/identity-3.3/schemas.draft.mjs +1428 -0
  47. package/dist/lib/identity-3.3/server.d.mts +46 -0
  48. package/dist/lib/identity-3.3/server.d.ts +46 -0
  49. package/dist/lib/identity-3.3/server.js +279 -0
  50. package/dist/lib/identity-3.3/server.mjs +260 -0
  51. package/dist/lib/identity-3.3/store.d.mts +24 -0
  52. package/dist/lib/identity-3.3/store.d.ts +24 -0
  53. package/dist/lib/identity-3.3/store.js +191 -0
  54. package/dist/lib/identity-3.3/store.mjs +169 -0
  55. package/dist/lib/identity-3.3/trust.d.mts +155 -0
  56. package/dist/lib/identity-3.3/trust.d.ts +155 -0
  57. package/dist/lib/identity-3.3/trust.js +383 -0
  58. package/dist/lib/identity-3.3/trust.mjs +342 -0
  59. package/dist/lib/identity-3.3/types.d.mts +155 -0
  60. package/dist/lib/identity-3.3/types.d.ts +155 -0
  61. package/dist/lib/identity-3.3/types.js +16 -0
  62. package/dist/lib/identity-3.3/types.mjs +0 -0
  63. package/dist/lib/identity-3.3/validation.d.mts +2 -0
  64. package/dist/lib/identity-3.3/validation.d.ts +2 -0
  65. package/dist/lib/identity-3.3/validation.js +125 -0
  66. package/dist/lib/identity-3.3/validation.mjs +91 -0
  67. package/dist/lib/index.d.mts +1 -1
  68. package/dist/lib/index.d.ts +1 -1
  69. package/dist/lib/index.js +2 -0
  70. package/dist/lib/index.mjs +2 -0
  71. package/dist/lib/protocols/index.d.mts +1 -1
  72. package/dist/lib/protocols/index.d.ts +1 -1
  73. package/dist/lib/protocols/index.js +2 -2
  74. package/dist/lib/protocols/index.mjs +2 -2
  75. package/dist/lib/reporting/azure/index.d.mts +5 -0
  76. package/dist/lib/reporting/azure/index.d.ts +5 -0
  77. package/dist/lib/reporting/azure/index.js +26 -7
  78. package/dist/lib/reporting/azure/index.mjs +26 -7
  79. package/dist/lib/reporting/bigquery/index.d.mts +39 -5
  80. package/dist/lib/reporting/bigquery/index.d.ts +39 -5
  81. package/dist/lib/reporting/bigquery/index.js +26 -20
  82. package/dist/lib/reporting/bigquery/index.mjs +26 -20
  83. package/dist/lib/reporting/gcs/adapter.d.mts +8 -1
  84. package/dist/lib/reporting/gcs/adapter.d.ts +8 -1
  85. package/dist/lib/reporting/gcs/errors.d.mts +6 -0
  86. package/dist/lib/reporting/gcs/errors.d.ts +6 -0
  87. package/dist/lib/reporting/gcs/fence.d.mts +5 -0
  88. package/dist/lib/reporting/gcs/fence.d.ts +5 -0
  89. package/dist/lib/reporting/gcs/index.d.mts +9 -0
  90. package/dist/lib/reporting/gcs/index.d.ts +9 -0
  91. package/dist/lib/reporting/gcs/reader.d.mts +27 -3
  92. package/dist/lib/reporting/gcs/reader.d.ts +27 -3
  93. package/dist/lib/reporting/gcs/row-provider.d.mts +7 -0
  94. package/dist/lib/reporting/gcs/row-provider.d.ts +7 -0
  95. package/dist/lib/reporting/gcs/row-provider.js +34 -14
  96. package/dist/lib/reporting/gcs/row-provider.mjs +34 -14
  97. package/dist/lib/reporting/ledger/change-feed.d.mts +22 -1
  98. package/dist/lib/reporting/ledger/change-feed.d.ts +22 -1
  99. package/dist/lib/reporting/ledger/index.d.mts +11 -0
  100. package/dist/lib/reporting/ledger/index.d.ts +11 -0
  101. package/dist/lib/reporting/ledger/managed-postgres.d.mts +8 -0
  102. package/dist/lib/reporting/ledger/managed-postgres.d.ts +8 -0
  103. package/dist/lib/reporting/ledger/managed-router.d.mts +27 -2
  104. package/dist/lib/reporting/ledger/managed-router.d.ts +27 -2
  105. package/dist/lib/reporting/ledger/managed.d.mts +86 -6
  106. package/dist/lib/reporting/ledger/managed.d.ts +86 -6
  107. package/dist/lib/reporting/ledger/notification-activity.d.mts +65 -3
  108. package/dist/lib/reporting/ledger/notification-activity.d.ts +65 -3
  109. package/dist/lib/reporting/ledger/object-writes.d.mts +64 -5
  110. package/dist/lib/reporting/ledger/object-writes.d.ts +64 -5
  111. package/dist/lib/reporting/ledger/postgres.d.mts +43 -9
  112. package/dist/lib/reporting/ledger/postgres.d.ts +43 -9
  113. package/dist/lib/reporting/ledger/postgres.js +43 -27
  114. package/dist/lib/reporting/ledger/postgres.mjs +43 -27
  115. package/dist/lib/reporting/ledger/row-encoding.d.mts +92 -6
  116. package/dist/lib/reporting/ledger/row-encoding.d.ts +92 -6
  117. package/dist/lib/reporting/ledger/row-encoding.js +8 -4
  118. package/dist/lib/reporting/ledger/row-encoding.mjs +6 -3
  119. package/dist/lib/reporting/ledger/row-storage-errors.d.mts +11 -1
  120. package/dist/lib/reporting/ledger/row-storage-errors.d.ts +11 -1
  121. package/dist/lib/reporting/ledger/row-storage-migration.d.mts +19 -1
  122. package/dist/lib/reporting/ledger/row-storage-migration.d.ts +19 -1
  123. package/dist/lib/reporting/ledger/row-storage-migration.js +133 -18
  124. package/dist/lib/reporting/ledger/row-storage-migration.mjs +133 -18
  125. package/dist/lib/reporting/ledger/row-storage-object.d.mts +103 -12
  126. package/dist/lib/reporting/ledger/row-storage-object.d.ts +103 -12
  127. package/dist/lib/reporting/ledger/row-storage-object.js +106 -31
  128. package/dist/lib/reporting/ledger/row-storage-object.mjs +104 -31
  129. package/dist/lib/reporting/ledger/row-storage.d.mts +87 -20
  130. package/dist/lib/reporting/ledger/row-storage.d.ts +87 -20
  131. package/dist/lib/reporting/ledger/row-storage.js +126 -66
  132. package/dist/lib/reporting/ledger/row-storage.mjs +129 -67
  133. package/dist/lib/reporting/ledger/schedule.d.mts +13 -0
  134. package/dist/lib/reporting/ledger/schedule.d.ts +13 -0
  135. package/dist/lib/reporting/ledger/schedule.js +24 -0
  136. package/dist/lib/reporting/ledger/schedule.mjs +23 -0
  137. package/dist/lib/reporting/ledger/types.d.mts +23 -1
  138. package/dist/lib/reporting/ledger/types.d.ts +23 -1
  139. package/dist/lib/reporting/s3/index.d.mts +5 -0
  140. package/dist/lib/reporting/s3/index.d.ts +5 -0
  141. package/dist/lib/reporting/s3/index.js +37 -14
  142. package/dist/lib/reporting/s3/index.mjs +37 -14
  143. package/dist/lib/reporting/service/index.d.mts +10 -0
  144. package/dist/lib/reporting/service/index.d.ts +10 -0
  145. package/dist/lib/reporting/service/maintenance.d.mts +28 -2
  146. package/dist/lib/reporting/service/maintenance.d.ts +28 -2
  147. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  148. package/dist/lib/server/account-sync-contract.d.mts +14 -0
  149. package/dist/lib/server/account-sync-contract.d.ts +14 -0
  150. package/dist/lib/server/account-sync-contract.js +225 -0
  151. package/dist/lib/server/account-sync-contract.mjs +197 -0
  152. package/dist/lib/server/adcp-server.js +5 -0
  153. package/dist/lib/server/adcp-server.mjs +10 -0
  154. package/dist/lib/server/auth-signature.d.mts +15 -0
  155. package/dist/lib/server/auth-signature.d.ts +15 -0
  156. package/dist/lib/server/auth-signature.js +72 -2
  157. package/dist/lib/server/auth-signature.mjs +81 -2
  158. package/dist/lib/server/auth.d.mts +6 -0
  159. package/dist/lib/server/auth.d.ts +6 -0
  160. package/dist/lib/server/auth.js +6 -0
  161. package/dist/lib/server/auth.mjs +6 -0
  162. package/dist/lib/server/create-adcp-server.d.mts +19 -5
  163. package/dist/lib/server/create-adcp-server.d.ts +19 -5
  164. package/dist/lib/server/create-adcp-server.js +259 -19
  165. package/dist/lib/server/create-adcp-server.mjs +266 -10
  166. package/dist/lib/server/decisioning/account.d.mts +24 -1
  167. package/dist/lib/server/decisioning/account.d.ts +24 -1
  168. package/dist/lib/server/decisioning/index.d.mts +1 -1
  169. package/dist/lib/server/decisioning/index.d.ts +1 -1
  170. package/dist/lib/server/decisioning/runtime/from-platform.d.mts +0 -50
  171. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +0 -50
  172. package/dist/lib/server/decisioning/runtime/from-platform.js +61 -11
  173. package/dist/lib/server/decisioning/runtime/from-platform.mjs +65 -12
  174. package/dist/lib/server/index.d.mts +2 -0
  175. package/dist/lib/server/index.d.ts +2 -0
  176. package/dist/lib/server/index.js +12 -0
  177. package/dist/lib/server/index.mjs +12 -0
  178. package/dist/lib/server/notification-subscriptions/runtime.js +9 -1
  179. package/dist/lib/server/notification-subscriptions/runtime.mjs +9 -1
  180. package/dist/lib/server/notification-subscriptions/types.d.mts +5 -0
  181. package/dist/lib/server/notification-subscriptions/types.d.ts +5 -0
  182. package/dist/lib/server/principal/types.d.mts +1 -1
  183. package/dist/lib/server/principal/types.d.ts +1 -1
  184. package/dist/lib/server/serve.js +5 -0
  185. package/dist/lib/server/serve.mjs +5 -0
  186. package/dist/lib/server/webhook-delivery/recovery.d.mts +1 -0
  187. package/dist/lib/server/webhook-delivery/recovery.d.ts +1 -0
  188. package/dist/lib/server/webhook-delivery/recovery.js +4 -0
  189. package/dist/lib/server/webhook-delivery/recovery.mjs +4 -0
  190. package/dist/lib/server/webhook-emitter.d.mts +10 -0
  191. package/dist/lib/server/webhook-emitter.d.ts +10 -0
  192. package/dist/lib/server/webhook-emitter.js +22 -0
  193. package/dist/lib/server/webhook-emitter.mjs +22 -0
  194. package/dist/lib/signing/agent-context.d.mts +0 -5
  195. package/dist/lib/signing/agent-context.d.ts +0 -5
  196. package/dist/lib/signing/agent-context.js +9 -1
  197. package/dist/lib/signing/agent-context.mjs +9 -1
  198. package/dist/lib/signing/agent-fetch.js +27 -2
  199. package/dist/lib/signing/agent-fetch.mjs +32 -2
  200. package/dist/lib/signing/agent-resolver/canonicalize.d.mts +10 -15
  201. package/dist/lib/signing/agent-resolver/canonicalize.d.ts +10 -15
  202. package/dist/lib/signing/agent-resolver/canonicalize.js +17 -5
  203. package/dist/lib/signing/agent-resolver/canonicalize.mjs +16 -5
  204. package/dist/lib/signing/agent-resolver/index.d.mts +1 -0
  205. package/dist/lib/signing/agent-resolver/index.d.ts +1 -0
  206. package/dist/lib/signing/agent-resolver/index.js +5 -0
  207. package/dist/lib/signing/agent-resolver/index.mjs +3 -0
  208. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +3 -2
  209. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +3 -2
  210. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +30 -3
  211. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +30 -3
  212. package/dist/lib/signing/agent-resolver/strict-json.d.mts +2 -2
  213. package/dist/lib/signing/agent-resolver/strict-json.d.ts +2 -2
  214. package/dist/lib/signing/agent-resolver/strict-json.js +5 -3
  215. package/dist/lib/signing/agent-resolver/strict-json.mjs +5 -3
  216. package/dist/lib/signing/brand-jwks.d.mts +2 -2
  217. package/dist/lib/signing/brand-jwks.d.ts +2 -2
  218. package/dist/lib/signing/brand-jwks.js +4 -4
  219. package/dist/lib/signing/brand-jwks.mjs +4 -4
  220. package/dist/lib/signing/client.d.mts +1 -0
  221. package/dist/lib/signing/client.d.ts +1 -0
  222. package/dist/lib/signing/client.js +5 -0
  223. package/dist/lib/signing/client.mjs +3 -0
  224. package/dist/lib/signing/crypto.js +8 -0
  225. package/dist/lib/signing/crypto.mjs +9 -1
  226. package/dist/lib/signing/fetch-async.js +1 -1
  227. package/dist/lib/signing/fetch-async.mjs +1 -1
  228. package/dist/lib/signing/fetch.js +1 -1
  229. package/dist/lib/signing/fetch.mjs +1 -1
  230. package/dist/lib/signing/jwks-https.d.mts +1 -1
  231. package/dist/lib/signing/jwks-https.d.ts +1 -1
  232. package/dist/lib/signing/jwks-https.js +1 -1
  233. package/dist/lib/signing/jwks-https.mjs +1 -1
  234. package/dist/lib/signing/jwks.d.mts +11 -3
  235. package/dist/lib/signing/jwks.d.ts +11 -3
  236. package/dist/lib/signing/jwks.js +1 -1
  237. package/dist/lib/signing/jwks.mjs +1 -1
  238. package/dist/lib/signing/middleware.js +9 -0
  239. package/dist/lib/signing/middleware.mjs +14 -0
  240. package/dist/lib/signing/parser.d.mts +9 -6
  241. package/dist/lib/signing/parser.d.ts +9 -6
  242. package/dist/lib/signing/parser.js +82 -64
  243. package/dist/lib/signing/parser.mjs +83 -65
  244. package/dist/lib/signing/revocation.d.mts +4 -1
  245. package/dist/lib/signing/revocation.d.ts +4 -1
  246. package/dist/lib/signing/revocation.js +36 -3
  247. package/dist/lib/signing/revocation.mjs +34 -2
  248. package/dist/lib/signing/server.d.mts +1 -1
  249. package/dist/lib/signing/server.d.ts +1 -1
  250. package/dist/lib/signing/server.js +4 -0
  251. package/dist/lib/signing/server.mjs +4 -0
  252. package/dist/lib/signing/types.d.mts +3 -0
  253. package/dist/lib/signing/types.d.ts +3 -0
  254. package/dist/lib/signing/verifier.js +1 -1
  255. package/dist/lib/signing/verifier.mjs +1 -1
  256. package/dist/lib/signing/web-bot-auth.d.mts +191 -0
  257. package/dist/lib/signing/web-bot-auth.d.ts +191 -0
  258. package/dist/lib/signing/web-bot-auth.js +837 -0
  259. package/dist/lib/signing/web-bot-auth.mjs +796 -0
  260. package/dist/lib/signing/webhook-verifier.js +1 -1
  261. package/dist/lib/signing/webhook-verifier.mjs +1 -1
  262. package/dist/lib/testing/client.d.mts +1 -4
  263. package/dist/lib/testing/client.d.ts +1 -4
  264. package/dist/lib/testing/client.js +141 -20
  265. package/dist/lib/testing/client.mjs +137 -19
  266. package/dist/lib/testing/compliance/comply.js +29 -10
  267. package/dist/lib/testing/compliance/comply.mjs +37 -11
  268. package/dist/lib/testing/storyboard/account-sync-contract.d.mts +3 -0
  269. package/dist/lib/testing/storyboard/account-sync-contract.d.ts +3 -0
  270. package/dist/lib/testing/storyboard/account-sync-contract.js +79 -0
  271. package/dist/lib/testing/storyboard/account-sync-contract.mjs +55 -0
  272. package/dist/lib/testing/storyboard/default-invariants.js +2 -0
  273. package/dist/lib/testing/storyboard/default-invariants.mjs +2 -0
  274. package/dist/lib/testing/storyboard/runner.js +23 -8
  275. package/dist/lib/testing/storyboard/runner.mjs +24 -8
  276. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  277. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  278. package/dist/lib/types/adcp.d.mts +4 -0
  279. package/dist/lib/types/adcp.d.ts +4 -0
  280. package/dist/lib/types/schemas.generated.d.ts +6 -6
  281. package/dist/lib/utils/capabilities.d.mts +1 -0
  282. package/dist/lib/utils/capabilities.d.ts +1 -0
  283. package/dist/lib/utils/capabilities.js +5 -0
  284. package/dist/lib/utils/capabilities.mjs +5 -0
  285. package/dist/lib/v2/projection/catalog-snapshot.js +3 -1
  286. package/dist/lib/v2/projection/catalog-snapshot.mjs +3 -1
  287. package/dist/lib/v2/projection/creative-delivery.d.mts +34 -10
  288. package/dist/lib/v2/projection/creative-delivery.d.ts +34 -10
  289. package/dist/lib/v2/projection/creative-delivery.js +26 -10
  290. package/dist/lib/v2/projection/creative-delivery.mjs +26 -10
  291. package/dist/lib/v2/projection/index.d.mts +1 -1
  292. package/dist/lib/v2/projection/index.d.ts +1 -1
  293. package/dist/lib/v2/projection/index.js +2 -0
  294. package/dist/lib/v2/projection/index.mjs +2 -0
  295. package/dist/lib/v2/projection/legacy-routes.d.mts +34 -2
  296. package/dist/lib/v2/projection/legacy-routes.d.ts +34 -2
  297. package/dist/lib/v2/projection/legacy-routes.js +35 -2
  298. package/dist/lib/v2/projection/legacy-routes.mjs +32 -2
  299. package/dist/lib/validation/schema-loader.d.mts +2 -0
  300. package/dist/lib/validation/schema-loader.d.ts +2 -0
  301. package/dist/lib/validation/schema-loader.js +43 -4
  302. package/dist/lib/validation/schema-loader.mjs +39 -3
  303. package/dist/lib/version.d.mts +3 -3
  304. package/dist/lib/version.d.ts +3 -3
  305. package/dist/lib/version.js +3 -3
  306. package/dist/lib/version.mjs +3 -3
  307. package/docs/TYPE-SUMMARY.md +2 -2
  308. package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +4 -0
  309. package/docs/guides/REPORTING-LEDGER-CUTOVER.md +1 -1
  310. package/docs/guides/REPORTING-LEDGER.md +99 -29
  311. package/docs/guides/REPORTING-OPERATIONS.md +29 -13
  312. package/docs/guides/REPORTING-STABILITY.md +76 -0
  313. package/docs/guides/account-resolution.md +101 -0
  314. package/docs/llms.txt +3 -2
  315. package/docs/migration-12-to-13.md +15 -0
  316. package/docs/migration-14.x-rc-worksheet.md +4 -4
  317. package/docs/migration-agent-identity-3.3.md +494 -0
  318. package/docs/migration-agent-resolution-3.3.md +5 -0
  319. package/docs/migration-reporting-row-storage-alignment.md +137 -0
  320. package/examples/storyboards/account-sync-contract.yaml +57 -0
  321. package/package.json +17 -1
@@ -0,0 +1,796 @@
1
+ import { toReleasePrecisionVersion } from "../version.mjs";
2
+ import {
3
+ constants as cryptoConstants,
4
+ createHash,
5
+ createPrivateKey,
6
+ randomBytes,
7
+ sign as nodeSign
8
+ } from "crypto";
9
+ import { parseDictionary, parseItem, serializeItem } from "structured-headers";
10
+ import { ssrfSafeFetch } from "../net/index.mjs";
11
+ import { eTldPlusOne } from "./agent-resolver/etld.mjs";
12
+ import { canonicalAgentOrigin } from "./agent-resolver/canonicalize.mjs";
13
+ import { canonicalAgentUrl } from "./agent-resolver/select-agent.mjs";
14
+ import { parseStrictJson } from "./agent-resolver/strict-json.mjs";
15
+ import { getHeaderValue } from "./canonicalize.mjs";
16
+ import { computeContentDigest, contentDigestMatches, contentDigestUsesEncoding } from "./content-digest.mjs";
17
+ import { jwkToPublicKey, verifySignature } from "./crypto.mjs";
18
+ import {
19
+ parseSignature,
20
+ parseSignatureInput,
21
+ rejectDuplicateDictionaryKeys
22
+ } from "./parser.mjs";
23
+ const WEB_BOT_AUTH_PROFILE = "web-bot-auth";
24
+ const WEB_BOT_AUTH_DRAFT_REVISION = "draft-ietf-webbotauth-httpsig-protocol-00";
25
+ const SIGNATURE_DIRECTORY_PATH = "/.well-known/http-message-signatures-directory";
26
+ const SIGNATURE_DIRECTORY_MEDIA_TYPE = "application/http-message-signatures-directory+json";
27
+ const DIRECTORY_TAG = "http-message-signatures-directory";
28
+ const DIRECTORY_CLOCK_SKEW_SECONDS = 60;
29
+ const TEST_KIDS = /* @__PURE__ */ new Set([
30
+ "6WsKMCObba49V3uN0vIENnk1BKAk7c9zjBJcWKyQ9_I",
31
+ "hHurKlVBnPJ4AGiPyl6HWMhxgkIcRBzaC8qWwEtnuVI",
32
+ "ZtaFf5jj-EzorJmZn6mX3YnmK_QyZKVLbbinzIQKOjQ",
33
+ "1XlJ3EIte7bPOEqKQtKrKNTNy5hywTyrwFXWrMbXKqE"
34
+ ]);
35
+ const NO_CHALLENGE_REASONS = /* @__PURE__ */ new Set(["key_revoked", "identity_not_bound", "profile_disabled", "authorization_failed"]);
36
+ class WebBotAuthError extends Error {
37
+ constructor(status, reason) {
38
+ super(
39
+ status === 400 ? "Malformed Web Bot Auth message" : status === 429 ? "Web Bot Auth rate limit" : "Web Bot Auth verification failed"
40
+ );
41
+ this.status = status;
42
+ this.reason = reason;
43
+ this.name = "WebBotAuthError";
44
+ this.headers = status === 403 && !NO_CHALLENGE_REASONS.has(reason) ? {
45
+ "Accept-Signature": 'sig1=("@method" "@target-uri" "@authority" "content-type" "content-digest" "signature-agent";key="sig1");created;expires;nonce;keyid;tag="web-bot-auth"'
46
+ } : status === 429 ? { "Retry-After": "60" } : {};
47
+ }
48
+ status;
49
+ reason;
50
+ headers;
51
+ get body() {
52
+ if (this.reason === "authorization_failed" || this.reason === "identity_not_bound")
53
+ return {
54
+ adcp_error: { code: "PERMISSION_DENIED", message: "Agent authorization failed", recovery: "correctable" }
55
+ };
56
+ return { adcp_error: { code: "AUTH_INVALID", message: this.message, recovery: "terminal" } };
57
+ }
58
+ /**
59
+ * The signer's identity could not be established because key discovery
60
+ * failed (WBA §6.10) or its revocation status is unknown. The request is
61
+ * unverified; servers apply their unverified-request policy.
62
+ */
63
+ get unverified() {
64
+ return this.reason.startsWith("directory_") || this.reason === "identity_unverified";
65
+ }
66
+ }
67
+ class WebBotAuthUnavailableError extends Error {
68
+ constructor(reason, cause) {
69
+ super("Web Bot Auth verification dependency is unavailable");
70
+ this.reason = reason;
71
+ this.name = "WebBotAuthUnavailableError";
72
+ if (cause !== void 0) Object.defineProperty(this, "cause", { value: cause });
73
+ }
74
+ reason;
75
+ }
76
+ function bad(reason) {
77
+ throw new WebBotAuthError(400, reason);
78
+ }
79
+ function denied(reason) {
80
+ throw new WebBotAuthError(403, reason);
81
+ }
82
+ function dictionary(value, field) {
83
+ if (value === void 0 || value === "") return denied("headers_missing");
84
+ if (value.length > 64 * 1024) return bad("headers");
85
+ try {
86
+ rejectDuplicateDictionaryKeys(value, field);
87
+ return parseDictionary(value);
88
+ } catch {
89
+ return bad("structured_field");
90
+ }
91
+ }
92
+ function selectSignatureProfile(request) {
93
+ if (getHeaderValue(request.headers, "signature-agent") !== void 0) return WEB_BOT_AUTH_PROFILE;
94
+ const input = getHeaderValue(request.headers, "signature-input");
95
+ if (input && /;\s*tag\s*=\s*"web-bot-auth"/.test(input)) return WEB_BOT_AUTH_PROFILE;
96
+ return "rfc9421";
97
+ }
98
+ function jwkThumbprint(jwk) {
99
+ let fields;
100
+ if (jwk.kty === "OKP" && typeof jwk.crv === "string" && typeof jwk.x === "string")
101
+ fields = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
102
+ else if (jwk.kty === "EC" && typeof jwk.crv === "string" && typeof jwk.x === "string" && typeof jwk.y === "string")
103
+ fields = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
104
+ else if (jwk.kty === "RSA" && typeof jwk.e === "string" && typeof jwk.n === "string")
105
+ fields = { e: jwk.e, kty: jwk.kty, n: jwk.n };
106
+ else return denied("unsupported_key");
107
+ return createHash("sha256").update(JSON.stringify(fields)).digest("base64url");
108
+ }
109
+ function algorithm(jwk) {
110
+ if (jwk.kty === "OKP" && jwk.crv === "Ed25519") return "ed25519";
111
+ if (jwk.kty === "EC" && jwk.crv === "P-256") return "ecdsa-p256-sha256";
112
+ if (jwk.kty === "RSA" && typeof jwk.n === "string" && jwk.e === "AQAB") {
113
+ const bytes = Buffer.from(jwk.n, "base64url").length;
114
+ if (bytes >= 256 && bytes <= 512) return "rsa-pss-sha512";
115
+ }
116
+ return denied("unsupported_key");
117
+ }
118
+ function inputs(headers) {
119
+ const value = getHeaderValue(headers, "signature-input");
120
+ return [...dictionary(value, "Signature-Input").keys()].map((label) => {
121
+ try {
122
+ return parseSignatureInput(value, label);
123
+ } catch {
124
+ return bad("signature_input");
125
+ }
126
+ });
127
+ }
128
+ function receivedAgentOrigin(value) {
129
+ try {
130
+ canonicalAgentUrl(value);
131
+ const origin = canonicalAgentOrigin(value);
132
+ if (value !== origin || /[^\x21-\x7e]/.test(value) || value.includes("%")) return bad("agent_origin");
133
+ return origin;
134
+ } catch {
135
+ return bad("agent_origin");
136
+ }
137
+ }
138
+ function buildWebBotAuthSignatureBase(input, request, response) {
139
+ const lines = input.components.map((component) => {
140
+ const bare = component.split(";", 1)[0];
141
+ let identifier;
142
+ try {
143
+ identifier = parseItem(`"${bare}"${component.slice(bare.length)}`);
144
+ } catch {
145
+ return bad("component");
146
+ }
147
+ const params = identifier[1];
148
+ for (const [key, value2] of params) {
149
+ if (key !== "key" && key !== "req" || key === "key" && typeof value2 !== "string" || key === "req" && value2 !== true)
150
+ return bad("component_parameter");
151
+ }
152
+ if (params.has("req") && !response) return bad("request_bound_component");
153
+ let value;
154
+ const source = response && !params.has("req") ? response.headers : request.headers;
155
+ if (bare.startsWith("@")) {
156
+ if (params.has("key")) return bad("derived_dictionary");
157
+ if (response && bare !== "@status" && !params.has("req")) return bad("response_component");
158
+ if (bare === "@method") value = request.method.toUpperCase();
159
+ else if (bare === "@target-uri") value = receivedTargetUri(request.url);
160
+ else if (bare === "@authority") value = receivedAuthority(request.url);
161
+ else if (bare === "@status" && response && !params.has("req")) value = String(response.status);
162
+ else return bad("derived_component");
163
+ } else {
164
+ value = getHeaderValue(source, bare);
165
+ if (params.has("key")) {
166
+ const member = dictionary(value, bare).get(params.get("key"));
167
+ if (!member) return denied("missing_member");
168
+ if (Array.isArray(member[0])) {
169
+ const selected = parseSignatureInput(value, params.get("key"));
170
+ value = selected.signatureParamsValue;
171
+ } else value = serializeItem(member);
172
+ }
173
+ }
174
+ if (value === void 0) return denied("missing_component");
175
+ if (/[\r\n]/.test(value)) return bad("component_value");
176
+ return `${serializeItem(identifier)}: ${value}`;
177
+ });
178
+ return [...lines, `"@signature-params": ${input.signatureParamsValue}`].join("\n");
179
+ }
180
+ function receivedTargetUri(url) {
181
+ try {
182
+ new URL(url);
183
+ } catch {
184
+ return bad("target_uri");
185
+ }
186
+ return url;
187
+ }
188
+ function receivedAuthority(url) {
189
+ try {
190
+ return new URL(url).host.toLowerCase();
191
+ } catch {
192
+ return bad("target_uri");
193
+ }
194
+ }
195
+ function assertDigest(headers, body) {
196
+ const digest = getHeaderValue(headers, "content-digest");
197
+ if (!digest || !contentDigestUsesEncoding(digest, "rfc8941-base64") || !contentDigestMatches(digest, body))
198
+ denied("content_digest");
199
+ }
200
+ function verifyBytes(input, request, jwk, response) {
201
+ const alg = algorithm(jwk);
202
+ if (input.params.alg !== void 0 && input.params.alg !== alg) denied("algorithm");
203
+ const signature = getHeaderValue(response?.headers ?? request.headers, "signature");
204
+ if (signature === void 0 || signature === "") denied("headers_missing");
205
+ let bytes;
206
+ try {
207
+ bytes = parseSignature(signature ?? "", input.label, "rfc8941-base64").bytes;
208
+ } catch {
209
+ return bad("signature");
210
+ }
211
+ let valid = false;
212
+ try {
213
+ valid = verifySignature(
214
+ alg,
215
+ jwkToPublicKey(jwk),
216
+ Buffer.from(buildWebBotAuthSignatureBase(input, request, response)),
217
+ bytes
218
+ );
219
+ } catch (error) {
220
+ if (error instanceof WebBotAuthError) throw error;
221
+ }
222
+ if (!valid) denied("signature_invalid");
223
+ }
224
+ const legacyOriginGates = /* @__PURE__ */ new WeakMap();
225
+ function registerWebBotAuthLegacyOriginGate(resolver, gate) {
226
+ legacyOriginGates.set(resolver, gate);
227
+ }
228
+ function getWebBotAuthLegacyOriginGate(resolver) {
229
+ return resolver && legacyOriginGates.get(resolver);
230
+ }
231
+ const revocationCheckingResolvers = /* @__PURE__ */ new WeakSet();
232
+ function registerWebBotAuthRevocationCheck(resolver) {
233
+ revocationCheckingResolvers.add(resolver);
234
+ }
235
+ function isWebBotAuthRevocationChecked(resolver) {
236
+ return !!resolver && revocationCheckingResolvers.has(resolver);
237
+ }
238
+ function webBotAuthUnavailableResponse() {
239
+ return {
240
+ status: 503,
241
+ headers: { "Retry-After": "60" },
242
+ body: {
243
+ adcp_error: {
244
+ code: "SERVICE_UNAVAILABLE",
245
+ message: "Agent identity verification is temporarily unavailable",
246
+ recovery: "transient"
247
+ }
248
+ }
249
+ };
250
+ }
251
+ const authorizationFailures = /* @__PURE__ */ new WeakSet();
252
+ const AUTHORIZATION_FAILURE_MESSAGES = {
253
+ key_revoked: "Agent identity key is revoked",
254
+ identity_not_bound: "Agent identity is not onboarded for this operation",
255
+ revocation_unavailable: "Identity revocation information is unavailable",
256
+ authorization_failed: "Agent authorization failed",
257
+ authorization_unavailable: "Agent authorization is temporarily unavailable"
258
+ };
259
+ function createWebBotAuthAuthorizationFailure(reason) {
260
+ const transient = reason === "revocation_unavailable" || reason === "authorization_unavailable";
261
+ const failure = Object.freeze({
262
+ code: transient ? "SERVICE_UNAVAILABLE" : "PERMISSION_DENIED",
263
+ message: AUTHORIZATION_FAILURE_MESSAGES[reason],
264
+ recovery: transient ? "transient" : "correctable"
265
+ });
266
+ authorizationFailures.add(failure);
267
+ return failure;
268
+ }
269
+ function principalFailureIsTransient(error) {
270
+ const value = error;
271
+ if (value?.transient === true || value?.recovery === "transient" || value?.code === "SERVICE_UNAVAILABLE")
272
+ return true;
273
+ return typeof value?.code !== "string";
274
+ }
275
+ function webBotAuthPrincipalFailureError(error) {
276
+ return principalFailureIsTransient(error) ? new WebBotAuthUnavailableError("authorization_unavailable", error) : new WebBotAuthError(403, "authorization_failed");
277
+ }
278
+ function deferWebBotAuthPrincipalFailure(signers, error) {
279
+ const signer = signers[signers.length - 1];
280
+ const authorizationFailure = createWebBotAuthAuthorizationFailure(
281
+ principalFailureIsTransient(error) ? "authorization_unavailable" : "authorization_failed"
282
+ );
283
+ return {
284
+ principal: `anonymous:${createHash("sha256").update(signer.origin).digest("hex")}`,
285
+ authorizationFailure
286
+ };
287
+ }
288
+ function getWebBotAuthAuthorizationFailure(extra) {
289
+ for (const value of [extra?.webBotAuthAuthorization, extra?.identity33]) {
290
+ const claim = value;
291
+ if (claim && isVerifiedWebBotSigner(claim.signer) && claim.authorizationFailure && authorizationFailures.has(claim.authorizationFailure))
292
+ return claim.authorizationFailure;
293
+ }
294
+ return void 0;
295
+ }
296
+ function webBotAuthToolFailure(failure, version) {
297
+ return {
298
+ isError: true,
299
+ content: [
300
+ {
301
+ type: "text",
302
+ text: JSON.stringify({
303
+ adcp_error: failure,
304
+ status: "failed",
305
+ ...version && { adcp_version: toReleasePrecisionVersion(version) }
306
+ })
307
+ }
308
+ ]
309
+ };
310
+ }
311
+ const verifiedSigners = /* @__PURE__ */ new WeakSet();
312
+ function isVerifiedWebBotSigner(value) {
313
+ return typeof value === "object" && value !== null && verifiedSigners.has(value);
314
+ }
315
+ async function verifyWebBotAuth(request, options) {
316
+ try {
317
+ if (!(options.directory instanceof SignatureDirectoryResolver)) denied("directory_unverified");
318
+ const now = Math.floor((options.now ?? (() => Date.now() / 1e3))());
319
+ if (!Number.isFinite(now)) denied("verification_unavailable");
320
+ const all = inputs(request.headers);
321
+ const candidates = all.filter((input) => input.params.tag === WEB_BOT_AUTH_PROFILE);
322
+ const selected = [];
323
+ const remaining = [...candidates];
324
+ while (remaining.length) {
325
+ const index = remaining.findIndex(
326
+ (input) => candidates.every(
327
+ (other) => !input.components.some(
328
+ (c) => c === `signature-input;key="${other.label}"` || c === `signature;key="${other.label}"`
329
+ ) || selected.some((done) => done.label === other.label)
330
+ )
331
+ );
332
+ if (index < 0) denied("relay_cycle");
333
+ selected.push(remaining.splice(index, 1)[0]);
334
+ }
335
+ if (!selected.length || selected.length > 8) denied("profile");
336
+ const agents = dictionary(getHeaderValue(request.headers, "signature-agent"), "Signature-Agent");
337
+ const verified = [];
338
+ const nonces = /* @__PURE__ */ new Set();
339
+ for (const input of selected) {
340
+ const { created, expires, nonce, keyid } = input.params;
341
+ if ([created, expires, nonce, keyid].some((value) => value === void 0)) denied("parameters_missing");
342
+ if (!Number.isInteger(created) || !Number.isInteger(expires) || typeof nonce !== "string" || !/^[A-Za-z0-9_+\/-]{22,256}={0,2}$/.test(nonce) || typeof keyid !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(keyid))
343
+ bad("parameters");
344
+ if (expires <= created || expires - created > 300 || created > now + 60 || expires < now - 60) denied("window");
345
+ const agentComponents = input.components.filter(
346
+ (c) => c.startsWith("signature-agent;key=") && !verified.some((s) => selected.find((i) => i.label === s.label).components.includes(c))
347
+ );
348
+ if (agentComponents.length !== 1) denied("agent_not_covered");
349
+ const component = parseItem(`"${agentComponents[0].replace("signature-agent;", 'signature-agent";')}`);
350
+ const memberKey = component[1].get("key");
351
+ if (typeof memberKey !== "string" || memberKey !== input.label) bad("agent_member");
352
+ const member = agents.get(memberKey);
353
+ if (!member) denied("agent_not_covered");
354
+ if (typeof member[0] !== "string") bad("signature_agent");
355
+ const type = member[1].get("type");
356
+ if (type !== void 0 && type !== "directory") denied("agent_type");
357
+ const origin = receivedAgentOrigin(member[0]);
358
+ for (const earlier of verified) {
359
+ const inner = selected.find((i) => i.label === earlier.label);
360
+ const required = [
361
+ ...inner.components,
362
+ `signature-input;key="${inner.label}"`,
363
+ `signature;key="${inner.label}"`
364
+ ];
365
+ if (required.some((c) => !input.components.includes(c))) denied("relay_coverage");
366
+ }
367
+ if (new Set(input.components).size !== input.components.length || ["@method", "@target-uri", "@authority"].some((c) => !input.components.includes(c)))
368
+ denied("coverage");
369
+ if (request.body !== void 0 && request.body !== "") {
370
+ if (["content-type", "content-digest"].some((c) => !input.components.includes(c))) denied("body_coverage");
371
+ assertDigest(request.headers, request.body);
372
+ }
373
+ await options.directory.resolve(keyid, origin);
374
+ const checkedDirectory = options.directory.getVerifiedDirectory(origin);
375
+ if (!checkedDirectory || new URL(checkedDirectory.proof.request.url).origin !== origin)
376
+ denied("directory_origin");
377
+ if (checkedDirectory.expires <= now) denied("directory_expired");
378
+ if (!testKeysAllowed(options.allowTestKeys ?? options.directory.allowsTestKeys) && checkedDirectory.keys.some((key2) => TEST_KIDS.has(key2.kid)))
379
+ denied("directory_key");
380
+ const key = checkedDirectory.keys.find((key2) => key2.kid === keyid);
381
+ if (!key || jwkThumbprint(key) !== keyid) denied("key");
382
+ verifyBytes(input, request, key);
383
+ if (options.isRevoked) await legacyRevocationCheck(options, origin, keyid);
384
+ const nonceTuple = JSON.stringify([origin, nonce]);
385
+ if (nonces.has(nonceTuple)) throw new WebBotAuthError(options.replayStatus ?? 429, "nonce_replayed");
386
+ nonces.add(nonceTuple);
387
+ const signer = Object.freeze({
388
+ profile: WEB_BOT_AUTH_PROFILE,
389
+ origin,
390
+ keyid,
391
+ label: input.label,
392
+ verified_at: now
393
+ });
394
+ verified.push(signer);
395
+ }
396
+ for (const signer of verified) {
397
+ const input = selected.find((i) => i.label === signer.label);
398
+ let result;
399
+ try {
400
+ result = await options.replayStore.insert(
401
+ signer.origin,
402
+ WEB_BOT_AUTH_PROFILE,
403
+ input.params.nonce,
404
+ // Acceptance is inclusive of expires + 60, so the nonce must outlive it.
405
+ input.params.expires + 60 - now + 1,
406
+ now
407
+ );
408
+ } catch (cause) {
409
+ throw new WebBotAuthUnavailableError("replay_unavailable", cause);
410
+ }
411
+ if (result !== "ok")
412
+ throw new WebBotAuthError(result === "replayed" ? options.replayStatus ?? 429 : 429, result);
413
+ }
414
+ for (const signer of verified) verifiedSigners.add(signer);
415
+ return verified;
416
+ } catch (error) {
417
+ if (error instanceof WebBotAuthError) {
418
+ if (error.headers["Accept-Signature"]) {
419
+ let label = "sig1";
420
+ try {
421
+ label = inputs(request.headers)[0]?.label ?? "sig1";
422
+ } catch {
423
+ }
424
+ const bodyComponents = request.body ? '"content-type" "content-digest" ' : "";
425
+ error.headers["Accept-Signature"] = `${label}=("@method" "@target-uri" "@authority" ${bodyComponents}"signature-agent";key="${label}");created;expires;nonce;keyid;tag="web-bot-auth"`;
426
+ }
427
+ throw error;
428
+ }
429
+ if (error instanceof WebBotAuthUnavailableError) throw error;
430
+ return denied("verification_unavailable");
431
+ }
432
+ }
433
+ async function legacyRevocationCheck(options, origin, keyid) {
434
+ let revoked;
435
+ try {
436
+ revoked = await options.isRevoked(origin, keyid);
437
+ } catch (error) {
438
+ if (error instanceof WebBotAuthUnavailableError) throw error;
439
+ if (error instanceof WebBotAuthError && error.reason === "revocation_persistence_failed")
440
+ throw new WebBotAuthUnavailableError("revocation_persistence_failed", error);
441
+ if (error instanceof WebBotAuthError && error.reason === "key_revoked") return denied("key_revoked");
442
+ if (options.requireFreshRevocation === false && error instanceof WebBotAuthError && (error.reason === "revocation_unavailable" || error.reason === "revocation_absent"))
443
+ return;
444
+ return denied("identity_unverified");
445
+ }
446
+ if (revoked) denied("key_revoked");
447
+ }
448
+ function testKeysAllowed(allowTestKeys) {
449
+ return allowTestKeys === true && ["test", "development"].includes(process.env.NODE_ENV ?? "");
450
+ }
451
+ const UNSIGNED_DIRECTORY_MEDIA_TYPES = /* @__PURE__ */ new Set([
452
+ SIGNATURE_DIRECTORY_MEDIA_TYPE,
453
+ "application/jwk-set+json",
454
+ "application/json"
455
+ ]);
456
+ function verifyKeyDirectory(response, now = Date.now() / 1e3, options = {}) {
457
+ try {
458
+ if (!Number.isFinite(now)) denied("directory_invalid");
459
+ const origin = canonicalAgentOrigin(response.request.url);
460
+ if (response.request.url !== origin + SIGNATURE_DIRECTORY_PATH || response.status !== 200)
461
+ denied("directory_response");
462
+ const signed = getHeaderValue(response.headers, "signature-input") !== void 0;
463
+ if (!signed && options.requireSigned) denied("directory_unsigned");
464
+ const mediaType = getHeaderValue(response.headers, "content-type")?.split(";")[0]?.trim().toLowerCase();
465
+ if (signed ? mediaType !== SIGNATURE_DIRECTORY_MEDIA_TYPE : !UNSIGNED_DIRECTORY_MEDIA_TYPES.has(mediaType ?? ""))
466
+ denied("directory_response");
467
+ const body = response.body ?? "";
468
+ if (Buffer.byteLength(body) > 256 * 1024) denied("directory_size");
469
+ if (signed || getHeaderValue(response.headers, "content-digest") !== void 0)
470
+ assertDirectoryDigest(response.headers, body);
471
+ const document = parseStrictJson(body, 32);
472
+ if (!Array.isArray(document.keys) || !document.keys.length || document.keys.length > 64) denied("directory_keys");
473
+ const signatures = signed ? inputs(response.headers) : [];
474
+ const kids = /* @__PURE__ */ new Set();
475
+ const keys = [];
476
+ let expires = Infinity;
477
+ for (const raw of document.keys) {
478
+ if (!raw || typeof raw !== "object") denied("directory_key");
479
+ const thumbprint = jwkThumbprint(raw);
480
+ const key = { ...raw, kid: raw.kid ?? thumbprint };
481
+ if (key.kid !== thumbprint || kids.has(key.kid) || key.d !== void 0 || Object.keys(key).some((k) => k.startsWith("_private")) || key.adcp_use !== void 0 || !testKeysAllowed(options.allowTestKeys) && TEST_KIDS.has(key.kid))
482
+ denied("directory_key");
483
+ algorithm(key);
484
+ kids.add(key.kid);
485
+ keys.push(key);
486
+ if (!signed) continue;
487
+ const proof = signatures.find((input) => input.params.keyid === key.kid && input.params.tag === DIRECTORY_TAG);
488
+ if (!proof || !proof.components.includes("@authority;req") || !proof.components.includes("content-digest") || !Number.isInteger(proof.params.created) || !Number.isInteger(proof.params.expires) || proof.params.created > now + DIRECTORY_CLOCK_SKEW_SECONDS || proof.params.expires <= now || proof.params.expires <= proof.params.created)
489
+ denied("directory_proof");
490
+ verifyBytes(proof, { ...response.request, headers: response.request.headers ?? {} }, key, response);
491
+ expires = Math.min(expires, proof.params.expires);
492
+ }
493
+ if (!signed) expires = now + unsignedAge(options.maxUnsignedAgeSeconds);
494
+ return { keys, expires, signed, proof: response };
495
+ } catch (error) {
496
+ if (error instanceof WebBotAuthError) {
497
+ throw error.reason.startsWith("directory_") ? error : new WebBotAuthError(403, `directory_${error.reason}`);
498
+ }
499
+ return denied("directory_invalid");
500
+ }
501
+ }
502
+ function unsignedAge(seconds) {
503
+ const value = seconds ?? 86400;
504
+ if (!Number.isFinite(value) || value < 60)
505
+ throw new TypeError("Unsigned directory age must be a finite number of seconds, at least 60");
506
+ return value;
507
+ }
508
+ function verifySignatureDirectory(response, now = Date.now() / 1e3, allowTestKeys = false) {
509
+ return verifyKeyDirectory(response, now, { allowTestKeys, requireSigned: true });
510
+ }
511
+ function assertDirectoryDigest(headers, body) {
512
+ try {
513
+ assertDigest(headers, body);
514
+ } catch {
515
+ denied("directory_digest");
516
+ }
517
+ }
518
+ class SignatureDirectoryResolver {
519
+ constructor(options = {}) {
520
+ this.options = options;
521
+ unsignedAge(options.maxUnsignedDirectoryAgeSeconds);
522
+ }
523
+ options;
524
+ cache = /* @__PURE__ */ new Map();
525
+ failures = /* @__PURE__ */ new Map();
526
+ keyMissCooldown = /* @__PURE__ */ new Map();
527
+ pending = /* @__PURE__ */ new Map();
528
+ async resolve(keyid, signerOrigin) {
529
+ if (!signerOrigin) return denied("origin_required");
530
+ const origin = receivedAgentOrigin(signerOrigin);
531
+ const now = (this.options.now ?? (() => Date.now() / 1e3))();
532
+ const cached = this.cache.get(origin);
533
+ const usable = () => cached && cached.directory.expires > now ? cached.directory : void 0;
534
+ if (cached && cached.freshUntil > now && cached.directory.expires > now) {
535
+ const key = cached.directory.keys.find((k) => k.kid === keyid);
536
+ if (key || (this.keyMissCooldown.get(origin) ?? 0) > now) return key ?? null;
537
+ this.keyMissCooldown.set(origin, now + 60);
538
+ }
539
+ const fromFallback = () => {
540
+ const key = usable()?.keys.find((k) => k.kid === keyid);
541
+ return key ?? denied("directory_unavailable");
542
+ };
543
+ if ((this.failures.get(origin) ?? 0) > now) return fromFallback();
544
+ let pending = this.pending.get(origin);
545
+ if (!pending) {
546
+ if ([...this.pending.keys()].filter((pendingOrigin) => eTldPlusOne(pendingOrigin) === eTldPlusOne(origin)).length >= 2 || this.pending.size >= (this.options.maxConcurrentFetches ?? 16)) {
547
+ const key = usable()?.keys.find((k) => k.kid === keyid);
548
+ if (key) return key;
549
+ throw new WebBotAuthError(429, "directory_fetch_limit");
550
+ }
551
+ pending = this.fetch(origin, now);
552
+ this.pending.set(origin, pending);
553
+ }
554
+ try {
555
+ return (await pending).keys.find((k) => k.kid === keyid) ?? null;
556
+ } catch (error) {
557
+ if (usable()) return fromFallback();
558
+ throw error;
559
+ } finally {
560
+ if (this.pending.get(origin) === pending) this.pending.delete(origin);
561
+ }
562
+ }
563
+ async fetch(origin, now) {
564
+ try {
565
+ const url = origin + SIGNATURE_DIRECTORY_PATH;
566
+ const result = await ssrfSafeFetch(url, {
567
+ ...this.options.fetchOptions,
568
+ headers: {
569
+ accept: `${SIGNATURE_DIRECTORY_MEDIA_TYPE}, application/jwk-set+json;q=0.9, application/json;q=0.8`
570
+ },
571
+ timeoutMs: 1e4,
572
+ maxBodyBytes: 256 * 1024
573
+ });
574
+ if (result.status !== 200) denied("directory_unavailable");
575
+ const body = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(result.body);
576
+ const directory = verifyKeyDirectory(
577
+ { status: result.status, headers: result.headers, body, request: { method: "GET", url } },
578
+ now,
579
+ {
580
+ allowTestKeys: this.options.allowTestKeys,
581
+ requireSigned: this.options.requireSignedDirectories,
582
+ maxUnsignedAgeSeconds: this.options.maxUnsignedDirectoryAgeSeconds
583
+ }
584
+ );
585
+ const cacheControl = result.headers["cache-control"] ?? "";
586
+ const maxAge = /(?:^|,)\s*(?:no-cache|no-store)(?:,|$)/i.test(cacheControl) ? 0 : Number(cacheControl.match(/(?:^|,)\s*max-age=(\d+)/i)?.[1] ?? 300);
587
+ const age = Number(result.headers.age ?? 0);
588
+ if (!Number.isFinite(age) || age < 0) denied("directory_invalid");
589
+ if (this.cache.size >= (this.options.maxOrigins ?? 1e3) && !this.cache.has(origin)) {
590
+ const oldest = this.cache.keys().next().value;
591
+ this.cache.delete(oldest);
592
+ this.keyMissCooldown.delete(oldest);
593
+ }
594
+ this.cache.set(origin, {
595
+ directory,
596
+ freshUntil: Math.min(directory.expires, now + Math.max(0, Math.min(maxAge - age, 300)))
597
+ });
598
+ this.failures.delete(origin);
599
+ return directory;
600
+ } catch (error) {
601
+ if (this.failures.size >= (this.options.maxOrigins ?? 1e3))
602
+ this.failures.delete(this.failures.keys().next().value);
603
+ this.failures.set(origin, now + 60);
604
+ throw error instanceof WebBotAuthError ? error : new WebBotAuthError(403, "directory_unavailable");
605
+ }
606
+ }
607
+ get allowsTestKeys() {
608
+ return testKeysAllowed(this.options.allowTestKeys);
609
+ }
610
+ /** The directory currently used to verify `origin`, including one kept through a failed refresh. */
611
+ getVerifiedDirectory(origin) {
612
+ return this.cache.get(receivedAgentOrigin(origin))?.directory;
613
+ }
614
+ getAuditProof(origin) {
615
+ return this.getVerifiedDirectory(origin)?.proof;
616
+ }
617
+ }
618
+ async function signBase(base, key) {
619
+ const jwk = "privateKey" in key ? key.privateKey : key.publicKey;
620
+ const alg = algorithm(jwk);
621
+ if (!["test", "development"].includes(process.env.NODE_ENV ?? "") && TEST_KIDS.has(jwkThumbprint(jwk)))
622
+ throw new TypeError("Published Web Bot Auth test keys cannot sign in production");
623
+ if ("provider" in key) {
624
+ if (key.provider.keyid !== jwkThumbprint(jwk) || key.provider.algorithm !== alg)
625
+ throw new TypeError("Web Bot Auth provider identity does not match its public key");
626
+ return key.provider.sign(Buffer.from(base));
627
+ }
628
+ const privateKey = createPrivateKey({ key: jwk, format: "jwk" });
629
+ if (alg === "rsa-pss-sha512")
630
+ return nodeSign("sha512", Buffer.from(base), {
631
+ key: privateKey,
632
+ padding: cryptoConstants.RSA_PKCS1_PSS_PADDING,
633
+ saltLength: 64
634
+ });
635
+ return alg === "ed25519" ? nodeSign(null, Buffer.from(base), privateKey) : nodeSign("sha256", Buffer.from(base), { key: privateKey, dsaEncoding: "ieee-p1363" });
636
+ }
637
+ async function signWebBotAuth(request, key, options) {
638
+ const origin = canonicalAgentOrigin(options.origin);
639
+ const label = options.label ?? "sig1";
640
+ if (!/^[a-z][a-z0-9_.*-]*$/.test(label)) throw new TypeError("Invalid signature label");
641
+ const jwk = "privateKey" in key ? key.privateKey : key.publicKey;
642
+ const now = Math.floor((options.now ?? (() => Date.now() / 1e3))());
643
+ const window = options.windowSeconds ?? 300;
644
+ const nonce = options.nonce ?? randomBytes(64).toString("base64url");
645
+ if (!Number.isInteger(window) || window < 1 || window > 300 || !/^[A-Za-z0-9_+\/-]{22,256}={0,2}$/.test(nonce))
646
+ throw new TypeError("Invalid Web Bot Auth lifetime or nonce");
647
+ if (!options.relay && ["signature", "signature-input", "signature-agent"].some(
648
+ (name) => getHeaderValue(request.headers, name) !== void 0
649
+ ))
650
+ throw new TypeError(
651
+ "Request already carries signature fields; pass relay: true to add a covering signature instead of replacing them"
652
+ );
653
+ const headers = {};
654
+ for (const [name, value] of Object.entries(request.headers))
655
+ if (value !== void 0 && !["signature", "signature-input", "signature-agent", "content-digest"].includes(name.toLowerCase()))
656
+ headers[name.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
657
+ const components = ['"@method"', '"@target-uri"', '"@authority"'];
658
+ if (request.body !== void 0 && request.body !== "") {
659
+ if (!getHeaderValue(headers, "content-type"))
660
+ throw new TypeError("Body-bearing Web Bot Auth requests need Content-Type");
661
+ headers["content-digest"] = computeContentDigest(request.body, "rfc8941-base64");
662
+ components.push('"content-type"', '"content-digest"');
663
+ }
664
+ if (options.relay && ["signature", "signature-input", "signature-agent"].some((name) => !getHeaderValue(request.headers, name)))
665
+ throw new TypeError("Relay requires the existing signature fields");
666
+ const earlier = options.relay ? inputs(request.headers) : [];
667
+ if (earlier.some((input2) => input2.label === label || input2.params.tag !== WEB_BOT_AUTH_PROFILE))
668
+ throw new TypeError("Relay requires existing Web Bot Auth signatures and a fresh label");
669
+ const priorAgent = options.relay ? getHeaderValue(request.headers, "signature-agent") : void 0;
670
+ if (priorAgent) dictionary(priorAgent, "Signature-Agent");
671
+ headers["signature-agent"] = `${priorAgent ? priorAgent + ", " : ""}${label}="${origin}"`;
672
+ components.push(`"signature-agent";key="${label}"`);
673
+ for (const inner of earlier) {
674
+ for (const component of inner.components) {
675
+ const bare = component.split(";", 1)[0];
676
+ const encoded = `"${bare}"${component.slice(bare.length)}`;
677
+ if (!components.includes(encoded)) components.push(encoded);
678
+ }
679
+ components.push(`"signature-input";key="${inner.label}"`, `"signature";key="${inner.label}"`);
680
+ }
681
+ const priorInput = options.relay ? getHeaderValue(request.headers, "signature-input") : void 0;
682
+ const priorSignature = options.relay ? getHeaderValue(request.headers, "signature") : void 0;
683
+ if (options.relay && (!priorInput || !priorSignature || !priorAgent))
684
+ throw new TypeError("Relay requires the existing signature fields");
685
+ headers["signature-input"] = `${priorInput ? priorInput + ", " : ""}${label}=(${components.join(" ")});created=${now};expires=${now + window};nonce="${nonce}";keyid="${jwkThumbprint(jwk)}";alg="${algorithm(jwk)}";tag="web-bot-auth"`;
686
+ const input = parseSignatureInput(headers["signature-input"], label);
687
+ if (priorSignature) headers.signature = priorSignature;
688
+ const bytes = await signBase(
689
+ buildWebBotAuthSignatureBase(input, { ...request, url: new URL(request.url).href, headers }),
690
+ key
691
+ );
692
+ headers.signature = `${priorSignature ? priorSignature + ", " : ""}${label}=:${Buffer.from(bytes).toString("base64")}:`;
693
+ return headers;
694
+ }
695
+ async function createSignedSignatureDirectory(originUrl, signingKeys, options = {}) {
696
+ const origin = canonicalAgentOrigin(originUrl);
697
+ const now = Math.floor((options.now ?? (() => Date.now() / 1e3))());
698
+ const lifetime = options.lifetimeSeconds ?? 86400;
699
+ if (!Number.isFinite(now) || !Number.isSafeInteger(lifetime) || lifetime < 1 || !Number.isSafeInteger(now + lifetime))
700
+ throw new TypeError("Invalid directory proof clock or lifetime");
701
+ if (!signingKeys.length || signingKeys.length > 64 || !Number.isInteger(lifetime) || lifetime < 1)
702
+ throw new TypeError("Invalid directory keys or lifetime");
703
+ const keys = signingKeys.map((key) => {
704
+ const jwk = "privateKey" in key ? key.privateKey : key.publicKey;
705
+ return {
706
+ kty: jwk.kty,
707
+ ...jwk.kty === "RSA" ? { n: jwk.n, e: jwk.e } : { crv: jwk.crv, x: jwk.x },
708
+ ...jwk.y ? { y: jwk.y } : {},
709
+ kid: jwkThumbprint(jwk),
710
+ use: "sig"
711
+ };
712
+ });
713
+ if (new Set(keys.map((k) => k.kid)).size !== keys.length) throw new TypeError("Duplicate directory key");
714
+ const body = JSON.stringify({ keys });
715
+ const response = {
716
+ status: 200,
717
+ body,
718
+ request: { method: "GET", url: origin + SIGNATURE_DIRECTORY_PATH },
719
+ headers: {
720
+ "content-type": SIGNATURE_DIRECTORY_MEDIA_TYPE,
721
+ "content-digest": computeContentDigest(body, "rfc8941-base64"),
722
+ "cache-control": `max-age=${Math.min(lifetime, 300)}`
723
+ }
724
+ };
725
+ const signatureInputs = [], signatures = [];
726
+ for (let i = 0; i < keys.length; i++) {
727
+ const label = `key${i + 1}`;
728
+ const value = `${label}=("@authority";req "content-digest");created=${now - DIRECTORY_CLOCK_SKEW_SECONDS};expires=${now + lifetime};keyid="${keys[i].kid}";alg="${algorithm(keys[i])}";tag="${DIRECTORY_TAG}"`;
729
+ const input = parseSignatureInput(value, label);
730
+ const bytes = await signBase(
731
+ buildWebBotAuthSignatureBase(input, { method: "GET", url: response.request.url, headers: {} }, response),
732
+ signingKeys[i]
733
+ );
734
+ signatureInputs.push(value);
735
+ signatures.push(`${label}=:${Buffer.from(bytes).toString("base64")}:`);
736
+ }
737
+ response.headers["signature-input"] = signatureInputs.join(", ");
738
+ response.headers.signature = signatures.join(", ");
739
+ return response;
740
+ }
741
+ function createWebBotAuthFetch(upstream, key, options) {
742
+ return async (input, init) => {
743
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
744
+ if (options.shouldSign && !options.shouldSign(url, init)) return upstream(input, init);
745
+ if (input instanceof Request)
746
+ throw new TypeError("Pass a URL and init to Web Bot Auth fetch so the exact body can be signed");
747
+ const parsed = new URL(url);
748
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.hash)
749
+ throw new TypeError("Signed targets must be HTTPS without credentials or fragments");
750
+ let body;
751
+ if (init?.body != null) {
752
+ if (typeof init.body === "string") body = init.body;
753
+ else if (init.body instanceof Uint8Array || init.body instanceof ArrayBuffer)
754
+ body = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(init.body);
755
+ else throw new TypeError("Web Bot Auth fetch requires a UTF-8 string or byte body");
756
+ }
757
+ const headers = {};
758
+ new Headers(init?.headers).forEach((value, name) => {
759
+ headers[name] = value;
760
+ });
761
+ if (body !== void 0 && !headers["content-type"]) headers["content-type"] = "application/json";
762
+ const method = init?.method ?? "GET";
763
+ const signed = await signWebBotAuth({ method, url, headers, body }, key, options);
764
+ return upstream(url, { ...init, method, body, headers: signed, redirect: "manual" });
765
+ };
766
+ }
767
+ export {
768
+ SIGNATURE_DIRECTORY_MEDIA_TYPE,
769
+ SIGNATURE_DIRECTORY_PATH,
770
+ SignatureDirectoryResolver,
771
+ WEB_BOT_AUTH_DRAFT_REVISION,
772
+ WEB_BOT_AUTH_PROFILE,
773
+ WebBotAuthError,
774
+ WebBotAuthUnavailableError,
775
+ buildWebBotAuthSignatureBase,
776
+ createSignedSignatureDirectory,
777
+ createWebBotAuthAuthorizationFailure,
778
+ createWebBotAuthFetch,
779
+ deferWebBotAuthPrincipalFailure,
780
+ getWebBotAuthAuthorizationFailure,
781
+ getWebBotAuthLegacyOriginGate,
782
+ isVerifiedWebBotSigner,
783
+ isWebBotAuthRevocationChecked,
784
+ jwkThumbprint,
785
+ receivedAgentOrigin,
786
+ registerWebBotAuthLegacyOriginGate,
787
+ registerWebBotAuthRevocationCheck,
788
+ selectSignatureProfile,
789
+ signWebBotAuth,
790
+ verifyKeyDirectory,
791
+ verifySignatureDirectory,
792
+ verifyWebBotAuth,
793
+ webBotAuthPrincipalFailureError,
794
+ webBotAuthToolFailure,
795
+ webBotAuthUnavailableResponse
796
+ };