@udibo/oauth2 0.1.0

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 (375) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +174 -0
  3. package/SECURITY.md +77 -0
  4. package/esm/_dnt.polyfills.d.ts +17 -0
  5. package/esm/_dnt.polyfills.js +15 -0
  6. package/esm/adapters/hono/_common.d.ts +66 -0
  7. package/esm/adapters/hono/_common.js +108 -0
  8. package/esm/adapters/hono/authorization-server.d.ts +211 -0
  9. package/esm/adapters/hono/authorization-server.js +214 -0
  10. package/esm/adapters/hono/bff/auth-request-store.d.ts +164 -0
  11. package/esm/adapters/hono/bff/auth-request-store.js +154 -0
  12. package/esm/adapters/hono/bff/bff.d.ts +852 -0
  13. package/esm/adapters/hono/bff/bff.js +1194 -0
  14. package/esm/adapters/hono/bff/cookie-prefix.d.ts +72 -0
  15. package/esm/adapters/hono/bff/cookie-prefix.js +85 -0
  16. package/esm/adapters/hono/bff/mod.d.ts +19 -0
  17. package/esm/adapters/hono/bff/mod.js +19 -0
  18. package/esm/adapters/hono/bff/proxy.d.ts +108 -0
  19. package/esm/adapters/hono/bff/proxy.js +314 -0
  20. package/esm/adapters/hono/bff/session-store.d.ts +250 -0
  21. package/esm/adapters/hono/bff/session-store.js +216 -0
  22. package/esm/adapters/hono/bff/testing.d.ts +165 -0
  23. package/esm/adapters/hono/bff/testing.js +254 -0
  24. package/esm/adapters/hono/identity.d.ts +117 -0
  25. package/esm/adapters/hono/identity.js +215 -0
  26. package/esm/adapters/hono/log.d.ts +49 -0
  27. package/esm/adapters/hono/log.js +66 -0
  28. package/esm/adapters/hono/resource-server.d.ts +101 -0
  29. package/esm/adapters/hono/resource-server.js +108 -0
  30. package/esm/client/_http.d.ts +91 -0
  31. package/esm/client/_http.js +303 -0
  32. package/esm/client/base.d.ts +184 -0
  33. package/esm/client/base.js +93 -0
  34. package/esm/client/bff-client.d.ts +235 -0
  35. package/esm/client/bff-client.js +280 -0
  36. package/esm/client/browser-storage.d.ts +152 -0
  37. package/esm/client/browser-storage.js +246 -0
  38. package/esm/client/check.d.ts +114 -0
  39. package/esm/client/check.js +97 -0
  40. package/esm/client/direct-client.d.ts +707 -0
  41. package/esm/client/direct-client.js +1237 -0
  42. package/esm/client/discovery-cache.d.ts +138 -0
  43. package/esm/client/discovery-cache.js +124 -0
  44. package/esm/client/events.d.ts +54 -0
  45. package/esm/client/events.js +37 -0
  46. package/esm/client/mod.d.ts +35 -0
  47. package/esm/client/mod.js +33 -0
  48. package/esm/client/storage.d.ts +127 -0
  49. package/esm/client/storage.js +81 -0
  50. package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.d.ts +29 -0
  51. package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.js +47 -0
  52. package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.d.ts +28 -0
  53. package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.js +65 -0
  54. package/esm/deps/jsr.io/@std/assert/1.0.19/assert.d.ts +16 -0
  55. package/esm/deps/jsr.io/@std/assert/1.0.19/assert.js +23 -0
  56. package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.d.ts +25 -0
  57. package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.js +30 -0
  58. package/esm/deps/jsr.io/@std/assert/1.0.19/equal.d.ts +24 -0
  59. package/esm/deps/jsr.io/@std/assert/1.0.19/equal.js +193 -0
  60. package/esm/deps/jsr.io/@std/assert/1.0.19/equals.d.ts +36 -0
  61. package/esm/deps/jsr.io/@std/assert/1.0.19/equals.js +61 -0
  62. package/esm/deps/jsr.io/@std/assert/1.0.19/exists.d.ts +17 -0
  63. package/esm/deps/jsr.io/@std/assert/1.0.19/exists.js +27 -0
  64. package/esm/deps/jsr.io/@std/assert/1.0.19/fail.d.ts +14 -0
  65. package/esm/deps/jsr.io/@std/assert/1.0.19/fail.js +20 -0
  66. package/esm/deps/jsr.io/@std/assert/1.0.19/false.d.ts +18 -0
  67. package/esm/deps/jsr.io/@std/assert/1.0.19/false.js +23 -0
  68. package/esm/deps/jsr.io/@std/assert/1.0.19/greater.d.ts +19 -0
  69. package/esm/deps/jsr.io/@std/assert/1.0.19/greater.js +29 -0
  70. package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.d.ts +19 -0
  71. package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.js +29 -0
  72. package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.d.ts +22 -0
  73. package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.js +52 -0
  74. package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.d.ts +24 -0
  75. package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.js +53 -0
  76. package/esm/deps/jsr.io/@std/assert/1.0.19/less.d.ts +18 -0
  77. package/esm/deps/jsr.io/@std/assert/1.0.19/less.js +28 -0
  78. package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.d.ts +19 -0
  79. package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.js +29 -0
  80. package/esm/deps/jsr.io/@std/assert/1.0.19/match.d.ts +17 -0
  81. package/esm/deps/jsr.io/@std/assert/1.0.19/match.js +26 -0
  82. package/esm/deps/jsr.io/@std/assert/1.0.19/mod.d.ts +43 -0
  83. package/esm/deps/jsr.io/@std/assert/1.0.19/mod.js +45 -0
  84. package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.d.ts +26 -0
  85. package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.js +39 -0
  86. package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.d.ts +19 -0
  87. package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.js +29 -0
  88. package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.d.ts +17 -0
  89. package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.js +26 -0
  90. package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.d.ts +22 -0
  91. package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.js +32 -0
  92. package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.d.ts +28 -0
  93. package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.js +177 -0
  94. package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.d.ts +41 -0
  95. package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.js +53 -0
  96. package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.d.ts +23 -0
  97. package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.js +58 -0
  98. package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.d.ts +17 -0
  99. package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.js +26 -0
  100. package/esm/deps/jsr.io/@std/assert/1.0.19/throws.d.ts +44 -0
  101. package/esm/deps/jsr.io/@std/assert/1.0.19/throws.js +44 -0
  102. package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.d.ts +14 -0
  103. package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.js +20 -0
  104. package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.d.ts +14 -0
  105. package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.js +20 -0
  106. package/esm/deps/jsr.io/@std/async/1.5.0/delay.d.ts +44 -0
  107. package/esm/deps/jsr.io/@std/async/1.5.0/delay.js +84 -0
  108. package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.d.ts +39 -0
  109. package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.js +61 -0
  110. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.d.ts +22 -0
  111. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.js +51 -0
  112. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.d.ts +34 -0
  113. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.js +192 -0
  114. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.d.ts +34 -0
  115. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.js +113 -0
  116. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.d.ts +3 -0
  117. package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.js +13 -0
  118. package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.d.ts +8 -0
  119. package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.js +2 -0
  120. package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.d.ts +1 -0
  121. package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.js +26 -0
  122. package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.d.ts +60 -0
  123. package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.js +152 -0
  124. package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.d.ts +39 -0
  125. package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.js +87 -0
  126. package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.d.ts +39 -0
  127. package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.js +131 -0
  128. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.d.ts +39 -0
  129. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.js +82 -0
  130. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.d.ts +39 -0
  131. package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.js +72 -0
  132. package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.d.ts +38 -0
  133. package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.js +87 -0
  134. package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.d.ts +97 -0
  135. package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.js +99 -0
  136. package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.d.ts +119 -0
  137. package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.js +209 -0
  138. package/esm/deps/jsr.io/@std/http/1.1.3/status.d.ts +343 -0
  139. package/esm/deps/jsr.io/@std/http/1.1.3/status.js +343 -0
  140. package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.d.ts +159 -0
  141. package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.js +223 -0
  142. package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.d.ts +79 -0
  143. package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.js +116 -0
  144. package/esm/deps/jsr.io/@std/internal/1.0.14/diff.d.ts +139 -0
  145. package/esm/deps/jsr.io/@std/internal/1.0.14/diff.js +277 -0
  146. package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.d.ts +98 -0
  147. package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.js +181 -0
  148. package/esm/deps/jsr.io/@std/internal/1.0.14/format.d.ts +29 -0
  149. package/esm/deps/jsr.io/@std/internal/1.0.14/format.js +68 -0
  150. package/esm/deps/jsr.io/@std/internal/1.0.14/styles.d.ts +158 -0
  151. package/esm/deps/jsr.io/@std/internal/1.0.14/styles.js +206 -0
  152. package/esm/deps/jsr.io/@std/internal/1.0.14/types.d.ts +24 -0
  153. package/esm/deps/jsr.io/@std/internal/1.0.14/types.js +3 -0
  154. package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.d.ts +91 -0
  155. package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.js +347 -0
  156. package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.d.ts +382 -0
  157. package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.js +1110 -0
  158. package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.d.ts +363 -0
  159. package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.js +616 -0
  160. package/esm/errors.d.ts +203 -0
  161. package/esm/errors.js +287 -0
  162. package/esm/identity/captcha.d.ts +147 -0
  163. package/esm/identity/captcha.js +64 -0
  164. package/esm/identity/delivery.d.ts +96 -0
  165. package/esm/identity/delivery.js +41 -0
  166. package/esm/identity/errors.d.ts +39 -0
  167. package/esm/identity/errors.js +46 -0
  168. package/esm/identity/events.d.ts +242 -0
  169. package/esm/identity/events.js +39 -0
  170. package/esm/identity/external/_shared.d.ts +86 -0
  171. package/esm/identity/external/_shared.js +107 -0
  172. package/esm/identity/external/_token-exchange.d.ts +61 -0
  173. package/esm/identity/external/_token-exchange.js +179 -0
  174. package/esm/identity/external/apple-client-secret.d.ts +82 -0
  175. package/esm/identity/external/apple-client-secret.js +133 -0
  176. package/esm/identity/external/apple.d.ts +64 -0
  177. package/esm/identity/external/apple.js +248 -0
  178. package/esm/identity/external/discord.d.ts +38 -0
  179. package/esm/identity/external/discord.js +62 -0
  180. package/esm/identity/external/errors.d.ts +48 -0
  181. package/esm/identity/external/errors.js +36 -0
  182. package/esm/identity/external/flow.d.ts +107 -0
  183. package/esm/identity/external/flow.js +158 -0
  184. package/esm/identity/external/github.d.ts +42 -0
  185. package/esm/identity/external/github.js +164 -0
  186. package/esm/identity/external/google.d.ts +52 -0
  187. package/esm/identity/external/google.js +38 -0
  188. package/esm/identity/external/mod.d.ts +63 -0
  189. package/esm/identity/external/mod.js +61 -0
  190. package/esm/identity/external/oauth2.d.ts +110 -0
  191. package/esm/identity/external/oauth2.js +158 -0
  192. package/esm/identity/external/oidc.d.ts +81 -0
  193. package/esm/identity/external/oidc.js +250 -0
  194. package/esm/identity/external/provider.d.ts +132 -0
  195. package/esm/identity/external/provider.js +12 -0
  196. package/esm/identity/hibp.d.ts +67 -0
  197. package/esm/identity/hibp.js +87 -0
  198. package/esm/identity/identifier.d.ts +44 -0
  199. package/esm/identity/identifier.js +47 -0
  200. package/esm/identity/lockout.d.ts +155 -0
  201. package/esm/identity/lockout.js +117 -0
  202. package/esm/identity/mfa/mod.d.ts +42 -0
  203. package/esm/identity/mfa/mod.js +42 -0
  204. package/esm/identity/mfa/recovery-codes.d.ts +96 -0
  205. package/esm/identity/mfa/recovery-codes.js +80 -0
  206. package/esm/identity/mfa/service.d.ts +338 -0
  207. package/esm/identity/mfa/service.js +351 -0
  208. package/esm/identity/mfa/totp.d.ts +186 -0
  209. package/esm/identity/mfa/totp.js +194 -0
  210. package/esm/identity/migration.d.ts +155 -0
  211. package/esm/identity/migration.js +243 -0
  212. package/esm/identity/mod.d.ts +65 -0
  213. package/esm/identity/mod.js +64 -0
  214. package/esm/identity/otp.d.ts +255 -0
  215. package/esm/identity/otp.js +245 -0
  216. package/esm/identity/password-policy.d.ts +45 -0
  217. package/esm/identity/password-policy.js +60 -0
  218. package/esm/identity/password.d.ts +171 -0
  219. package/esm/identity/password.js +160 -0
  220. package/esm/identity/rate-limit.d.ts +192 -0
  221. package/esm/identity/rate-limit.js +168 -0
  222. package/esm/identity/service.d.ts +579 -0
  223. package/esm/identity/service.js +835 -0
  224. package/esm/identity/session.d.ts +122 -0
  225. package/esm/identity/session.js +48 -0
  226. package/esm/identity/token-flow.d.ts +214 -0
  227. package/esm/identity/token-flow.js +173 -0
  228. package/esm/models/authentication.d.ts +24 -0
  229. package/esm/models/authentication.js +1 -0
  230. package/esm/models/authorization-code.d.ts +63 -0
  231. package/esm/models/authorization-code.js +1 -0
  232. package/esm/models/authorization.d.ts +165 -0
  233. package/esm/models/authorization.js +160 -0
  234. package/esm/models/client.d.ts +78 -0
  235. package/esm/models/client.js +1 -0
  236. package/esm/models/device-authorization.d.ts +30 -0
  237. package/esm/models/device-authorization.js +1 -0
  238. package/esm/models/responses.d.ts +93 -0
  239. package/esm/models/responses.js +1 -0
  240. package/esm/models/scope.d.ts +81 -0
  241. package/esm/models/scope.js +125 -0
  242. package/esm/models/token.d.ts +135 -0
  243. package/esm/models/token.js +1 -0
  244. package/esm/package.json +3 -0
  245. package/esm/react/callback.d.ts +52 -0
  246. package/esm/react/callback.js +71 -0
  247. package/esm/react/components/class-names.d.ts +91 -0
  248. package/esm/react/components/class-names.js +118 -0
  249. package/esm/react/components/internal.d.ts +62 -0
  250. package/esm/react/components/internal.js +102 -0
  251. package/esm/react/components/mfa-challenge-form.d.ts +66 -0
  252. package/esm/react/components/mfa-challenge-form.js +46 -0
  253. package/esm/react/components/mfa-enrollment-form.d.ts +83 -0
  254. package/esm/react/components/mfa-enrollment-form.js +41 -0
  255. package/esm/react/components/mod.d.ts +103 -0
  256. package/esm/react/components/mod.js +93 -0
  257. package/esm/react/components/request-password-reset-form.d.ts +61 -0
  258. package/esm/react/components/request-password-reset-form.js +41 -0
  259. package/esm/react/components/reset-password-form.d.ts +66 -0
  260. package/esm/react/components/reset-password-form.js +56 -0
  261. package/esm/react/components/sign-in-form.d.ts +74 -0
  262. package/esm/react/components/sign-in-form.js +43 -0
  263. package/esm/react/components/sign-up-form.d.ts +85 -0
  264. package/esm/react/components/sign-up-form.js +57 -0
  265. package/esm/react/components/types.d.ts +208 -0
  266. package/esm/react/components/types.js +14 -0
  267. package/esm/react/components/use-auth-form.d.ts +118 -0
  268. package/esm/react/components/use-auth-form.js +172 -0
  269. package/esm/react/components/user-menu.d.ts +79 -0
  270. package/esm/react/components/user-menu.js +73 -0
  271. package/esm/react/context.d.ts +51 -0
  272. package/esm/react/context.js +16 -0
  273. package/esm/react/mod.d.ts +26 -0
  274. package/esm/react/mod.js +26 -0
  275. package/esm/react/provider.d.ts +40 -0
  276. package/esm/react/provider.js +189 -0
  277. package/esm/react/require-auth.d.ts +31 -0
  278. package/esm/react/require-auth.js +37 -0
  279. package/esm/react/testing.d.ts +322 -0
  280. package/esm/react/testing.js +461 -0
  281. package/esm/react/use-authorization.d.ts +31 -0
  282. package/esm/react/use-authorization.js +36 -0
  283. package/esm/react/use-oauth2.d.ts +112 -0
  284. package/esm/react/use-oauth2.js +127 -0
  285. package/esm/server/authorization-server.d.ts +736 -0
  286. package/esm/server/authorization-server.js +1304 -0
  287. package/esm/server/client-authentication.d.ts +28 -0
  288. package/esm/server/client-authentication.js +51 -0
  289. package/esm/server/grants/authorization-code.d.ts +179 -0
  290. package/esm/server/grants/authorization-code.js +232 -0
  291. package/esm/server/grants/client-credentials.d.ts +52 -0
  292. package/esm/server/grants/client-credentials.js +53 -0
  293. package/esm/server/grants/device-authorization.d.ts +103 -0
  294. package/esm/server/grants/device-authorization.js +156 -0
  295. package/esm/server/grants/grant.d.ts +165 -0
  296. package/esm/server/grants/grant.js +173 -0
  297. package/esm/server/grants/password.d.ts +80 -0
  298. package/esm/server/grants/password.js +92 -0
  299. package/esm/server/grants/refresh-token.d.ts +80 -0
  300. package/esm/server/grants/refresh-token.js +136 -0
  301. package/esm/server/introspection-token-reader.d.ts +153 -0
  302. package/esm/server/introspection-token-reader.js +164 -0
  303. package/esm/server/jwks-token-reader.d.ts +263 -0
  304. package/esm/server/jwks-token-reader.js +508 -0
  305. package/esm/server/mod.d.ts +23 -0
  306. package/esm/server/mod.js +15 -0
  307. package/esm/server/public-suffix/mod.d.ts +62 -0
  308. package/esm/server/public-suffix/mod.js +103 -0
  309. package/esm/server/public-suffix/rules.d.ts +23 -0
  310. package/esm/server/public-suffix/rules.js +23 -0
  311. package/esm/server/redirect-uri.d.ts +187 -0
  312. package/esm/server/redirect-uri.js +331 -0
  313. package/esm/server/resource-server.d.ts +286 -0
  314. package/esm/server/resource-server.js +365 -0
  315. package/esm/server/services/authorization-code.d.ts +84 -0
  316. package/esm/server/services/authorization-code.js +35 -0
  317. package/esm/server/services/client.d.ts +35 -0
  318. package/esm/server/services/client.js +1 -0
  319. package/esm/server/services/device-authorization.d.ts +134 -0
  320. package/esm/server/services/device-authorization.js +7 -0
  321. package/esm/server/services/token.d.ts +242 -0
  322. package/esm/server/services/token.js +78 -0
  323. package/esm/server/services/user.d.ts +29 -0
  324. package/esm/server/services/user.js +7 -0
  325. package/esm/server/signing-keys.d.ts +201 -0
  326. package/esm/server/signing-keys.js +225 -0
  327. package/esm/server/utils/hash.d.ts +6 -0
  328. package/esm/server/utils/hash.js +11 -0
  329. package/esm/testing/contract/authorization-code.d.ts +37 -0
  330. package/esm/testing/contract/authorization-code.js +93 -0
  331. package/esm/testing/contract/client.d.ts +85 -0
  332. package/esm/testing/contract/client.js +126 -0
  333. package/esm/testing/contract/device-authorization.d.ts +37 -0
  334. package/esm/testing/contract/device-authorization.js +193 -0
  335. package/esm/testing/contract/lockout-store.d.ts +47 -0
  336. package/esm/testing/contract/lockout-store.js +122 -0
  337. package/esm/testing/contract/mfa-store.d.ts +50 -0
  338. package/esm/testing/contract/mfa-store.js +226 -0
  339. package/esm/testing/contract/mod.d.ts +39 -0
  340. package/esm/testing/contract/mod.js +39 -0
  341. package/esm/testing/contract/otp-store.d.ts +48 -0
  342. package/esm/testing/contract/otp-store.js +197 -0
  343. package/esm/testing/contract/rate-limit-store.d.ts +52 -0
  344. package/esm/testing/contract/rate-limit-store.js +105 -0
  345. package/esm/testing/contract/token-flow-store.d.ts +54 -0
  346. package/esm/testing/contract/token-flow-store.js +179 -0
  347. package/esm/testing/contract/token-reader.d.ts +77 -0
  348. package/esm/testing/contract/token-reader.js +78 -0
  349. package/esm/testing/contract/token.d.ts +61 -0
  350. package/esm/testing/contract/token.js +287 -0
  351. package/esm/testing/contract/user.d.ts +63 -0
  352. package/esm/testing/contract/user.js +75 -0
  353. package/esm/testing/mod.d.ts +34 -0
  354. package/esm/testing/mod.js +34 -0
  355. package/esm/testing/server.d.ts +197 -0
  356. package/esm/testing/server.js +193 -0
  357. package/esm/testing/services.d.ts +373 -0
  358. package/esm/testing/services.js +703 -0
  359. package/esm/utils/_buffer.d.ts +10 -0
  360. package/esm/utils/_buffer.js +12 -0
  361. package/esm/utils/_timing-safe.d.ts +13 -0
  362. package/esm/utils/_timing-safe.js +22 -0
  363. package/esm/utils/authentication-context.d.ts +3 -0
  364. package/esm/utils/authentication-context.js +14 -0
  365. package/esm/utils/basic-auth.d.ts +50 -0
  366. package/esm/utils/basic-auth.js +106 -0
  367. package/esm/utils/crypto.d.ts +97 -0
  368. package/esm/utils/crypto.js +162 -0
  369. package/esm/utils/pkce.d.ts +151 -0
  370. package/esm/utils/pkce.js +179 -0
  371. package/esm/utils/text.d.ts +24 -0
  372. package/esm/utils/text.js +28 -0
  373. package/esm/utils/url.d.ts +66 -0
  374. package/esm/utils/url.js +90 -0
  375. package/package.json +92 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Internal buffer conversion shared by the modules that call Web Crypto.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Copy to a standalone `ArrayBuffer` to satisfy `BufferSource` on runtimes that
