@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,102 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
3
+ import { AuthFormClassNamesScope, useAuthFormClassNames, } from "./class-names.js";
4
+ /**
5
+ * The single exit every default form component returns through — it owns both
6
+ * outcomes, so a form has one `return` and cannot leave a subtree unscoped.
7
+ *
8
+ * With no `render`, it lays out the markup the forms share: the root wrapper
9
+ * carrying `data-oauth2-form={name}`, then the `<form>` with its error summary
10
+ * and the caller's fields. Use `aboveForm` for content that belongs inside the
11
+ * root but outside the form (social buttons, an enrollment secret), and
12
+ * `succeeded` for a terminal state that replaces the form entirely. With a
13
+ * `render` — the form's render-prop `children` — that markup is skipped for the
14
+ * caller's own. Either way the already-resolved `classNames` is published to
15
+ * what renders, so the elements inside take their classes from the tree.
16
+ */
17
+ export function AuthFormShell(props) {
18
+ const { name, form, className, classNames, render, rootProps, aboveForm, succeeded, children, action, method, formRef, } = props;
19
+ return (_jsx(AuthFormClassNamesScope, { classNames: classNames, children: render ? render(form) : (_jsxs("div", { "data-oauth2-form": name, ...rootProps, className: combineClassNames(className, classNames.root), children: [aboveForm, succeeded ?? (_jsxs("form", { onSubmit: form.handleSubmit, action: action, method: method ?? (action ? "post" : undefined), ref: formRef, noValidate: true, "data-oauth2-form-element": "", className: classNames.form, children: [_jsx(ErrorSummary, { error: form.formError, focusTrigger: form.submitCount }), children] }))] })) }));
20
+ }
21
+ /** The actions row and its submit button, disabled while the form is in flight. */
22
+ export function SubmitButton(props) {
23
+ const { isSubmitting, label, submittingLabel } = props;
24
+ const classNames = useAuthFormClassNames();
25
+ return (_jsx("div", { "data-oauth2-actions": "", className: classNames.actions, children: _jsx("button", { type: "submit", disabled: isSubmitting, "data-oauth2-submit": "", className: classNames.submit, children: isSubmitting ? submittingLabel : label }) }));
26
+ }
27
+ export function TextField(props) {
28
+ const { fieldProps, label, type = "text", autoComplete, required, error, inputRef, children, } = props;
29
+ const classNames = useAuthFormClassNames();
30
+ return (_jsxs("div", { "data-oauth2-field": fieldProps.name, className: classNames.field, children: [_jsx("label", { htmlFor: fieldProps.id, "data-oauth2-label": "", className: classNames.label, children: label }), _jsx("input", { ...fieldProps, type: type, autoComplete: autoComplete, required: required, "data-oauth2-input": "", className: classNames.input, ref: inputRef }), children, error
31
+ ? (_jsx("p", { id: `${fieldProps.id}-error`, role: "alert", "data-oauth2-error": "", className: classNames.error, children: error }))
32
+ : null] }));
33
+ }
34
+ export function ErrorSummary(props) {
35
+ const { error, focusTrigger } = props;
36
+ const classNames = useAuthFormClassNames();
37
+ const ref = useRef(null);
38
+ useEffect(() => {
39
+ if (error)
40
+ ref.current?.focus();
41
+ }, [error, focusTrigger]);
42
+ if (!error)
43
+ return null;
44
+ return (_jsx("div", { ref: ref, tabIndex: -1, "data-oauth2-error-summary": "", className: classNames.errorSummary, children: _jsx("span", { role: "alert", "data-oauth2-error-summary-message": "", children: error }) }));
45
+ }
46
+ /**
47
+ * Moves keyboard focus to the first field that came back with an error after a
48
+ * failed submit, so a screen-reader / keyboard user lands on the control to fix
49
+ * (its label + error are read via `aria-describedby`) instead of being stranded
50
+ * on the disabled submit button. No-ops on the initial render and when the form
51
+ * carries a form-level error ({@link ErrorSummary} owns that focus). Call it
52
+ * once, unconditionally, from a form component built on {@link useAuthForm}.
53
+ */
54
+ export function useFocusFirstError(form) {
55
+ const handled = useRef(0);
56
+ useEffect(() => {
57
+ if (form.status !== "error")
58
+ return;
59
+ if (handled.current === form.submitCount)
60
+ return;
61
+ handled.current = form.submitCount;
62
+ if (form.formError)
63
+ return;
64
+ const names = Object.keys(form.values);
65
+ const firstErrored = names.find((name) => Boolean(form.errors[name]));
66
+ if (!firstErrored)
67
+ return;
68
+ const { id } = form.getFieldProps(firstErrored);
69
+ if (typeof document === "undefined")
70
+ return;
71
+ document.getElementById(id)?.focus();
72
+ }, [form.status, form.submitCount, form.formError]);
73
+ }
74
+ export function SocialButtons(props) {
75
+ const { socialProviders, onSocialSelect, socialHref } = props;
76
+ const classNames = useAuthFormClassNames();
77
+ if (!socialProviders || socialProviders.length === 0)
78
+ return null;
79
+ const label = (provider) => (_jsxs(_Fragment, { children: [provider.iconSlot
80
+ ? _jsx("span", { "data-oauth2-social-icon": "", children: provider.iconSlot })
81
+ : null, _jsxs("span", { "data-oauth2-social-label": "", children: ["Continue with ", provider.name] })] }));
82
+ return (_jsxs(_Fragment, { children: [_jsx("div", { "data-oauth2-social": "", className: classNames.socialSection, children: socialProviders.map((provider) => socialHref
83
+ ? (_jsx("a", { href: socialHref(provider), "data-oauth2-social-button": provider.id, className: classNames.socialButton, children: label(provider) }, provider.id))
84
+ : (_jsx("button", { type: "button", onClick: () => onSocialSelect?.(provider), "data-oauth2-social-button": provider.id, className: classNames.socialButton, children: label(provider) }, provider.id))) }), _jsx("div", { "data-oauth2-social-divider": "", "aria-hidden": "true", className: classNames.socialDivider })] }));
85
+ }
86
+ export function combineClassNames(className, slot) {
87
+ return [className, slot].filter(Boolean).join(" ") || undefined;
88
+ }
89
+ export function displayName(user) {
90
+ const candidates = [
91
+ user.name,
92
+ user.preferred_username,
93
+ user.nickname,
94
+ user.email,
95
+ user.sub,
96
+ ];
97
+ for (const candidate of candidates) {
98
+ if (typeof candidate === "string" && candidate.length > 0)
99
+ return candidate;
100
+ }
101
+ return "Account";
102
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * {@link MfaChallengeForm} — a default, accessible, unstyled second-factor
3
+ * challenge form built on {@link useAuthForm}. Collects a one-time code and,
4
+ * when allowed, toggles to accepting a recovery code instead. The active
5
+ * method is forwarded to `onSubmit` so your endpoint knows how to verify it.
6
+ *
7
+ * @module
8
+ */
9
+ import type { ReactNode } from "react";
10
+ import type { AuthFormState } from "./use-auth-form.js";
11
+ import type { BaseAuthFormProps } from "./types.js";
12
+ /** Which credential the user is submitting. */
13
+ export type MfaChallengeMethod = "totp" | "recovery";
14
+ /** Values collected by {@link MfaChallengeForm}. */
15
+ export type MfaChallengeValues = {
16
+ /** The one-time code or recovery code the user entered. */
17
+ code: string;
18
+ /** Which credential `code` represents. */
19
+ method: MfaChallengeMethod;
20
+ };
21
+ /** Visible text overrides for {@link MfaChallengeForm}. */
22
+ export interface MfaChallengeLabels {
23
+ /** Label for the TOTP code field. Default: `"Authentication code"`. */
24
+ code?: string;
25
+ /** Label for the recovery-code field. Default: `"Recovery code"`. */
26
+ recoveryCode?: string;
27
+ /** Submit button text. Default: `"Verify"`. */
28
+ submit?: string;
29
+ /** Submit button text while submitting. Default: `"Verifying…"`. */
30
+ submitting?: string;
31
+ /** Toggle text to switch to recovery mode. Default: `"Use a recovery code"`. */
32
+ useRecovery?: string;
33
+ /** Toggle text to switch back to code mode. Default: `"Use your authenticator app"`. */
34
+ useAuthenticator?: string;
35
+ }
36
+ /** Props for {@link MfaChallengeForm}. */
37
+ export interface MfaChallengeFormProps extends BaseAuthFormProps<MfaChallengeValues> {
38
+ /** Offer the "use a recovery code" toggle. Default: `true`. */
39
+ allowRecoveryCode?: boolean;
40
+ /** Text overrides. */
41
+ labels?: MfaChallengeLabels;
42
+ /**
43
+ * Render-prop escape hatch. When provided, you own the markup and receive
44
+ * the live {@link AuthFormState}. Toggle `method` with `setValue`.
45
+ */
46
+ children?: (state: AuthFormState<MfaChallengeValues>) => ReactNode;
47
+ }
48
+ /**
49
+ * A ready-to-use MFA challenge form. Wire `onSubmit` to your verification
50
+ * endpoint; inspect `values.method` to decide how to validate `values.code`.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * <MfaChallengeForm
55
+ * onSubmit={async (values) => {
56
+ * const res = await fetch("/auth/mfa/verify", {
57
+ * method: "POST",
58
+ * headers: { "content-type": "application/json" },
59
+ * body: JSON.stringify(values),
60
+ * });
61
+ * if (!res.ok) return { error: "That code was not valid." };
62
+ * }}
63
+ * />
64
+ * ```
65
+ */
66
+ export declare function MfaChallengeForm(props: MfaChallengeFormProps): ReactNode;
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuthForm } from "./use-auth-form.js";
3
+ import { AuthFormShell, SubmitButton, TextField, useFocusFirstError, } from "./internal.js";
4
+ import { useAuthFormClassNames } from "./class-names.js";
5
+ /**
6
+ * A ready-to-use MFA challenge form. Wire `onSubmit` to your verification
7
+ * endpoint; inspect `values.method` to decide how to validate `values.code`.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <MfaChallengeForm
12
+ * onSubmit={async (values) => {
13
+ * const res = await fetch("/auth/mfa/verify", {
14
+ * method: "POST",
15
+ * headers: { "content-type": "application/json" },
16
+ * body: JSON.stringify(values),
17
+ * });
18
+ * if (!res.ok) return { error: "That code was not valid." };
19
+ * }}
20
+ * />
21
+ * ```
22
+ */
23
+ export function MfaChallengeForm(props) {
24
+ const { onSubmit, className, allowRecoveryCode = true, labels, children, } = props;
25
+ const form = useAuthForm({
26
+ initialValues: { code: "", method: "totp" },
27
+ onSubmit,
28
+ onSuccess: props.onSuccess,
29
+ });
30
+ useFocusFirstError(form);
31
+ const classNames = useAuthFormClassNames(props.classNames);
32
+ const isRecovery = form.values.method === "recovery";
33
+ const toggleMethod = () => {
34
+ form.setValues({
35
+ method: isRecovery ? "totp" : "recovery",
36
+ code: "",
37
+ });
38
+ };
39
+ return (_jsxs(AuthFormShell, { name: "mfa-challenge", form: form, className: className, classNames: classNames, render: children, action: props.action, method: props.method, formRef: props.formRef, rootProps: { "data-oauth2-method": form.values.method }, children: [_jsx(TextField, { fieldProps: form.getFieldProps("code"), label: isRecovery
40
+ ? labels?.recoveryCode ?? "Recovery code"
41
+ : labels?.code ?? "Authentication code", autoComplete: isRecovery ? "off" : "one-time-code", required: true, error: form.errors.code }), _jsx(SubmitButton, { isSubmitting: form.isSubmitting, label: labels?.submit ?? "Verify", submittingLabel: labels?.submitting ?? "Verifying…" }), allowRecoveryCode
42
+ ? (_jsx("button", { type: "button", onClick: toggleMethod, "data-oauth2-secondary": "", "data-oauth2-toggle-recovery": "", className: classNames.secondary, children: isRecovery
43
+ ? labels?.useAuthenticator ?? "Use your authenticator app"
44
+ : labels?.useRecovery ?? "Use a recovery code" }))
45
+ : null] }));
46
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * {@link MfaEnrollmentForm} — a default, accessible, unstyled TOTP enrollment
3
+ * panel built on {@link useAuthForm}. Displays the shared secret / `otpauth://`
4
+ * URI (with a slot for your own QR renderer) and recovery codes, then collects
5
+ * a confirmation code to prove the authenticator is set up.
6
+ *
7
+ * The package ships no QR-code or crypto dependency: pass a rendered QR node
8
+ * via `qrSlot`, and provide the secret / URI / recovery codes as data.
9
+ *
10
+ * @module
11
+ */
12
+ import type { ReactNode } from "react";
13
+ import type { AuthFormState } from "./use-auth-form.js";
14
+ import type { BaseAuthFormProps } from "./types.js";
15
+ /** The enrollment secret material to display, produced server-side. */
16
+ export interface MfaEnrollmentData {
17
+ /** Base32 shared secret for manual entry into an authenticator app. */
18
+ secret: string;
19
+ /** The full `otpauth://totp/...` provisioning URI (encode as a QR code). */
20
+ otpauthUri: string;
21
+ /** One-time recovery codes to show once and have the user save. */
22
+ recoveryCodes?: string[];
23
+ }
24
+ /** Values collected by {@link MfaEnrollmentForm}'s confirmation step. */
25
+ export type MfaEnrollmentValues = {
26
+ /** The code from the authenticator app confirming enrollment. */
27
+ code: string;
28
+ };
29
+ /** Visible text overrides for {@link MfaEnrollmentForm}. */
30
+ export interface MfaEnrollmentLabels {
31
+ /** Heading above the secret. Default: `"Scan this QR code"`. */
32
+ secretHeading?: string;
33
+ /** Prefix before the manual secret. Default: `"Or enter this code:"`. */
34
+ secretHint?: string;
35
+ /** Heading above recovery codes. Default: `"Save your recovery codes"`. */
36
+ recoveryHeading?: string;
37
+ /** Label for the confirmation code field. Default: `"Authentication code"`. */
38
+ code?: string;
39
+ /** Submit button text. Default: `"Confirm"`. */
40
+ submit?: string;
41
+ /** Submit button text while submitting. Default: `"Confirming…"`. */
42
+ submitting?: string;
43
+ }
44
+ /** Props for {@link MfaEnrollmentForm}. */
45
+ export interface MfaEnrollmentFormProps extends BaseAuthFormProps<MfaEnrollmentValues> {
46
+ /** The secret, URI, and recovery codes to display. */
47
+ data: MfaEnrollmentData;
48
+ /**
49
+ * Your rendered QR code for `data.otpauthUri` (the package ships no QR
50
+ * dependency). A node, or a function of the URI.
51
+ */
52
+ qrSlot?: ReactNode | ((otpauthUri: string) => ReactNode);
53
+ /** Text overrides. */
54
+ labels?: MfaEnrollmentLabels;
55
+ /**
56
+ * Render-prop escape hatch. When provided, you own all of the markup —
57
+ * including the secret / QR / recovery-code display — and receive the live
58
+ * {@link AuthFormState}; the component still owns state and submit.
59
+ */
60
+ children?: (state: AuthFormState<MfaEnrollmentValues>) => ReactNode;
61
+ }
62
+ /**
63
+ * A ready-to-use TOTP enrollment panel: shows the QR/secret and recovery
64
+ * codes, then confirms via a code. Wire `onSubmit` to your verification
65
+ * endpoint and pass a rendered QR via `qrSlot`.
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * <MfaEnrollmentForm
70
+ * data={{ secret, otpauthUri, recoveryCodes }}
71
+ * qrSlot={(uri) => <QRCode value={uri} />}
72
+ * onSubmit={async ({ code }) => {
73
+ * const res = await fetch("/auth/mfa/enroll/confirm", {
74
+ * method: "POST",
75
+ * headers: { "content-type": "application/json" },
76
+ * body: JSON.stringify({ code }),
77
+ * });
78
+ * if (!res.ok) return { error: "That code was not valid." };
79
+ * }}
80
+ * />
81
+ * ```
82
+ */
83
+ export declare function MfaEnrollmentForm(props: MfaEnrollmentFormProps): ReactNode;
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useAuthForm } from "./use-auth-form.js";
3
+ import { AuthFormShell, SubmitButton, TextField, useFocusFirstError, } from "./internal.js";
4
+ import { useAuthFormClassNames } from "./class-names.js";
5
+ /**
6
+ * A ready-to-use TOTP enrollment panel: shows the QR/secret and recovery
7
+ * codes, then confirms via a code. Wire `onSubmit` to your verification
8
+ * endpoint and pass a rendered QR via `qrSlot`.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <MfaEnrollmentForm
13
+ * data={{ secret, otpauthUri, recoveryCodes }}
14
+ * qrSlot={(uri) => <QRCode value={uri} />}
15
+ * onSubmit={async ({ code }) => {
16
+ * const res = await fetch("/auth/mfa/enroll/confirm", {
17
+ * method: "POST",
18
+ * headers: { "content-type": "application/json" },
19
+ * body: JSON.stringify({ code }),
20
+ * });
21
+ * if (!res.ok) return { error: "That code was not valid." };
22
+ * }}
23
+ * />
24
+ * ```
25
+ */
26
+ export function MfaEnrollmentForm(props) {
27
+ const { data, onSubmit, qrSlot, className, labels, children } = props;
28
+ const form = useAuthForm({
29
+ initialValues: { code: "" },
30
+ onSubmit,
31
+ onSuccess: props.onSuccess,
32
+ });
33
+ useFocusFirstError(form);
34
+ const classNames = useAuthFormClassNames(props.classNames);
35
+ const qr = typeof qrSlot === "function" ? qrSlot(data.otpauthUri) : qrSlot;
36
+ return (_jsxs(AuthFormShell, { name: "mfa-enrollment", form: form, className: className, classNames: classNames, render: children, action: props.action, method: props.method, formRef: props.formRef, aboveForm: _jsxs(_Fragment, { children: [_jsxs("div", { "data-oauth2-secret": "", className: classNames.secret, children: [_jsx("p", { role: "heading", "aria-level": 2, "data-oauth2-secret-heading": "", children: labels?.secretHeading ?? "Scan this QR code" }), qr
37
+ ? _jsx("div", { "data-oauth2-qr": "", className: classNames.qr, children: qr })
38
+ : null, _jsxs("p", { "data-oauth2-secret-hint": "", children: [labels?.secretHint ?? "Or enter this code:", " ", _jsx("code", { "data-oauth2-secret-value": "", children: data.secret })] })] }), data.recoveryCodes && data.recoveryCodes.length > 0
39
+ ? (_jsxs("div", { "data-oauth2-recovery": "", children: [_jsx("p", { role: "heading", "aria-level": 2, "data-oauth2-recovery-heading": "", children: labels?.recoveryHeading ?? "Save your recovery codes" }), _jsx("ul", { "data-oauth2-recovery-codes": "", className: classNames.recoveryCodes, children: data.recoveryCodes.map((recoveryCode) => (_jsx("li", { "data-oauth2-recovery-code": "", className: classNames.recoveryCode, children: _jsx("code", { children: recoveryCode }) }, recoveryCode))) })] }))
40
+ : null] }), children: [_jsx(TextField, { fieldProps: form.getFieldProps("code"), label: labels?.code ?? "Authentication code", autoComplete: "one-time-code", required: true, error: form.errors.code }), _jsx(SubmitButton, { isSubmitting: form.isSubmitting, label: labels?.submit ?? "Confirm", submittingLabel: labels?.submitting ?? "Confirming…" })] }));
41
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Prebuilt React auth components for `@udibo/oauth2` — the composable,
3
+ * headless-first, themeable form set an app assembles into its own auth
4
+ * surface. Works in both plain React Router v7 and Juniper (SSR React).
5
+ *
6
+ * These components are **composed by your app**: you own the routes, storage,
7
+ * session, and the submit wiring. Nothing here mounts routes, talks to a
8
+ * server on its own, or assumes a design system — every component ships
9
+ * unstyled with per-slot `className` hooks and stable `data-oauth2-*`
10
+ * attributes, and takes its action via an injected `onSubmit` prop so the same
11
+ * component drives a client-side `fetch` or a router action.
12
+ *
13
+ * ## Two integration modes
14
+ *
15
+ * - **Default components** — {@link SignInForm}, {@link SignUpForm},
16
+ * {@link RequestPasswordResetForm}, {@link ResetPasswordForm},
17
+ * {@link MfaEnrollmentForm}, {@link MfaChallengeForm}, {@link UserMenu} —
18
+ * render accessible markup with class hooks.
19
+ * - **Headless** — {@link useAuthForm} owns values, errors, and the submit
20
+ * lifecycle with no markup. Every default component is built on it; "eject"
21
+ * by using the hook directly, or by passing a function as a component's
22
+ * `children` (render prop) to keep the component's wiring but own the markup.
23
+ *
24
+ * Social buttons appear automatically on the sign-in / sign-up components when
25
+ * you pass a `socialProviders` array.
26
+ *
27
+ * ## Server-rendered pages
28
+ *
29
+ * Pass `action` + `method` and the rendered `<form>` carries them, so a
30
+ * server-rendered page still posts when JavaScript never arrives — every field
31
+ * carries its `name`, so the native post sends the same keys `onSubmit`
32
+ * receives. Once React is running, `onSubmit` handles the submit and the
33
+ * native post is suppressed. `formRef` hands you the same `<form>` element, for
34
+ * example to trigger a native `submit()` yourself.
35
+ *
36
+ * ## Theming
37
+ *
38
+ * Every form takes a per-slot {@link AuthFormClassNames} map and publishes it
39
+ * to the elements it renders. Wrap a subtree in
40
+ * {@link AuthFormClassNamesProvider} to theme every form under it at once — a
41
+ * form's own `classNames` overrides the inherited map slot by slot — and read
42
+ * the map in effect from your own markup with {@link useAuthFormClassNames}.
43
+ *
44
+ * The claims type {@link UserMenu} hands to `renderUser` / `children` is
45
+ * `UserInfoClaims`; import it from `@udibo/oauth2/client`, which owns it.
46
+ *
47
+ * ## Accessibility
48
+ *
49
+ * These components target **WCAG 2.2 level AA** and are accessible by default,
50
+ * so dropping them in does not quietly ship a barrier:
51
+ *
52
+ * - Every field is programmatically labelled (`<label htmlFor>` ↔ input `id`);
53
+ * errors set `aria-invalid` + `aria-describedby` and render `role="alert"`.
54
+ * - A failed submit moves focus to the first errored field (or the form-level
55
+ * error summary), so a keyboard / screen-reader user lands on what to fix.
56
+ * - One-time-code fields use `autocomplete="one-time-code"` and **never block
57
+ * paste** (WCAG 2.2 §3.3.8 Accessible Authentication).
58
+ * - Section titles carry heading semantics; the components ship unstyled, so
59
+ * **contrast and visible focus are yours to satisfy** with your own theme —
60
+ * the ratios and focus rules Udibo uses are in its `DESIGN.md`.
61
+ *
62
+ * @example Plain React Router v7
63
+ * ```tsx
64
+ * import { SignInForm } from "@udibo/oauth2/react/components";
65
+ *
66
+ * function SignIn() {
67
+ * return (
68
+ * <SignInForm
69
+ * onSubmit={async (values) => {
70
+ * const res = await fetch("/auth/sign-in", {
71
+ * method: "POST",
72
+ * headers: { "content-type": "application/json" },
73
+ * body: JSON.stringify(values),
74
+ * });
75
+ * if (!res.ok) return await res.json();
76
+ * }}
77
+ * />
78
+ * );
79
+ * }
80
+ * ```
81
+ *
82
+ * @module
83
+ */
84
+ import "../../_dnt.polyfills.js";
85
+ export { useAuthForm } from "./use-auth-form.js";
86
+ export type { AuthFieldProps, AuthFormState, AuthFormStatus, UseAuthFormOptions, } from "./use-auth-form.js";
87
+ export type { AuthFormClassNames, AuthFormErrors, AuthFormResult, AuthFormValues, AuthSubmitHandler, BaseAuthFormProps, BaseClassNames, SocialProvider, SocialProvidersProps, } from "./types.js";
88
+ export { AuthFormClassNamesProvider, useAuthFormClassNames, } from "./class-names.js";
89
+ export type { AuthFormClassNamesProviderProps } from "./class-names.js";
90
+ export { SignInForm } from "./sign-in-form.js";
91
+ export type { SignInFormProps, SignInLabels, SignInValues, } from "./sign-in-form.js";
92
+ export { SignUpForm } from "./sign-up-form.js";
93
+ export type { SignUpFormProps, SignUpLabels, SignUpValues, } from "./sign-up-form.js";
94
+ export { RequestPasswordResetForm } from "./request-password-reset-form.js";
95
+ export type { RequestPasswordResetFormProps, RequestPasswordResetLabels, RequestPasswordResetValues, } from "./request-password-reset-form.js";
96
+ export { ResetPasswordForm } from "./reset-password-form.js";
97
+ export type { ResetPasswordFormProps, ResetPasswordLabels, ResetPasswordValues, } from "./reset-password-form.js";
98
+ export { MfaEnrollmentForm } from "./mfa-enrollment-form.js";
99
+ export type { MfaEnrollmentData, MfaEnrollmentFormProps, MfaEnrollmentLabels, MfaEnrollmentValues, } from "./mfa-enrollment-form.js";
100
+ export { MfaChallengeForm } from "./mfa-challenge-form.js";
101
+ export type { MfaChallengeFormProps, MfaChallengeLabels, MfaChallengeMethod, MfaChallengeValues, } from "./mfa-challenge-form.js";
102
+ export { UserMenu } from "./user-menu.js";
103
+ export type { UserMenuClassNames, UserMenuLabels, UserMenuProps, } from "./user-menu.js";
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Prebuilt React auth components for `@udibo/oauth2` — the composable,
3
+ * headless-first, themeable form set an app assembles into its own auth
4
+ * surface. Works in both plain React Router v7 and Juniper (SSR React).
5
+ *
6
+ * These components are **composed by your app**: you own the routes, storage,
7
+ * session, and the submit wiring. Nothing here mounts routes, talks to a
8
+ * server on its own, or assumes a design system — every component ships
9
+ * unstyled with per-slot `className` hooks and stable `data-oauth2-*`
10
+ * attributes, and takes its action via an injected `onSubmit` prop so the same
11
+ * component drives a client-side `fetch` or a router action.
12
+ *
13
+ * ## Two integration modes
14
+ *
15
+ * - **Default components** — {@link SignInForm}, {@link SignUpForm},
16
+ * {@link RequestPasswordResetForm}, {@link ResetPasswordForm},
17
+ * {@link MfaEnrollmentForm}, {@link MfaChallengeForm}, {@link UserMenu} —
18
+ * render accessible markup with class hooks.
19
+ * - **Headless** — {@link useAuthForm} owns values, errors, and the submit
20
+ * lifecycle with no markup. Every default component is built on it; "eject"
21
+ * by using the hook directly, or by passing a function as a component's
22
+ * `children` (render prop) to keep the component's wiring but own the markup.
23
+ *
24
+ * Social buttons appear automatically on the sign-in / sign-up components when
25
+ * you pass a `socialProviders` array.
26
+ *
27
+ * ## Server-rendered pages
28
+ *
29
+ * Pass `action` + `method` and the rendered `<form>` carries them, so a
30
+ * server-rendered page still posts when JavaScript never arrives — every field
31
+ * carries its `name`, so the native post sends the same keys `onSubmit`
32
+ * receives. Once React is running, `onSubmit` handles the submit and the
33
+ * native post is suppressed. `formRef` hands you the same `<form>` element, for
34
+ * example to trigger a native `submit()` yourself.
35
+ *
36
+ * ## Theming
37
+ *
38
+ * Every form takes a per-slot {@link AuthFormClassNames} map and publishes it
39
+ * to the elements it renders. Wrap a subtree in
40
+ * {@link AuthFormClassNamesProvider} to theme every form under it at once — a
41
+ * form's own `classNames` overrides the inherited map slot by slot — and read
42
+ * the map in effect from your own markup with {@link useAuthFormClassNames}.
43
+ *
44
+ * The claims type {@link UserMenu} hands to `renderUser` / `children` is
45
+ * `UserInfoClaims`; import it from `@udibo/oauth2/client`, which owns it.
46
+ *
47
+ * ## Accessibility
48
+ *
49
+ * These components target **WCAG 2.2 level AA** and are accessible by default,
50
+ * so dropping them in does not quietly ship a barrier:
51
+ *
52
+ * - Every field is programmatically labelled (`<label htmlFor>` ↔ input `id`);
53
+ * errors set `aria-invalid` + `aria-describedby` and render `role="alert"`.
54
+ * - A failed submit moves focus to the first errored field (or the form-level
55
+ * error summary), so a keyboard / screen-reader user lands on what to fix.
56
+ * - One-time-code fields use `autocomplete="one-time-code"` and **never block
57
+ * paste** (WCAG 2.2 §3.3.8 Accessible Authentication).
58
+ * - Section titles carry heading semantics; the components ship unstyled, so
59
+ * **contrast and visible focus are yours to satisfy** with your own theme —
60
+ * the ratios and focus rules Udibo uses are in its `DESIGN.md`.
61
+ *
62
+ * @example Plain React Router v7
63
+ * ```tsx
64
+ * import { SignInForm } from "@udibo/oauth2/react/components";
65
+ *
66
+ * function SignIn() {
67
+ * return (
68
+ * <SignInForm
69
+ * onSubmit={async (values) => {
70
+ * const res = await fetch("/auth/sign-in", {
71
+ * method: "POST",
72
+ * headers: { "content-type": "application/json" },
73
+ * body: JSON.stringify(values),
74
+ * });
75
+ * if (!res.ok) return await res.json();
76
+ * }}
77
+ * />
78
+ * );
79
+ * }
80
+ * ```
81
+ *
82
+ * @module
83
+ */
84
+ import "../../_dnt.polyfills.js";
85
+ export { useAuthForm } from "./use-auth-form.js";
86
+ export { AuthFormClassNamesProvider, useAuthFormClassNames, } from "./class-names.js";
87
+ export { SignInForm } from "./sign-in-form.js";
88
+ export { SignUpForm } from "./sign-up-form.js";
89
+ export { RequestPasswordResetForm } from "./request-password-reset-form.js";
90
+ export { ResetPasswordForm } from "./reset-password-form.js";
91
+ export { MfaEnrollmentForm } from "./mfa-enrollment-form.js";
92
+ export { MfaChallengeForm } from "./mfa-challenge-form.js";
93
+ export { UserMenu } from "./user-menu.js";
@@ -0,0 +1,61 @@
1
+ /**
2
+ * {@link RequestPasswordResetForm} — a default, accessible, unstyled
3
+ * "forgot password" form built on {@link useAuthForm}. Collects an email and,
4
+ * on success, swaps the form for a confirmation message (a neutral one that
5
+ * doesn't reveal whether the address exists).
6
+ *
7
+ * @module
8
+ */
9
+ import type { ReactNode } from "react";
10
+ import type { AuthFormState } from "./use-auth-form.js";
11
+ import type { BaseAuthFormProps } from "./types.js";
12
+ /** Values collected by {@link RequestPasswordResetForm}. */
13
+ export type RequestPasswordResetValues = {
14
+ /** The email to send the reset link to. */
15
+ email: string;
16
+ };
17
+ /** Visible text overrides for {@link RequestPasswordResetForm}. */
18
+ export interface RequestPasswordResetLabels {
19
+ /** Label for the email field. Default: `"Email"`. */
20
+ email?: string;
21
+ /** Submit button text. Default: `"Send reset link"`. */
22
+ submit?: string;
23
+ /** Submit button text while submitting. Default: `"Sending…"`. */
24
+ submitting?: string;
25
+ /** Confirmation shown after success. Default: a neutral "check your email". */
26
+ success?: string;
27
+ }
28
+ /** Props for {@link RequestPasswordResetForm}. */
29
+ export interface RequestPasswordResetFormProps extends BaseAuthFormProps<RequestPasswordResetValues> {
30
+ /** Text overrides. */
31
+ labels?: RequestPasswordResetLabels;
32
+ /**
33
+ * Render-prop escape hatch. When provided, you own the markup and receive
34
+ * the live {@link AuthFormState} (including `succeeded`).
35
+ */
36
+ children?: (state: AuthFormState<RequestPasswordResetValues>) => ReactNode;
37
+ }
38
+ /**
39
+ * A ready-to-use "forgot password" request form. Wire `onSubmit` to your
40
+ * reset-request endpoint. Return no error to trigger the neutral success state.
41
+ *
42
+ * **Enumeration safety is the endpoint's job:** the success copy is neutral
43
+ * ("if an account exists…"), but this form renders whatever error `onSubmit`
44
+ * returns/throws. Point it at an enumeration-safe endpoint (like
45
+ * `IdentityService.requestPasswordReset`, which always resolves) — do **not**
46
+ * surface a "no such account" error, or the neutral copy is undone.
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * <RequestPasswordResetForm
51
+ * onSubmit={async ({ email }) => {
52
+ * await fetch("/auth/forgot-password", {
53
+ * method: "POST",
54
+ * headers: { "content-type": "application/json" },
55
+ * body: JSON.stringify({ email }),
56
+ * });
57
+ * }}
58
+ * />
59
+ * ```
60
+ */
61
+ export declare function RequestPasswordResetForm(props: RequestPasswordResetFormProps): ReactNode;
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuthForm } from "./use-auth-form.js";
3
+ import { AuthFormShell, SubmitButton, TextField, useFocusFirstError, } from "./internal.js";
4
+ import { useAuthFormClassNames } from "./class-names.js";
5
+ const DEFAULT_SUCCESS = "If an account exists for that email, a reset link is on its way.";
6
+ /**
7
+ * A ready-to-use "forgot password" request form. Wire `onSubmit` to your
8
+ * reset-request endpoint. Return no error to trigger the neutral success state.
9
+ *
10
+ * **Enumeration safety is the endpoint's job:** the success copy is neutral
11
+ * ("if an account exists…"), but this form renders whatever error `onSubmit`
12
+ * returns/throws. Point it at an enumeration-safe endpoint (like
13
+ * `IdentityService.requestPasswordReset`, which always resolves) — do **not**
14
+ * surface a "no such account" error, or the neutral copy is undone.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <RequestPasswordResetForm
19
+ * onSubmit={async ({ email }) => {
20
+ * await fetch("/auth/forgot-password", {
21
+ * method: "POST",
22
+ * headers: { "content-type": "application/json" },
23
+ * body: JSON.stringify({ email }),
24
+ * });
25
+ * }}
26
+ * />
27
+ * ```
28
+ */
29
+ export function RequestPasswordResetForm(props) {
30
+ const { onSubmit, className, labels, children } = props;
31
+ const form = useAuthForm({
32
+ initialValues: { email: "" },
33
+ onSubmit,
34
+ onSuccess: props.onSuccess,
35
+ });
36
+ useFocusFirstError(form);
37
+ const classNames = useAuthFormClassNames(props.classNames);
38
+ return (_jsxs(AuthFormShell, { name: "request-password-reset", form: form, className: className, classNames: classNames, render: children, action: props.action, method: props.method, formRef: props.formRef, succeeded: form.succeeded
39
+ ? (_jsx("div", { "data-oauth2-success": "", role: "status", className: classNames.success, children: labels?.success ?? DEFAULT_SUCCESS }))
40
+ : null, children: [_jsx(TextField, { fieldProps: form.getFieldProps("email"), label: labels?.email ?? "Email", type: "email", autoComplete: "email", required: true, error: form.errors.email }), _jsx(SubmitButton, { isSubmitting: form.isSubmitting, label: labels?.submit ?? "Send reset link", submittingLabel: labels?.submitting ?? "Sending…" })] }));
41
+ }