@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,64 @@
1
+ /**
2
+ * Bring-your-own bot-challenge verification for auth endpoints — the CAPTCHA
3
+ * analogue of the {@link RateLimiterLike} seam. {@link CaptchaProvider} is the
4
+ * contract a client-issued challenge token is verified against server-side;
5
+ * Cloudflare Turnstile, hCaptcha, and reCAPTCHA all satisfy it, so nothing here
6
+ * depends on a vendor SDK. {@link verifyCaptcha} composes a provider with the
7
+ * fail-open/closed discipline a login flow needs: a missing or rejected token
8
+ * is a refusal, but a provider *outage* degrades to your existing rate-limit and
9
+ * lockout floor rather than locking everyone out.
10
+ *
11
+ * Tenant-agnostic: scope by passing whatever `action` and `ip` your provider
12
+ * scores on. **No provider means no challenge** — {@link verifyCaptcha} passes
13
+ * every request, so wiring the seam without configuring a provider changes
14
+ * nothing about existing behavior.
15
+ *
16
+ * @module
17
+ */
18
+ /**
19
+ * Verify a client-issued CAPTCHA token with the fail-open/closed discipline an
20
+ * auth endpoint needs, and return a {@link CaptchaOutcome}.
21
+ *
22
+ * - **No provider** → unchallenged `"pass"` (`challenged: false`).
23
+ * - **Missing/empty token** → `"fail"` (the challenge went unanswered).
24
+ * - **Provider `{ success }`** → `"pass"`/`"fail"` accordingly.
25
+ * - **Provider throws (outage)** → `failOpen` decides `"pass"` or `"fail"`;
26
+ * either way the outcome is `degraded: true`, because no token was really
27
+ * evaluated.
28
+ *
29
+ * Never throws — a broken provider becomes a decision, not an exception the
30
+ * route must catch. Compose the result with your rate limiter and lockout; this
31
+ * does not replace them.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const outcome = await verifyCaptcha({ provider, token, context: { ip, action: "signin" } });
36
+ * if (outcome.decision === "fail") return uniformRejection();
37
+ * ```
38
+ */
39
+ export async function verifyCaptcha(options) {
40
+ const { provider, token, context } = options;
41
+ const failOpen = options.failOpen ?? true;
42
+ if (!provider) {
43
+ return { decision: "pass", challenged: false, degraded: false };
44
+ }
45
+ if (!token)
46
+ return { decision: "fail", challenged: true, degraded: false };
47
+ try {
48
+ const result = await provider.verify(token, context);
49
+ return {
50
+ decision: result.success ? "pass" : "fail",
51
+ challenged: true,
52
+ score: result.score,
53
+ degraded: false,
54
+ };
55
+ }
56
+ catch (error) {
57
+ console.error("[@udibo/oauth2] captcha provider verify failed (%s):", failOpen ? "fail-open, allowing" : "fail-closed, refusing", error instanceof Error ? error.message : error);
58
+ return {
59
+ decision: failOpen ? "pass" : "fail",
60
+ challenged: true,
61
+ degraded: true,
62
+ };
63
+ }
64
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Delivery seam for identity self-service messages (verification, reset, and
3
+ * passwordless sign-in links, plus one-time sign-in codes).
4
+ *
5
+ * The library owns the **token lifecycle** ({@link TokenFlowService}); your app
6
+ * owns **transport** — SMTP, SES, Resend, Twilio, etc. So delivery is a set of
7
+ * app-supplied async hooks, and the package ships URL builders so apps stop
8
+ * hand-assembling `${protocol}://${host}/verify-email?token=…` strings. This is
9
+ * the Better Auth model (`sendVerificationEmail` / `sendResetPassword`).
10
+ *
11
+ * @module
12
+ */
13
+ /** What a delivery hook receives for one message. */
14
+ export interface DeliveryMessage {
15
+ /** Recipient — an email address or phone number. */
16
+ to: string;
17
+ /**
18
+ * The ready-to-use action link (already carrying the token), when the caller
19
+ * built one via {@link buildVerificationUrl} / {@link buildResetUrl}. Absent
20
+ * if you'd rather build the message body from `token` yourself.
21
+ */
22
+ url?: string;
23
+ /** The raw token, for apps that compose their own URL or message. */
24
+ token: string;
25
+ /** The subject the token acts on (typically a user id). */
26
+ subject: string;
27
+ /** Token expiry, epoch ms. */
28
+ expiresAt: number;
29
+ }
30
+ /** A single delivery transport callback. */
31
+ export type DeliveryHook = (message: DeliveryMessage) => Promise<void> | void;
32
+ /**
33
+ * What a code-delivery hook receives for one message. Unlike a
34
+ * {@link DeliveryMessage} there is no URL — a one-time code is typed in, not
35
+ * clicked — and the raw `code` is the whole payload.
36
+ */
37
+ export interface CodeDeliveryMessage {
38
+ /** Recipient — an email address or phone number. */
39
+ to: string;
40
+ /** The one-time code to include in the message body, e.g. `"482913"`. */
41
+ code: string;
42
+ /** The subject the code signs in (typically a user id). */
43
+ subject: string;
44
+ /** Code expiry, epoch ms. */
45
+ expiresAt: number;
46
+ }
47
+ /** A delivery transport callback for one-time codes. */
48
+ export type CodeDeliveryHook = (message: CodeDeliveryMessage) => Promise<void> | void;
49
+ /**
50
+ * App-supplied delivery transports. Omit a hook to disable that message type.
51
+ * The package calls the relevant hook after minting the token; you send it.
52
+ *
53
+ * Trigger point `delivery` (see `docs/trigger-points.md`). The package calls a
54
+ * hook after minting the credential; a **throw** (e.g. a mailer outage) is
55
+ * trapped for every hook here: `IdentityService` invalidates the just-minted
56
+ * token or code — best effort, so a failed send does not leave a live,
57
+ * un-emailed credential in your store — and the requesting flow still resolves
58
+ * with its uniform result, so a transport outage cannot become an
59
+ * account-enumeration oracle. Handle transport failures inside the hook (log,
60
+ * retry, queue) if you need to observe or recover from them; the flow will not
61
+ * surface the throw to your caller.
62
+ *
63
+ * Every trapped failure emits the `delivery.failed` identity event, carrying the
64
+ * hook and an `invalidated` flag. `invalidated: false` means the cleanup failed
65
+ * too and a credential the recipient never received is live until it expires —
66
+ * the one case that needs an alert, and only your `onEvent` hook can see it.
67
+ */
68
+ export interface DeliveryHooks {
69
+ /** Deliver an email-verification link. */
70
+ sendEmailVerification?: DeliveryHook;
71
+ /** Deliver a password-reset link. */
72
+ sendPasswordReset?: DeliveryHook;
73
+ /** Deliver an account-unlock link after a failed-attempt lockout. */
74
+ sendAccountUnlock?: DeliveryHook;
75
+ /** Deliver a passwordless sign-in (magic) link. */
76
+ sendSignInLink?: DeliveryHook;
77
+ /** Deliver a one-time sign-in code (no URL; the user types the code in). */
78
+ sendSignInCode?: CodeDeliveryHook;
79
+ }
80
+ /**
81
+ * Build an absolute action URL that embeds a token as a query parameter.
82
+ *
83
+ * @param base An origin (e.g. `https://app.example`) or origin + path prefix.
84
+ * @param path The route the token is consumed at (e.g. `/reset-password`).
85
+ * @param token The raw token to embed.
86
+ * @param param The query parameter name. Defaults to `"token"`.
87
+ */
88
+ export declare function buildTokenUrl(base: string, path: string, token: string, param?: string): string;
89
+ /** Build an email-verification URL (default path `/verify-email`). */
90
+ export declare function buildVerificationUrl(base: string, token: string, path?: string): string;
91
+ /** Build a password-reset URL (default path `/reset-password`). */
92
+ export declare function buildResetUrl(base: string, token: string, path?: string): string;
93
+ /** Build a passwordless sign-in (magic-link) URL (default path `/signin-link`). */
94
+ export declare function buildSignInUrl(base: string, token: string, path?: string): string;
95
+ /** Build an account-unlock URL (default path `/unlock-account`). */
96
+ export declare function buildUnlockUrl(base: string, token: string, path?: string): string;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Delivery seam for identity self-service messages (verification, reset, and
3
+ * passwordless sign-in links, plus one-time sign-in codes).
4
+ *
5
+ * The library owns the **token lifecycle** ({@link TokenFlowService}); your app
6
+ * owns **transport** — SMTP, SES, Resend, Twilio, etc. So delivery is a set of
7
+ * app-supplied async hooks, and the package ships URL builders so apps stop
8
+ * hand-assembling `${protocol}://${host}/verify-email?token=…` strings. This is
9
+ * the Better Auth model (`sendVerificationEmail` / `sendResetPassword`).
10
+ *
11
+ * @module
12
+ */
13
+ /**
14
+ * Build an absolute action URL that embeds a token as a query parameter.
15
+ *
16
+ * @param base An origin (e.g. `https://app.example`) or origin + path prefix.
17
+ * @param path The route the token is consumed at (e.g. `/reset-password`).
18
+ * @param token The raw token to embed.
19
+ * @param param The query parameter name. Defaults to `"token"`.
20
+ */
21
+ export function buildTokenUrl(base, path, token, param = "token") {
22
+ const url = new URL(path, base);
23
+ url.searchParams.set(param, token);
24
+ return url.toString();
25
+ }
26
+ /** Build an email-verification URL (default path `/verify-email`). */
27
+ export function buildVerificationUrl(base, token, path = "/verify-email") {
28
+ return buildTokenUrl(base, path, token);
29
+ }
30
+ /** Build a password-reset URL (default path `/reset-password`). */
31
+ export function buildResetUrl(base, token, path = "/reset-password") {
32
+ return buildTokenUrl(base, path, token);
33
+ }
34
+ /** Build a passwordless sign-in (magic-link) URL (default path `/signin-link`). */
35
+ export function buildSignInUrl(base, token, path = "/signin-link") {
36
+ return buildTokenUrl(base, path, token);
37
+ }
38
+ /** Build an account-unlock URL (default path `/unlock-account`). */
39
+ export function buildUnlockUrl(base, token, path = "/unlock-account") {
40
+ return buildTokenUrl(base, path, token);
41
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A stable error contract for identity flows, so a front-end can switch on a
3
+ * code instead of parsing messages, and the Hono route factory can map errors
4
+ * to consistent responses.
5
+ *
6
+ * @module
7
+ */
8
+ /**
9
+ * Stable, machine-readable identity error codes. Most are raised by your store
10
+ * or the service; `forbidden_origin` is raised by the Hono route factory's
11
+ * same-origin guard before a handler runs.
12
+ */
13
+ export type IdentityErrorCode = "invalid_credentials" | "invalid_token" | "rate_limited" | "weak_password" | "identifier_taken" | "email_not_verified" | "mfa_not_enrolled" | "mfa_already_enrolled" | "captcha_failed" | "forbidden_origin";
14
+ /** Options for {@link IdentityError}. */
15
+ export interface IdentityErrorOptions extends ErrorOptions {
16
+ /** For `rate_limited`: milliseconds until the caller may retry. */
17
+ retryAfterMs?: number;
18
+ }
19
+ /**
20
+ * A typed error for identity flows. Throw it from your `IdentityUserStore`
21
+ * (e.g. `identifier_taken` on a duplicate) or your own validation; the Hono
22
+ * route factory maps it to `{ error: code }` with the right status.
23
+ */
24
+ export declare class IdentityError extends Error {
25
+ /** Always `"IdentityError"`, so instances are recognizable across realms. */
26
+ readonly name = "IdentityError";
27
+ /** The stable, machine-readable code a client can switch on. */
28
+ readonly code: IdentityErrorCode;
29
+ /** For `rate_limited`: ms until retry, if known. */
30
+ readonly retryAfterMs?: number;
31
+ /** Builds an identity error; `message` defaults to the `code` when omitted. */
32
+ constructor(code: IdentityErrorCode, message?: string, options?: IdentityErrorOptions);
33
+ }
34
+ /** Type guard for {@link IdentityError}. */
35
+ export declare function isIdentityError(value: unknown): value is IdentityError;
36
+ /** The HTTP statuses an identity error maps to (a `ContentfulStatusCode` union). */
37
+ export type IdentityErrorStatus = 400 | 401 | 403 | 409 | 422 | 429;
38
+ /** The HTTP status conventionally paired with an identity error code. */
39
+ export declare function identityErrorStatus(code: IdentityErrorCode): IdentityErrorStatus;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * A stable error contract for identity flows, so a front-end can switch on a
3
+ * code instead of parsing messages, and the Hono route factory can map errors
4
+ * to consistent responses.
5
+ *
6
+ * @module
7
+ */
8
+ /**
9
+ * A typed error for identity flows. Throw it from your `IdentityUserStore`
10
+ * (e.g. `identifier_taken` on a duplicate) or your own validation; the Hono
11
+ * route factory maps it to `{ error: code }` with the right status.
12
+ */
13
+ export class IdentityError extends Error {
14
+ /** Always `"IdentityError"`, so instances are recognizable across realms. */
15
+ name = "IdentityError";
16
+ /** The stable, machine-readable code a client can switch on. */
17
+ code;
18
+ /** For `rate_limited`: ms until retry, if known. */
19
+ retryAfterMs;
20
+ /** Builds an identity error; `message` defaults to the `code` when omitted. */
21
+ constructor(code, message, options) {
22
+ super(message ?? code, options);
23
+ this.code = code;
24
+ this.retryAfterMs = options?.retryAfterMs;
25
+ }
26
+ }
27
+ /** Type guard for {@link IdentityError}. */
28
+ export function isIdentityError(value) {
29
+ return value instanceof IdentityError;
30
+ }
31
+ const STATUS = {
32
+ invalid_credentials: 401,
33
+ invalid_token: 400,
34
+ rate_limited: 429,
35
+ weak_password: 422,
36
+ identifier_taken: 409,
37
+ email_not_verified: 403,
38
+ mfa_not_enrolled: 409,
39
+ mfa_already_enrolled: 409,
40
+ captcha_failed: 403,
41
+ forbidden_origin: 403,
42
+ };
43
+ /** The HTTP status conventionally paired with an identity error code. */
44
+ export function identityErrorStatus(code) {
45
+ return STATUS[code] ?? 400;
46
+ }
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Identity flow events — the observability seam for audit capture.
3
+ *
4
+ * {@link IdentityService} emits an {@link IdentityEvent} after each significant
5
+ * auth outcome (sign-in success/failure, sign-up, credential upgrade-on-login,
6
+ * reset and verification lifecycle, passwordless sign-in links and codes),
7
+ * `MfaService` emits the `mfa.*` events, and a password-policy validator emits
8
+ * `password_policy.check_unavailable` when it cannot complete its check. Wire
9
+ * an {@link IdentityEventHook} via the service's `onEvent`
10
+ * option to persist them in your own audit store — storage stays app-owned,
11
+ * like every other seam in this layer.
12
+ *
13
+ * Some outcomes are *only* visible here, because the flow that produced them
14
+ * resolves uniformly by design: `delivery.failed` is the sole in-band signal
15
+ * that a message never went out (and, when `invalidated` is `false`, that a
16
+ * live credential the user never received is still outstanding). Treat this
17
+ * hook as a monitored surface, not just an audit log.
18
+ *
19
+ * Events describe internal outcomes (including whether an identifier resolved
20
+ * to an account), so they are for server-side capture only — never surface
21
+ * their contents to the end user, or they become an enumeration oracle.
22
+ *
23
+ * @module
24
+ */
25
+ import type { DeliveryHooks } from "./delivery.js";
26
+ /** Why a sign-in attempt failed. Internal detail — never shown to the user. */
27
+ export type SignInFailureReason = "unknown_identifier" | "no_password" | "wrong_password";
28
+ /**
29
+ * Why a sign-in code verification failed. `unknown_email` is internal-only
30
+ * detail — the caller-facing result reports it as `invalid`.
31
+ */
32
+ export type SignInCodeFailureReason = "invalid" | "expired" | "locked" | "unknown_email";
33
+ /**
34
+ * An auth outcome emitted by {@link IdentityService}, `MfaService`, or a
35
+ * password-policy validator. Discriminated on `type`; `userId` is present when
36
+ * the flow resolved an account.
37
+ */
38
+ export type IdentityEvent = {
39
+ type: "sign_in.succeeded";
40
+ userId: string;
41
+ identifier: string;
42
+ } | {
43
+ type: "sign_in.failed";
44
+ identifier: string;
45
+ reason: SignInFailureReason;
46
+ userId?: string;
47
+ } | {
48
+ type: "sign_in.rate_limited";
49
+ identifier: string;
50
+ retryAfterMs: number;
51
+ enforced: boolean;
52
+ } | {
53
+ type: "sign_in.locked";
54
+ userId: string;
55
+ identifier: string;
56
+ enforced: boolean;
57
+ } | {
58
+ type: "lockout";
59
+ userId: string;
60
+ identifier: string;
61
+ failures: number;
62
+ lockedUntil?: number;
63
+ enforced: boolean;
64
+ } | {
65
+ type: "account_unlock.requested";
66
+ userId: string;
67
+ email: string;
68
+ } | {
69
+ type: "account_unlock.rate_limited";
70
+ userId: string;
71
+ email: string;
72
+ retryAfterMs: number;
73
+ enforced: boolean;
74
+ } | {
75
+ type: "account_unlock.completed";
76
+ userId: string;
77
+ } | {
78
+ type: "account_unlock.failed";
79
+ reason: "expired" | "invalid";
80
+ } | {
81
+ type: "sign_up";
82
+ userId: string;
83
+ } | {
84
+ type: "password.upgraded";
85
+ userId: string;
86
+ verifierId: string;
87
+ } | {
88
+ type: "password_policy.check_unavailable";
89
+ /**
90
+ * Which {@link PasswordPolicy} validator could not finish, as a stable
91
+ * snake_case name. {@link breachedPasswordValidator} reports
92
+ * `"breached_password"`.
93
+ */
94
+ validator: string;
95
+ /**
96
+ * `true` when the password was accepted anyway (the validator's `failOpen`
97
+ * default), `false` when it was rejected instead. A run of `true` is a
98
+ * control that stopped running — the question this event exists to answer.
99
+ */
100
+ failedOpen: boolean;
101
+ /**
102
+ * The underlying failure's message — a timeout, a DNS or egress-policy
103
+ * refusal, a non-2xx status from the upstream service.
104
+ */
105
+ error: string;
106
+ } | {
107
+ type: "password_reset.requested";
108
+ email: string;
109
+ userId?: string;
110
+ } | {
111
+ type: "password_reset.rate_limited";
112
+ email: string;
113
+ retryAfterMs: number;
114
+ enforced: boolean;
115
+ } | {
116
+ type: "password_reset.completed";
117
+ userId: string;
118
+ } | {
119
+ type: "password_reset.failed";
120
+ /**
121
+ * `"invalid_token"` — the reset link was unknown, expired, or already used.
122
+ * `"session_revocation_failed"` — the new password was set but the
123
+ * configured {@link RevocableSessionService} threw before the user's other
124
+ * sessions could be revoked, so the reset is reported failed (and rethrown)
125
+ * rather than trusted; retry it so the still-live sessions are cleared.
126
+ */
127
+ reason: "invalid_token" | "session_revocation_failed";
128
+ } | {
129
+ type: "email_verification.requested";
130
+ userId: string;
131
+ email: string;
132
+ } | {
133
+ type: "email_verification.rate_limited";
134
+ userId: string;
135
+ email: string;
136
+ retryAfterMs: number;
137
+ enforced: boolean;
138
+ } | {
139
+ type: "email_verification.completed";
140
+ userId: string;
141
+ email?: string;
142
+ } | {
143
+ type: "email_verification.failed";
144
+ reason: "expired" | "invalid";
145
+ } | {
146
+ type: "signin_link.requested";
147
+ email: string;
148
+ userId?: string;
149
+ } | {
150
+ type: "signin_link.consumed";
151
+ userId: string;
152
+ } | {
153
+ type: "signin_link.failed";
154
+ reason: "expired" | "invalid";
155
+ } | {
156
+ type: "signin_link.rate_limited";
157
+ email: string;
158
+ retryAfterMs?: number;
159
+ enforced: boolean;
160
+ } | {
161
+ type: "signin_code.requested";
162
+ email: string;
163
+ userId?: string;
164
+ } | {
165
+ type: "signin_code.verified";
166
+ userId: string;
167
+ } | {
168
+ type: "signin_code.failed";
169
+ email: string;
170
+ reason: SignInCodeFailureReason;
171
+ } | {
172
+ type: "signin_code.rate_limited";
173
+ email: string;
174
+ retryAfterMs?: number;
175
+ enforced: boolean;
176
+ } | {
177
+ type: "delivery.failed";
178
+ /** Which {@link DeliveryHooks} hook threw. */
179
+ hook: keyof DeliveryHooks;
180
+ /**
181
+ * `true` when the just-minted token or code was invalidated after the
182
+ * failed send — the intended outcome, nothing usable left behind. `false`
183
+ * when that cleanup *also* failed: a live credential the recipient never
184
+ * received is still outstanding until it expires. Alert on `false`.
185
+ */
186
+ invalidated: boolean;
187
+ /** The delivery hook's failure message. */
188
+ error: string;
189
+ } | {
190
+ type: "mfa.enrollment.confirmed";
191
+ userId: string;
192
+ } | {
193
+ type: "mfa.verify.succeeded";
194
+ userId: string;
195
+ method: "totp" | "recovery";
196
+ remainingRecoveryCodes?: number;
197
+ } | {
198
+ type: "mfa.verify.failed";
199
+ userId: string;
200
+ /**
201
+ * `"replayed"` marks a *valid* TOTP code rejected because its time step
202
+ * was already spent — the classic signal of code interception or a
203
+ * concurrent session, worth distinguishing from a typo in audit review.
204
+ */
205
+ reason: "invalid" | "replayed";
206
+ } | {
207
+ type: "mfa.verify.rate_limited";
208
+ userId: string;
209
+ retryAfterMs: number;
210
+ enforced: boolean;
211
+ } | {
212
+ type: "mfa.recovery_codes.regenerated";
213
+ userId: string;
214
+ } | {
215
+ type: "mfa.disabled";
216
+ userId: string;
217
+ };
218
+ /**
219
+ * Receives each {@link IdentityEvent} after the flow's outcome is decided. The
220
+ * hook is awaited, but it must not affect the flow: a rejection is caught and
221
+ * logged, never rethrown — report failures through your own channel if you
222
+ * need delivery guarantees.
223
+ *
224
+ * Trigger point `event` (see `docs/trigger-points.md`) — the one
225
+ * **fire-and-forget** seam in the identity layer.
226
+ */
227
+ export type IdentityEventHook = (event: IdentityEvent) => void | Promise<void>;
228
+ /** The `*.rate_limited` members of {@link IdentityEvent}. */
229
+ export type RateLimitedEvent = Extract<IdentityEvent, {
230
+ type: `${string}.rate_limited`;
231
+ }>;
232
+ /**
233
+ * A {@link RateLimitedEvent} minus the fields the throttle sequence fills in
234
+ * (`retryAfterMs`, `enforced`) — what a flow passes to `enforceRateLimit`.
235
+ */
236
+ export type RateLimitedEventInit<E = RateLimitedEvent> = E extends RateLimitedEvent ? Omit<E, "retryAfterMs" | "enforced"> : never;
237
+ /**
238
+ * Deliver an event to an optional {@link IdentityEventHook}, upholding the
239
+ * hook contract: a rejection is logged (prefixed with `label`), never
240
+ * rethrown. Every identity-layer service emits through this.
241
+ */
242
+ export declare function dispatchIdentityEvent(hook: IdentityEventHook | undefined, label: string, event: IdentityEvent): Promise<void>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Identity flow events — the observability seam for audit capture.
3
+ *
4
+ * {@link IdentityService} emits an {@link IdentityEvent} after each significant
5
+ * auth outcome (sign-in success/failure, sign-up, credential upgrade-on-login,
6
+ * reset and verification lifecycle, passwordless sign-in links and codes),
7
+ * `MfaService` emits the `mfa.*` events, and a password-policy validator emits
8
+ * `password_policy.check_unavailable` when it cannot complete its check. Wire
9
+ * an {@link IdentityEventHook} via the service's `onEvent`
10
+ * option to persist them in your own audit store — storage stays app-owned,
11
+ * like every other seam in this layer.
12
+ *
13
+ * Some outcomes are *only* visible here, because the flow that produced them
14
+ * resolves uniformly by design: `delivery.failed` is the sole in-band signal
15
+ * that a message never went out (and, when `invalidated` is `false`, that a
16
+ * live credential the user never received is still outstanding). Treat this
17
+ * hook as a monitored surface, not just an audit log.
18
+ *
19
+ * Events describe internal outcomes (including whether an identifier resolved
20
+ * to an account), so they are for server-side capture only — never surface
21
+ * their contents to the end user, or they become an enumeration oracle.
22
+ *
23
+ * @module
24
+ */
25
+ /**
26
+ * Deliver an event to an optional {@link IdentityEventHook}, upholding the
27
+ * hook contract: a rejection is logged (prefixed with `label`), never
28
+ * rethrown. Every identity-layer service emits through this.
29
+ */
30
+ export async function dispatchIdentityEvent(hook, label, event) {
31
+ if (!hook)
32
+ return;
33
+ try {
34
+ await hook(event);
35
+ }
36
+ catch (error) {
37
+ console.error(`[@udibo/oauth2] ${label} onEvent hook failed:`, error);
38
+ }
39
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Internal helpers shared by the external-provider connectors.
3
+ *
4
+ * Not part of the public API — reach for a connector from
5
+ * `@udibo/oauth2/identity/external` instead, which re-exports the one type an
6
+ * adopter configures ({@link AzpPolicy}). These live here so the same failure
7
+ * reads the same way whichever connector hit it: one error renderer, one
8
+ * error-body reader, one provider-text sanitizer, and one id_token claim gate
9
+ * rather than a copy per provider. The token exchange itself is
10
+ * `_token-exchange.ts`.
11
+ *
12
+ * @module
13
+ */
14
+ import { PROVIDER_TEXT_MAX_LENGTH, sanitizeProviderText } from "../../utils/text.js";
15
+ export { PROVIDER_TEXT_MAX_LENGTH, sanitizeProviderText };
16
+ /**
17
+ * Clock-skew allowance, in seconds, when checking an id_token's `exp` claim.
18
+ * The default for {@link assertIdTokenClaims}.
19
+ */
20
+ export declare const ID_TOKEN_EXPIRY_LEEWAY_SECONDS = 120;
21
+ /**
22
+ * Renders a thrown value for an error message, unwrapping an OAuth2 error into
23
+ * its `error` / `error_description` extensions so a provider-reported failure
24
+ * is quoted rather than buried in a generic message. Every branch is
25
+ * {@link sanitizeProviderText}-bounded: the thrown value routinely carries
26
+ * provider bytes — a JSON parse error quotes the body it choked on, and an
27
+ * OAuth2 error carries a description the provider wrote.
28
+ */
29
+ export declare function describeError(error: unknown): string;
30
+ /**
31
+ * Reads a failed response's body into a bounded, single-line `": <text>"`
32
+ * suffix to append to an error message, or `""` when the body is empty or
33
+ * unreadable. Consumes the body, so the caller must not read it again.
34
+ */
35
+ export declare function readErrorBody(res: Response): Promise<string>;
36
+ /**
37
+ * Drops one trailing `"/"` so issuer URLs compare and concatenate consistently
38
+ * (`"https://x/"` and `"https://x"` identify the same issuer).
39
+ */
40
+ export declare function stripTrailingSlash(value: string): string;
41
+ /**
42
+ * How {@link assertIdTokenClaims} treats an id_token's `azp` (authorized
43
+ * party) claim.
44
+ *
45
+ * - `"conditional"` — OpenID Connect Core §3.1.3.7: when the token carries
46
+ * multiple audiences or an `azp` claim, `azp` must be this client id.
47
+ * - `"ignore"` — skip the check. For a provider that legitimately reports a
48
+ * *different* authorized party than the client doing the exchange: Google's
49
+ * cross-client identity, where another client of the same project requests the
50
+ * token (`azp` is that client id) for this client's audience (`aud`), is the
51
+ * canonical case. Weakens §3.1.3.7 rules 4 and 5, so `aud` remains the only
52
+ * check binding the token to this registration — reach for it only when a real
53
+ * provider forces it. Configure it on
54
+ * {@link OidcProviderOptions.azp}/{@link GoogleProviderOptions.azp}.
55
+ */
56
+ export type AzpPolicy = "conditional" | "ignore";
57
+ /** Input for {@link assertIdTokenClaims}. */
58
+ export interface IdTokenClaimsInput {
59
+ /** Provider id carried on the thrown {@link ExternalAuthError}. */
60
+ provider: string;
61
+ /** The decoded id_token claims. */
62
+ claims: Record<string, unknown>;
63
+ /** The issuer the `iss` claim must equal exactly. */
64
+ issuer: string;
65
+ /** The client id the `aud` claim must contain. */
66
+ clientId: string;
67
+ /** The nonce sent on the authorize request; omit when none was sent. */
68
+ expectedNonce?: string;
69
+ /** How to check `azp`. Defaults to `"conditional"`. */
70
+ azp?: AzpPolicy;
71
+ /**
72
+ * Clock-skew allowance for `exp`, in seconds. Defaults to
73
+ * {@link ID_TOKEN_EXPIRY_LEEWAY_SECONDS}.
74
+ */
75
+ leewaySeconds?: number;
76
+ }
77
+ /**
78
+ * Enforces the id_token claim rules of OpenID Connect Core §3.1.3.7 that do not
79
+ * depend on the signature: `iss`, `aud`, `azp`, `exp`, and `nonce`, in that
80
+ * order. Callers verify the signature (or establish the token's provenance)
81
+ * themselves before calling this.
82
+ *
83
+ * Throws {@link ExternalAuthError} with code `nonce_mismatch` when the `nonce`
84
+ * claim does not match, and `provider_error` for every other rule.
85
+ */
86
+ export declare function assertIdTokenClaims(input: IdTokenClaimsInput): void;