8
+ * narrow `Uint8Array<ArrayBufferLike>` away from `ArrayBuffer`.
9
+ */
10
+ export declare function toArrayBuffer(bytes: Uint8Array): ArrayBuffer;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Internal buffer conversion shared by the modules that call Web Crypto.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Copy to a standalone `ArrayBuffer` to satisfy `BufferSource` on runtimes that
8
+ * narrow `Uint8Array<ArrayBufferLike>` away from `ArrayBuffer`.
9
+ */
10
+ export function toArrayBuffer(bytes) {
11
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
12
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Internal constant-time matching shared by the MFA and BFF code paths.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Constant-time scan for `target` among `candidates`: every candidate is
8
+ * compared (no early exit), so timing reveals neither whether nor where a
9
+ * match occurred. Returns the index of the *last* matching candidate, or
10
+ * `undefined` when none match — order candidates least- to most-preferred so
11
+ * a duplicate resolves to the preferred entry.
12
+ */
13
+ export declare function timingSafeMatchIndex(candidates: string[], target: string): number | undefined;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Internal constant-time matching shared by the MFA and BFF code paths.
3
+ *
4
+ * @module
5
+ */
6
+ import { timingSafeEqualString } from "./crypto.js";
7
+ /**
8
+ * Constant-time scan for `target` among `candidates`: every candidate is
9
+ * compared (no early exit), so timing reveals neither whether nor where a
10
+ * match occurred. Returns the index of the *last* matching candidate, or
11
+ * `undefined` when none match — order candidates least- to most-preferred so
12
+ * a duplicate resolves to the preferred entry.
13
+ */
14
+ export function timingSafeMatchIndex(candidates, target) {
15
+ let index;
16
+ for (let i = 0; i < candidates.length; i++) {
17
+ if (timingSafeEqualString(candidates[i], target)) {
18
+ index = i;
19
+ }
20
+ }
21
+ return index;
22
+ }
@@ -0,0 +1,3 @@
1
+ import type { AuthenticationContext } from "../models/authentication.js";
2
+ /** Copies verified event claims without sharing mutable method arrays. */
3
+ export declare function snapshotAuthenticationContext(context: AuthenticationContext | undefined): AuthenticationContext | undefined;
@@ -0,0 +1,14 @@
1
+ /** Copies verified event claims without sharing mutable method arrays. */
2
+ export function snapshotAuthenticationContext(context) {
3
+ if (context === undefined)
4
+ return undefined;
5
+ return Object.freeze({
6
+ ...(context.auth_time === undefined
7
+ ? {}
8
+ : { auth_time: context.auth_time }),
9
+ ...(context.acr === undefined ? {} : { acr: context.acr }),
10
+ ...(context.amr === undefined
11
+ ? {}
12
+ : { amr: Object.freeze([...context.amr]) }),
13
+ });
14
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * HTTP Basic Authentication utilities.
3
+ * @module
4
+ */
5
+ /** Parsed basic authentication credentials. */
6
+ export interface BasicAuth {
7
+ /** Username portion (the segment before the first colon). */
8
+ name: string;
9
+ /** Password portion (everything after the first colon). */
10
+ pass: string;
11
+ }
12
+ /**
13
+ * Parses basic authentication credentials from an Authorization header.
14
+ *
15
+ * Both segments are decoded from `application/x-www-form-urlencoded` form, as
16
+ * RFC 6749 section 2.3.1 requires of client credentials, so a client id or
17
+ * secret containing `+`, a space, `%`, `:`, or non-ASCII survives the round
18
+ * trip. A segment that is not valid percent-encoding is taken verbatim, so a
19
+ * client that sends such characters un-encoded still authenticates.
20
+ *
21
+ * @param authorization The Authorization header value
22
+ * @returns The parsed credentials
23
+ * @throws {InvalidClientError} If the header is missing or malformed
24
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
25
+ */
26
+ export declare function parseBasicAuth(authorization: string | null): BasicAuth;
27
+ /**
28
+ * Encodes client credentials for HTTP Basic Authentication.
29
+ *
30
+ * Both values are `application/x-www-form-urlencoded` before base64, as RFC
31
+ * 6749 section 2.3.1 requires: that keeps `+`, spaces, `%`, and `:` unambiguous
32
+ * on the wire and makes a non-ASCII secret encodable at all. Credentials made
33
+ * only of `A-Z`, `a-z`, `0-9`, `-`, `_`, `.` and `*` encode byte-identically to
34
+ * their raw form; every other character, `~` included, is percent-encoded.
35
+ *
36
+ * @param clientId The client ID
37
+ * @param clientSecret The client secret
38
+ * @returns The encoded Authorization header value
39
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
40
+ */
41
+ export declare function encodeBasicAuth(clientId: string, clientSecret: string): string;
42
+ /**
43
+ * Tries to parse basic authentication credentials from an Authorization header.
44
+ * Returns undefined if the header is missing or not Basic auth. Decodes both
45
+ * segments the same way {@link parseBasicAuth} does.
46
+ *
47
+ * @param authorization The Authorization header value
48
+ * @returns The parsed credentials or undefined
49
+ */
50
+ export declare function tryParseBasicAuth(authorization: string | null): BasicAuth | undefined;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * HTTP Basic Authentication utilities.
3
+ * @module
4
+ */
5
+ import { InvalidClientError } from "../errors.js";
6
+ const CREDENTIALS = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([\w-.~+/]+=*) *$/;
7
+ const NAME_PASS = /^([^:]+):(.*)$/;
8
+ const NOT_FORM_SAFE = /[!'()~]/g;
9
+ const ENCODED_SPACE = /%20/g;
10
+ const PLUS = /\+/g;
11
+ function formUrlEncode(value) {
12
+ return encodeURIComponent(value)
13
+ .replace(NOT_FORM_SAFE, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`)
14
+ .replace(ENCODED_SPACE, "+");
15
+ }
16
+ function formUrlDecode(value) {
17
+ try {
18
+ return decodeURIComponent(value.replace(PLUS, " "));
19
+ }
20
+ catch {
21
+ return value;
22
+ }
23
+ }
24
+ /**
25
+ * Parses basic authentication credentials from an Authorization header.
26
+ *
27
+ * Both segments are decoded from `application/x-www-form-urlencoded` form, as
28
+ * RFC 6749 section 2.3.1 requires of client credentials, so a client id or
29
+ * secret containing `+`, a space, `%`, `:`, or non-ASCII survives the round
30
+ * trip. A segment that is not valid percent-encoding is taken verbatim, so a
31
+ * client that sends such characters un-encoded still authenticates.
32
+ *
33
+ * @param authorization The Authorization header value
34
+ * @returns The parsed credentials
35
+ * @throws {InvalidClientError} If the header is missing or malformed
36
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
37
+ */
38
+ export function parseBasicAuth(authorization) {
39
+ if (!authorization) {
40
+ throw new InvalidClientError("authorization header required");
41
+ }
42
+ let match = CREDENTIALS.exec(authorization);
43
+ if (!match) {
44
+ throw new InvalidClientError("unsupported authorization header");
45
+ }
46
+ let value;
47
+ try {
48
+ value = atob(match[1]);
49
+ }
50
+ catch {
51
+ throw new InvalidClientError("authorization header is not correctly encoded");
52
+ }
53
+ match = NAME_PASS.exec(value);
54
+ if (!match) {
55
+ throw new InvalidClientError("authorization header is malformed");
56
+ }
57
+ return {
58
+ name: formUrlDecode(match[1]),
59
+ pass: formUrlDecode(match[2]),
60
+ };
61
+ }
62
+ /**
63
+ * Encodes client credentials for HTTP Basic Authentication.
64
+ *
65
+ * Both values are `application/x-www-form-urlencoded` before base64, as RFC
66
+ * 6749 section 2.3.1 requires: that keeps `+`, spaces, `%`, and `:` unambiguous
67
+ * on the wire and makes a non-ASCII secret encodable at all. Credentials made
68
+ * only of `A-Z`, `a-z`, `0-9`, `-`, `_`, `.` and `*` encode byte-identically to
69
+ * their raw form; every other character, `~` included, is percent-encoded.
70
+ *
71
+ * @param clientId The client ID
72
+ * @param clientSecret The client secret
73
+ * @returns The encoded Authorization header value
74
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
75
+ */
76
+ export function encodeBasicAuth(clientId, clientSecret) {
77
+ return `Basic ${btoa(`${formUrlEncode(clientId)}:${formUrlEncode(clientSecret)}`)}`;
78
+ }
79
+ /**
80
+ * Tries to parse basic authentication credentials from an Authorization header.
81
+ * Returns undefined if the header is missing or not Basic auth. Decodes both
82
+ * segments the same way {@link parseBasicAuth} does.
83
+ *
84
+ * @param authorization The Authorization header value
85
+ * @returns The parsed credentials or undefined
86
+ */
87
+ export function tryParseBasicAuth(authorization) {
88
+ if (!authorization)
89
+ return undefined;
90
+ const match = CREDENTIALS.exec(authorization);
91
+ if (!match)
92
+ return undefined;
93
+ try {
94
+ const value = atob(match[1]);
95
+ const namePass = NAME_PASS.exec(value);
96
+ if (!namePass)
97
+ return undefined;
98
+ return {
99
+ name: formUrlDecode(namePass[1]),
100
+ pass: formUrlDecode(namePass[2]),
101
+ };
102
+ }
103
+ catch {
104
+ return undefined;
105
+ }
106
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Authenticated-encryption (AES-256-GCM) and key-derivation helpers for sealing
3
+ * session and token data at rest or in a cookie.
4
+ *
5
+ * Two key-derivation paths, for two different threat models:
6
+ *
7
+ * - {@link deriveAesKey} hashes a single **server-held** secret (SHA-256) into
8
+ * an AES-256-GCM key. Use this for a stateless cookie sealed under one
9
+ * configured secret (see `EncryptedCookieSessionStore`), where the secret is
10
+ * never persisted.
11
+ *
12
+ * - {@link deriveSealKey} uses **HKDF** with a domain-separating `info` label so
13
+ * a high-entropy *per-session* secret yields an encryption key that is
14
+ * independent of any other value derived from the same secret. This is what
15
+ * makes "the cookie value is both the lookup credential **and** the decryption
16
+ * key" safe: a database can store `sha256(secret)` as a lookup hash while the
17
+ * sealing key is `HKDF(secret, info)`, and knowing the stored hash never
18
+ * reveals the key. Always pass a distinct `info` per purpose.
19
+ *
20
+ * {@link seal}/{@link unseal} use AES-256-GCM (an AEAD), so tampered ciphertext
21
+ * fails to unseal (returns `null`) rather than decrypting to garbage.
22
+ *
23
+ * @module
24
+ */
25
+ import "../_dnt.polyfills.js";
26
+ import { sha256Hash } from "../server/utils/hash.js";
27
+ import { toArrayBuffer } from "./_buffer.js";
28
+ export { sha256Hash };
29
+ export { toArrayBuffer };
30
+ /** Constant-time string equality (length difference short-circuits to false). */
31
+ export declare function timingSafeEqualString(a: string, b: string): boolean;
32
+ /** URL-safe base64 encode (no padding). */
33
+ export declare function base64urlEncode(bytes: Uint8Array): string;
34
+ /**
35
+ * URL-safe base64 decode. Padding is optional, so JWS/JWT segments — which
36
+ * carry none — decode as-is. Only the URL-safe alphabet is accepted.
37
+ *
38
+ * @throws {TypeError} on a character outside the URL-safe alphabet — including
39
+ * the standard alphabet's `+` and `/`, and whitespace.
40
+ * @throws {RangeError} on a truncated value (a length that leaves a remainder
41
+ * of 1 when divided by 4, which no encoding can produce).
42
+ */
43
+ export declare function base64urlDecode(value: string): Uint8Array;
44
+ /**
45
+ * Mint a cryptographically random, URL-safe token: `base64url` of `bytes`
46
+ * random bytes, unpadded, so it drops into a URL, a link in an email, or a
47
+ * cookie value with no further escaping.
48
+ *
49
+ * This is the primitive behind session secrets, verification / password-reset
50
+ * links, and download tokens. The token is a bearer credential: hand it out
51
+ * once and persist only a hash of it ({@link sha256Hash}, from this module).
52
+ * The default 32 bytes (256 bits, 43 characters) is what every such credential
53
+ * should use; shrink it only for values that don't authorize anything.
54
+ *
55
+ * @param bytes How many random bytes to draw. Defaults to 32.
56
+ * @returns The unpadded base64url encoding of those bytes.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * randomToken(); // 43 chars, e.g. "kQ7…" — a reset-link token
61
+ * randomToken(16); // 22 chars — a non-credential correlation id
62
+ * ```
63
+ */
64
+ export declare function randomToken(bytes?: number): string;
65
+ /**
66
+ * Derive an AES-256-GCM key from a single server-held secret via SHA-256.
67
+ *
68
+ * Suitable when the secret is a long-lived configuration value that is never
69
+ * stored alongside the ciphertext. Do **not** use this when a hash of the same
70
+ * secret is persisted (e.g. as a lookup key) — use {@link deriveSealKey} so the
71
+ * sealing key is independent of that stored hash.
72
+ *
73
+ * @throws if the secret has fewer than 16 bytes of entropy (32+ recommended).
74
+ */
75
+ export declare function deriveAesKey(secret: string | Uint8Array): Promise<CryptoKey>;
76
+ /**
77
+ * Derive an AES-256-GCM sealing key from a high-entropy secret via HKDF-SHA-256
78
+ * with a domain-separating `info` label.
79
+ *
80
+ * Keys derived with different `info` values (or different from `sha256(secret)`)
81
+ * are independent, so the same secret can safely back both a stored lookup hash
82
+ * and a never-stored sealing key. Pass a stable, unique `info` per purpose, e.g.
83
+ * `"udibo:bff-session-tokens:v1"`.
84
+ */
85
+ export declare function deriveSealKey(secret: string | Uint8Array, info: string): Promise<CryptoKey>;
86
+ /** Seal bytes with AES-256-GCM; returns `base64url(iv ‖ ciphertext+tag)`. */
87
+ export declare function seal(key: CryptoKey, plaintext: Uint8Array): Promise<string>;
88
+ /**
89
+ * Unseal an AES-256-GCM value produced by {@link seal}. Returns `null` on a
90
+ * malformed input, a wrong key, or tampering (the AEAD tag fails); it never
91
+ * throws.
92
+ */
93
+ export declare function unseal(key: CryptoKey, sealed: string): Promise<Uint8Array | null>;
94
+ /** Seal a JSON-serializable value. */
95
+ export declare function sealJson(key: CryptoKey, value: unknown): Promise<string>;
96
+ /** Unseal a value sealed by {@link sealJson}, or `null` if it can't be unsealed. */
97
+ export declare function unsealJson<T>(key: CryptoKey, sealed: string): Promise<T | null>;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Authenticated-encryption (AES-256-GCM) and key-derivation helpers for sealing
3
+ * session and token data at rest or in a cookie.
4
+ *
5
+ * Two key-derivation paths, for two different threat models:
6
+ *
7
+ * - {@link deriveAesKey} hashes a single **server-held** secret (SHA-256) into
8
+ * an AES-256-GCM key. Use this for a stateless cookie sealed under one
9
+ * configured secret (see `EncryptedCookieSessionStore`), where the secret is
10
+ * never persisted.
11
+ *
12
+ * - {@link deriveSealKey} uses **HKDF** with a domain-separating `info` label so
13
+ * a high-entropy *per-session* secret yields an encryption key that is
14
+ * independent of any other value derived from the same secret. This is what
15
+ * makes "the cookie value is both the lookup credential **and** the decryption
16
+ * key" safe: a database can store `sha256(secret)` as a lookup hash while the
17
+ * sealing key is `HKDF(secret, info)`, and knowing the stored hash never
18
+ * reveals the key. Always pass a distinct `info` per purpose.
19
+ *
20
+ * {@link seal}/{@link unseal} use AES-256-GCM (an AEAD), so tampered ciphertext
21
+ * fails to unseal (returns `null`) rather than decrypting to garbage.
22
+ *
23
+ * @module
24
+ */
25
+ import "../_dnt.polyfills.js";
26
+ import { timingSafeEqual } from "../deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.js";
27
+ import { decodeBase64Url, encodeBase64Url } from "../deps/jsr.io/@std/encoding/1.0.11/base64url.js";
28
+ import { sha256Hash } from "../server/utils/hash.js";
29
+ import { toArrayBuffer } from "./_buffer.js";
30
+ export { sha256Hash };
31
+ export { toArrayBuffer };
32
+ function toBytes(secret) {
33
+ return typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
34
+ }
35
+ /** Constant-time string equality (length difference short-circuits to false). */
36
+ export function timingSafeEqualString(a, b) {
37
+ const encoder = new TextEncoder();
38
+ const aBytes = encoder.encode(a);
39
+ const bBytes = encoder.encode(b);
40
+ if (aBytes.byteLength !== bBytes.byteLength)
41
+ return false;
42
+ return timingSafeEqual(aBytes, bBytes);
43
+ }
44
+ /** URL-safe base64 encode (no padding). */
45
+ export function base64urlEncode(bytes) {
46
+ return encodeBase64Url(bytes);
47
+ }
48
+ /**
49
+ * URL-safe base64 decode. Padding is optional, so JWS/JWT segments — which
50
+ * carry none — decode as-is. Only the URL-safe alphabet is accepted.
51
+ *
52
+ * @throws {TypeError} on a character outside the URL-safe alphabet — including
53
+ * the standard alphabet's `+` and `/`, and whitespace.
54
+ * @throws {RangeError} on a truncated value (a length that leaves a remainder
55
+ * of 1 when divided by 4, which no encoding can produce).
56
+ */
57
+ export function base64urlDecode(value) {
58
+ return decodeBase64Url(value);
59
+ }
60
+ /**
61
+ * Mint a cryptographically random, URL-safe token: `base64url` of `bytes`
62
+ * random bytes, unpadded, so it drops into a URL, a link in an email, or a
63
+ * cookie value with no further escaping.
64
+ *
65
+ * This is the primitive behind session secrets, verification / password-reset
66
+ * links, and download tokens. The token is a bearer credential: hand it out
67
+ * once and persist only a hash of it ({@link sha256Hash}, from this module).
68
+ * The default 32 bytes (256 bits, 43 characters) is what every such credential
69
+ * should use; shrink it only for values that don't authorize anything.
70
+ *
71
+ * @param bytes How many random bytes to draw. Defaults to 32.
72
+ * @returns The unpadded base64url encoding of those bytes.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * randomToken(); // 43 chars, e.g. "kQ7…" — a reset-link token
77
+ * randomToken(16); // 22 chars — a non-credential correlation id
78
+ * ```
79
+ */
80
+ export function randomToken(bytes = 32) {
81
+ return base64urlEncode(crypto.getRandomValues(new Uint8Array(bytes)));
82
+ }
83
+ /**
84
+ * Derive an AES-256-GCM key from a single server-held secret via SHA-256.
85
+ *
86
+ * Suitable when the secret is a long-lived configuration value that is never
87
+ * stored alongside the ciphertext. Do **not** use this when a hash of the same
88
+ * secret is persisted (e.g. as a lookup key) — use {@link deriveSealKey} so the
89
+ * sealing key is independent of that stored hash.
90
+ *
91
+ * @throws if the secret has fewer than 16 bytes of entropy (32+ recommended).
92
+ */
93
+ export async function deriveAesKey(secret) {
94
+ const raw = toBytes(secret);
95
+ if (raw.byteLength < 16) {
96
+ throw new Error("secret must be at least 16 bytes; 32+ bytes of entropy is recommended.");
97
+ }
98
+ const digest = await crypto.subtle.digest("SHA-256", toArrayBuffer(raw));
99
+ return await crypto.subtle.importKey("raw", digest, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
100
+ }
101
+ /**
102
+ * Derive an AES-256-GCM sealing key from a high-entropy secret via HKDF-SHA-256
103
+ * with a domain-separating `info` label.
104
+ *
105
+ * Keys derived with different `info` values (or different from `sha256(secret)`)
106
+ * are independent, so the same secret can safely back both a stored lookup hash
107
+ * and a never-stored sealing key. Pass a stable, unique `info` per purpose, e.g.
108
+ * `"udibo:bff-session-tokens:v1"`.
109
+ */
110
+ export async function deriveSealKey(secret, info) {
111
+ const base = await crypto.subtle.importKey("raw", toArrayBuffer(toBytes(secret)), "HKDF", false, ["deriveKey"]);
112
+ return await crypto.subtle.deriveKey({
113
+ name: "HKDF",
114
+ hash: "SHA-256",
115
+ salt: new Uint8Array(0),
116
+ info: new TextEncoder().encode(info),
117
+ }, base, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
118
+ }
119
+ /** Seal bytes with AES-256-GCM; returns `base64url(iv ‖ ciphertext+tag)`. */
120
+ export async function seal(key, plaintext) {
121
+ const iv = crypto.getRandomValues(new Uint8Array(12));
122
+ const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, toArrayBuffer(plaintext)));
123
+ const packed = new Uint8Array(iv.length + ciphertext.length);
124
+ packed.set(iv, 0);
125
+ packed.set(ciphertext, iv.length);
126
+ return base64urlEncode(packed);
127
+ }
128
+ /**
129
+ * Unseal an AES-256-GCM value produced by {@link seal}. Returns `null` on a
130
+ * malformed input, a wrong key, or tampering (the AEAD tag fails); it never
131
+ * throws.
132
+ */
133
+ export async function unseal(key, sealed) {
134
+ try {
135
+ const bytes = base64urlDecode(sealed);
136
+ if (bytes.length < 13)
137
+ return null;
138
+ const iv = bytes.slice(0, 12);
139
+ const ciphertext = bytes.slice(12);
140
+ const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, toArrayBuffer(ciphertext));
141
+ return new Uint8Array(plaintext);
142
+ }
143
+ catch {
144
+ return null;
145
+ }
146
+ }
147
+ /** Seal a JSON-serializable value. */
148
+ export async function sealJson(key, value) {
149
+ return await seal(key, new TextEncoder().encode(JSON.stringify(value)));
150
+ }
151
+ /** Unseal a value sealed by {@link sealJson}, or `null` if it can't be unsealed. */
152
+ export async function unsealJson(key, sealed) {
153
+ const bytes = await unseal(key, sealed);
154
+ if (!bytes)
155
+ return null;
156
+ try {
157
+ return JSON.parse(new TextDecoder().decode(bytes));
158
+ }
159
+ catch {
160
+ return null;
161
+ }
162
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) utilities for OAuth2.
3
+ *
4
+ * Implements RFC 7636 for preventing authorization code interception attacks.
5
+ *
6
+ * @see https://datatracker.ietf.org/doc/html/rfc7636
7
+ * @module
8
+ */
9
+ /**
10
+ * A challenge method used for PKCE.
11
+ * Transforms a verifier into a challenge.
12
+ */
13
+ export type ChallengeMethod = (verifier: string) => Promise<string>;
14
+ /** The allowed PKCE code challenge methods. */
15
+ export interface ChallengeMethods {
16
+ /** Maps a `code_challenge_method` name (e.g. `"S256"`) to its transform. */
17
+ [key: string]: ChallengeMethod;
18
+ }
19
+ /**
20
+ * Minimum length for code verifier per RFC 7636 Section 4.1.
21
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
22
+ */
23
+ export declare const CODE_VERIFIER_MIN_LENGTH = 43;
24
+ /**
25
+ * Maximum length for code verifier per RFC 7636 Section 4.1.
26
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
27
+ */
28
+ export declare const CODE_VERIFIER_MAX_LENGTH = 128;
29
+ /**
30
+ * Pattern for valid code verifier characters per RFC 7636 Section 4.1.
31
+ * Allowed characters: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
32
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
33
+ */
34
+ export declare const CODE_VERIFIER_PATTERN: RegExp;
35
+ /**
36
+ * The default allowed PKCE code challenge methods.
37
+ *
38
+ * Per RFC 7636 and OAuth 2.1, clients SHOULD use the S256 method.
39
+ * The "plain" method is NOT included by default as it provides
40
+ * weaker security guarantees.
41
+ *
42
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
43
+ * @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-2.1.1
44
+ */
45
+ export declare const challengeMethods: ChallengeMethods;
46
+ /**
47
+ * Validates a code verifier per RFC 7636 Section 4.1.
48
+ *
49
+ * The code verifier must:
50
+ * - Be between 43 and 128 characters long
51
+ * - Contain only unreserved characters: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
52
+ *
53
+ * @param verifier The code verifier to validate
54
+ * @returns true if the verifier is valid
55
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
56
+ */
57
+ export declare function validateCodeVerifier(verifier: string): boolean;
58
+ /**
59
+ * Generates a random code verifier with a minimum of 256 bits of entropy.
60
+ *
61
+ * This is done by generating a random 32-octet sequence then base64url encoding it
62
+ * to produce a 43 character URL-safe string.
63
+ *
64
+ * @returns A cryptographically random code verifier
65
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
66
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-7.1
67
+ */
68
+ export declare function generateCodeVerifier(): string;
69
+ /**
70
+ * Generates a code challenge from a code verifier using the S256 method.
71
+ *
72
+ * The S256 challenge is: BASE64URL(SHA256(ASCII(code_verifier)))
73
+ *
74
+ * @param verifier The code verifier to transform
75
+ * @returns The code challenge
76
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
77
+ */
78
+ export declare function generateCodeChallenge(verifier: string): Promise<string>;
79
+ /**
80
+ * Validates a `code_challenge` per RFC 7636 Section 4.2, for the challenge
81
+ * methods whose output shape that section fixes.
82
+ *
83
+ * `S256` and `plain` are the only methods the PKCE registry defines, and both
84
+ * produce a challenge drawn from the same 43-128 unreserved characters as a
85
+ * code verifier, so an authorization endpoint can reject a malformed challenge
86
+ * before it is ever stored. Any other `method` — one a server registered in its
87
+ * own {@link ChallengeMethods} map — may legitimately produce any shape, so
88
+ * this returns `true` for it rather than imposing a format its author never
89
+ * agreed to. Check the name is allowed with {@linkcode getChallengeMethod}
90
+ * first; this answers only for the format.
91
+ *
92
+ * @param challenge The client-supplied `code_challenge`
93
+ * @param method The client-supplied `code_challenge_method`; defaults to `S256`
94
+ * @returns true when the challenge is acceptable for that method
95
+ * @example
96
+ * ```ts
97
+ * validateCodeChallenge("a".repeat(43)); // → true
98
+ * validateCodeChallenge("short"); // → false
99
+ * validateCodeChallenge("short", "custom-method"); // → true
100
+ * ```
101
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
102
+ */
103
+ export declare function validateCodeChallenge(challenge: string, method?: string | null): boolean;
104
+ /**
105
+ * Resolves a client-supplied `code_challenge_method` name against the methods
106
+ * a server allows.
107
+ *
108
+ * Reach for this instead of indexing a {@link ChallengeMethods} map directly.
109
+ * The name comes off the wire, and a map written as a plain object literal
110
+ * also inherits `toString`, `valueOf`, `constructor` and friends from
111
+ * `Object.prototype` — a bare lookup hands those back as if the server had
112
+ * allowed them, which downgrades PKCE to a value any verifier satisfies. Only
113
+ * own, callable properties of `methods` resolve here; every other name is
114
+ * `undefined`, including names inherited from a prototype.
115
+ *
116
+ * @param methods The challenge methods the server allows
117
+ * @param method The client-supplied method name; defaults to `S256`
118
+ * @returns The transform for that method, or `undefined` when it is not allowed
119
+ * @example
120
+ * ```ts
121
+ * getChallengeMethod(challengeMethods, "S256"); // → the S256 transform
122
+ * getChallengeMethod(challengeMethods); // → the S256 transform
123
+ * getChallengeMethod(challengeMethods, "toString"); // → undefined
124
+ * getChallengeMethod(challengeMethods, "__proto__"); // → undefined
125
+ * ```
126
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.3
127
+ */
128
+ export declare function getChallengeMethod(methods: ChallengeMethods, method?: string | null): ChallengeMethod | undefined;
129
+ /**
130
+ * Verifies that a code verifier matches a code challenge.
131
+ *
132
+ * Fails closed against the default {@link challengeMethods} map only: a
133
+ * `method` that map does not hold — `"plain"`, a name inherited from
134
+ * `Object.prototype` such as `"toString"`, or one registered on a grant via
135
+ * its own `challengeMethods` — resolves `false` rather than throwing, so it is
136
+ * indistinguishable from a mismatched verifier.
137
+ *
138
+ * @param verifier The code verifier provided by the client
139
+ * @param challenge The code challenge stored from the authorization request
140
+ * @param method The challenge method used (defaults to S256)
141
+ * @returns true if the verifier matches the challenge
142
+ * @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
143
+ */
144
+ export declare function verifyCodeChallenge(verifier: string, challenge: string, method?: string): Promise<boolean>;
145
+ /**
146
+ * Generates a random state parameter for CSRF protection.
147
+ *
148
+ * @returns A random UUID to use as the state parameter
149
+ * @see https://datatracker.ietf.org/doc/html/rfc6749#section-10.12
150
+ */
151
+ export declare function generateState(): string;