@atproto/oauth-provider 0.4.0 → 0.5.1

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 (402) hide show
  1. package/.linguirc +57 -0
  2. package/CHANGELOG.md +29 -0
  3. package/dist/account/account-manager.d.ts +17 -3
  4. package/dist/account/account-manager.d.ts.map +1 -1
  5. package/dist/account/account-manager.js +102 -8
  6. package/dist/account/account-manager.js.map +1 -1
  7. package/dist/account/account-store.d.ts +81 -15
  8. package/dist/account/account-store.d.ts.map +1 -1
  9. package/dist/account/account-store.js +70 -19
  10. package/dist/account/account-store.js.map +1 -1
  11. package/dist/account/sign-in-data.d.ts +28 -0
  12. package/dist/account/sign-in-data.d.ts.map +1 -0
  13. package/dist/account/sign-in-data.js +16 -0
  14. package/dist/account/sign-in-data.js.map +1 -0
  15. package/dist/account/sign-up-data.d.ts +26 -0
  16. package/dist/account/sign-up-data.d.ts.map +1 -0
  17. package/dist/account/sign-up-data.js +11 -0
  18. package/dist/account/sign-up-data.js.map +1 -0
  19. package/dist/assets/app/bundle-manifest.json +598 -6
  20. package/dist/assets/app/index-ItwwtJ8r.js +36 -0
  21. package/dist/assets/app/index-ItwwtJ8r.js.map +1 -0
  22. package/dist/assets/app/main-B_dNxQo_.js +4 -0
  23. package/dist/assets/app/main-B_dNxQo_.js.map +1 -0
  24. package/dist/assets/app/main-CSatvmRR.css +3 -0
  25. package/dist/assets/app/main-CSatvmRR.js +306 -0
  26. package/dist/assets/app/main-CSatvmRR.js.map +1 -0
  27. package/dist/assets/app/messages-BQeltXSF.js +4 -0
  28. package/dist/assets/app/messages-BQeltXSF.js.map +1 -0
  29. package/dist/assets/app/messages-BQkEhfjg.js +4 -0
  30. package/dist/assets/app/messages-BQkEhfjg.js.map +1 -0
  31. package/dist/assets/app/messages-BUjKj_UJ.js +4 -0
  32. package/dist/assets/app/messages-BUjKj_UJ.js.map +1 -0
  33. package/dist/assets/app/messages-BWIQa8fO.js +4 -0
  34. package/dist/assets/app/messages-BWIQa8fO.js.map +1 -0
  35. package/dist/assets/app/messages-BaNVb0bp.js +4 -0
  36. package/dist/assets/app/messages-BaNVb0bp.js.map +1 -0
  37. package/dist/assets/app/messages-BaizVXcF.js +4 -0
  38. package/dist/assets/app/messages-BaizVXcF.js.map +1 -0
  39. package/dist/assets/app/messages-BfoClA1Y.js +4 -0
  40. package/dist/assets/app/messages-BfoClA1Y.js.map +1 -0
  41. package/dist/assets/app/messages-BsKGDZnC.js +4 -0
  42. package/dist/assets/app/messages-BsKGDZnC.js.map +1 -0
  43. package/dist/assets/app/messages-Bu-TJhml.js +4 -0
  44. package/dist/assets/app/messages-Bu-TJhml.js.map +1 -0
  45. package/dist/assets/app/messages-BvOKnBQk.js +4 -0
  46. package/dist/assets/app/messages-BvOKnBQk.js.map +1 -0
  47. package/dist/assets/app/messages-BxDzCiWz.js +4 -0
  48. package/dist/assets/app/messages-BxDzCiWz.js.map +1 -0
  49. package/dist/assets/app/messages-CDgFOy4S.js +4 -0
  50. package/dist/assets/app/messages-CDgFOy4S.js.map +1 -0
  51. package/dist/assets/app/messages-CLbTz0o9.js +4 -0
  52. package/dist/assets/app/messages-CLbTz0o9.js.map +1 -0
  53. package/dist/assets/app/messages-CNwSh0t7.js +4 -0
  54. package/dist/assets/app/messages-CNwSh0t7.js.map +1 -0
  55. package/dist/assets/app/messages-CSMNJ6P8.js +4 -0
  56. package/dist/assets/app/messages-CSMNJ6P8.js.map +1 -0
  57. package/dist/assets/app/messages-CZQUw3mp.js +4 -0
  58. package/dist/assets/app/messages-CZQUw3mp.js.map +1 -0
  59. package/dist/assets/app/messages-CZT41oVp.js +4 -0
  60. package/dist/assets/app/messages-CZT41oVp.js.map +1 -0
  61. package/dist/assets/app/messages-C_b-d3t8.js +4 -0
  62. package/dist/assets/app/messages-C_b-d3t8.js.map +1 -0
  63. package/dist/assets/app/messages-C_u3MTc2.js +4 -0
  64. package/dist/assets/app/messages-C_u3MTc2.js.map +1 -0
  65. package/dist/assets/app/messages-Cn8nHZic.js +4 -0
  66. package/dist/assets/app/messages-Cn8nHZic.js.map +1 -0
  67. package/dist/assets/app/messages-CtDywJUm.js +4 -0
  68. package/dist/assets/app/messages-CtDywJUm.js.map +1 -0
  69. package/dist/assets/app/messages-CurtIjBF.js +4 -0
  70. package/dist/assets/app/messages-CurtIjBF.js.map +1 -0
  71. package/dist/assets/app/messages-Cv6zIbaP.js +4 -0
  72. package/dist/assets/app/messages-Cv6zIbaP.js.map +1 -0
  73. package/dist/assets/app/messages-D1eLQuPE.js +4 -0
  74. package/dist/assets/app/messages-D1eLQuPE.js.map +1 -0
  75. package/dist/assets/app/messages-D8vHEaYW.js +4 -0
  76. package/dist/assets/app/messages-D8vHEaYW.js.map +1 -0
  77. package/dist/assets/app/messages-DJ1Q4GeC.js +4 -0
  78. package/dist/assets/app/messages-DJ1Q4GeC.js.map +1 -0
  79. package/dist/assets/app/messages-DRL3exqd.js +4 -0
  80. package/dist/assets/app/messages-DRL3exqd.js.map +1 -0
  81. package/dist/assets/app/messages-DWLPQRTp.js +4 -0
  82. package/dist/assets/app/messages-DWLPQRTp.js.map +1 -0
  83. package/dist/assets/app/messages-DjVaE9YE.js +4 -0
  84. package/dist/assets/app/messages-DjVaE9YE.js.map +1 -0
  85. package/dist/assets/app/messages-DqpMfFJR.js +4 -0
  86. package/dist/assets/app/messages-DqpMfFJR.js.map +1 -0
  87. package/dist/assets/app/messages-ETjhJBEN.js +4 -0
  88. package/dist/assets/app/messages-ETjhJBEN.js.map +1 -0
  89. package/dist/assets/app/messages-EUKrgrGn.js +4 -0
  90. package/dist/assets/app/messages-EUKrgrGn.js.map +1 -0
  91. package/dist/assets/app/messages-QQrOUcPW.js +4 -0
  92. package/dist/assets/app/messages-QQrOUcPW.js.map +1 -0
  93. package/dist/assets/app/messages-e2QGqFL6.js +4 -0
  94. package/dist/assets/app/messages-e2QGqFL6.js.map +1 -0
  95. package/dist/assets/app/messages-p61py7gD.js +4 -0
  96. package/dist/assets/app/messages-p61py7gD.js.map +1 -0
  97. package/dist/assets/asset.d.ts +1 -0
  98. package/dist/assets/asset.d.ts.map +1 -1
  99. package/dist/assets/assets-middleware.d.ts.map +1 -1
  100. package/dist/assets/assets-middleware.js +12 -7
  101. package/dist/assets/assets-middleware.js.map +1 -1
  102. package/dist/assets/index.d.ts +3 -2
  103. package/dist/assets/index.d.ts.map +1 -1
  104. package/dist/assets/index.js +13 -1
  105. package/dist/assets/index.js.map +1 -1
  106. package/dist/client/client-store.d.ts +3 -3
  107. package/dist/client/client-store.d.ts.map +1 -1
  108. package/dist/client/client-store.js +6 -5
  109. package/dist/client/client-store.js.map +1 -1
  110. package/dist/device/device-manager.d.ts +9 -8
  111. package/dist/device/device-manager.d.ts.map +1 -1
  112. package/dist/device/device-manager.js.map +1 -1
  113. package/dist/device/device-store.d.ts +3 -3
  114. package/dist/device/device-store.d.ts.map +1 -1
  115. package/dist/device/device-store.js +10 -9
  116. package/dist/device/device-store.js.map +1 -1
  117. package/dist/dpop/dpop-manager.d.ts +15 -7
  118. package/dist/dpop/dpop-manager.d.ts.map +1 -1
  119. package/dist/dpop/dpop-manager.js +17 -3
  120. package/dist/dpop/dpop-manager.js.map +1 -1
  121. package/dist/dpop/dpop-nonce.d.ts +11 -5
  122. package/dist/dpop/dpop-nonce.d.ts.map +1 -1
  123. package/dist/dpop/dpop-nonce.js +47 -38
  124. package/dist/dpop/dpop-nonce.js.map +1 -1
  125. package/dist/errors/handle-unavailable-error.d.ts +11 -0
  126. package/dist/errors/handle-unavailable-error.d.ts.map +1 -0
  127. package/dist/errors/handle-unavailable-error.js +19 -0
  128. package/dist/errors/handle-unavailable-error.js.map +1 -0
  129. package/dist/errors/invalid-request-error.d.ts +6 -8
  130. package/dist/errors/invalid-request-error.d.ts.map +1 -1
  131. package/dist/errors/invalid-request-error.js +10 -8
  132. package/dist/errors/invalid-request-error.js.map +1 -1
  133. package/dist/lib/csp/index.d.ts +18 -0
  134. package/dist/lib/csp/index.d.ts.map +1 -0
  135. package/dist/lib/csp/index.js +72 -0
  136. package/dist/lib/csp/index.js.map +1 -0
  137. package/dist/lib/hcaptcha.d.ts +177 -0
  138. package/dist/lib/hcaptcha.d.ts.map +1 -0
  139. package/dist/lib/hcaptcha.js +155 -0
  140. package/dist/lib/hcaptcha.js.map +1 -0
  141. package/dist/lib/html/build-document.d.ts +11 -3
  142. package/dist/lib/html/build-document.d.ts.map +1 -1
  143. package/dist/lib/html/build-document.js +51 -15
  144. package/dist/lib/html/build-document.js.map +1 -1
  145. package/dist/lib/http/middleware.d.ts.map +1 -1
  146. package/dist/lib/http/middleware.js +4 -1
  147. package/dist/lib/http/middleware.js.map +1 -1
  148. package/dist/lib/http/request.d.ts +5 -2
  149. package/dist/lib/http/request.d.ts.map +1 -1
  150. package/dist/lib/http/request.js +16 -1
  151. package/dist/lib/http/request.js.map +1 -1
  152. package/dist/lib/http/response.d.ts +4 -2
  153. package/dist/lib/http/response.d.ts.map +1 -1
  154. package/dist/lib/http/response.js +23 -5
  155. package/dist/lib/http/response.js.map +1 -1
  156. package/dist/lib/locale.d.ts +15 -0
  157. package/dist/lib/locale.d.ts.map +1 -0
  158. package/dist/lib/locale.js +17 -0
  159. package/dist/lib/locale.js.map +1 -0
  160. package/dist/lib/util/function.d.ts +2 -2
  161. package/dist/lib/util/function.d.ts.map +1 -1
  162. package/dist/lib/util/function.js.map +1 -1
  163. package/dist/lib/util/type.d.ts +88 -1
  164. package/dist/lib/util/type.d.ts.map +1 -1
  165. package/dist/lib/util/type.js +41 -0
  166. package/dist/lib/util/type.js.map +1 -1
  167. package/dist/metadata/build-metadata.d.ts +2 -2
  168. package/dist/metadata/build-metadata.d.ts.map +1 -1
  169. package/dist/metadata/build-metadata.js.map +1 -1
  170. package/dist/oauth-errors.d.ts +1 -0
  171. package/dist/oauth-errors.d.ts.map +1 -1
  172. package/dist/oauth-errors.js +3 -1
  173. package/dist/oauth-errors.js.map +1 -1
  174. package/dist/oauth-hooks.d.ts +60 -3
  175. package/dist/oauth-hooks.d.ts.map +1 -1
  176. package/dist/oauth-hooks.js +3 -3
  177. package/dist/oauth-hooks.js.map +1 -1
  178. package/dist/oauth-provider.d.ts +23 -18
  179. package/dist/oauth-provider.d.ts.map +1 -1
  180. package/dist/oauth-provider.js +207 -204
  181. package/dist/oauth-provider.js.map +1 -1
  182. package/dist/oauth-verifier.d.ts +1 -1
  183. package/dist/oauth-verifier.d.ts.map +1 -1
  184. package/dist/oauth-verifier.js +2 -1
  185. package/dist/oauth-verifier.js.map +1 -1
  186. package/dist/output/build-authorize-data.d.ts +0 -1
  187. package/dist/output/build-authorize-data.d.ts.map +1 -1
  188. package/dist/output/build-authorize-data.js +0 -1
  189. package/dist/output/build-authorize-data.js.map +1 -1
  190. package/dist/output/build-customization-data.d.ts +241 -0
  191. package/dist/output/build-customization-data.d.ts.map +1 -0
  192. package/dist/output/build-customization-data.js +174 -0
  193. package/dist/output/build-customization-data.js.map +1 -0
  194. package/dist/output/output-manager.d.ts +16 -9
  195. package/dist/output/output-manager.d.ts.map +1 -1
  196. package/dist/output/output-manager.js +78 -42
  197. package/dist/output/output-manager.js.map +1 -1
  198. package/dist/output/send-authorize-redirect.d.ts +9 -6
  199. package/dist/output/send-authorize-redirect.d.ts.map +1 -1
  200. package/dist/output/send-authorize-redirect.js +20 -14
  201. package/dist/output/send-authorize-redirect.js.map +1 -1
  202. package/dist/output/send-web-page.d.ts +7 -2
  203. package/dist/output/send-web-page.d.ts.map +1 -1
  204. package/dist/output/send-web-page.js +37 -21
  205. package/dist/output/send-web-page.js.map +1 -1
  206. package/dist/request/request-manager.d.ts +1 -1
  207. package/dist/request/request-manager.d.ts.map +1 -1
  208. package/dist/request/request-manager.js +4 -4
  209. package/dist/request/request-manager.js.map +1 -1
  210. package/dist/request/request-store.d.ts +3 -3
  211. package/dist/request/request-store.d.ts.map +1 -1
  212. package/dist/request/request-store.js +11 -10
  213. package/dist/request/request-store.js.map +1 -1
  214. package/dist/token/token-store.d.ts +4 -4
  215. package/dist/token/token-store.d.ts.map +1 -1
  216. package/dist/token/token-store.js +13 -12
  217. package/dist/token/token-store.js.map +1 -1
  218. package/package.json +43 -20
  219. package/rollup.config.js +61 -17
  220. package/src/account/account-manager.ts +159 -8
  221. package/src/account/account-store.ts +127 -32
  222. package/src/account/sign-in-data.ts +15 -0
  223. package/src/account/sign-up-data.ts +11 -0
  224. package/src/assets/app/app.tsx +31 -16
  225. package/src/assets/app/backend-data.ts +15 -60
  226. package/src/assets/app/backend-types.ts +66 -0
  227. package/src/assets/app/components/forms/button-toggle-visibility.tsx +43 -0
  228. package/src/assets/app/components/forms/button.tsx +60 -0
  229. package/src/assets/app/components/forms/fieldset.tsx +55 -0
  230. package/src/assets/app/components/forms/form-card-async.tsx +103 -0
  231. package/src/assets/app/components/forms/form-card.tsx +49 -0
  232. package/src/assets/app/components/forms/input-checkbox.tsx +73 -0
  233. package/src/assets/app/components/forms/input-container.tsx +107 -0
  234. package/src/assets/app/components/forms/input-email-address.tsx +66 -0
  235. package/src/assets/app/components/forms/input-new-password.tsx +62 -0
  236. package/src/assets/app/components/forms/input-password.tsx +88 -0
  237. package/src/assets/app/components/forms/input-text.tsx +76 -0
  238. package/src/assets/app/components/forms/input-token.tsx +94 -0
  239. package/src/assets/app/components/forms/wizard-card.tsx +116 -0
  240. package/src/assets/app/components/layouts/layout-title-page.tsx +77 -0
  241. package/src/assets/app/components/layouts/layout-welcome.tsx +73 -0
  242. package/src/assets/app/components/utils/account-identifier.tsx +23 -0
  243. package/src/assets/app/components/utils/account-image.tsx +33 -0
  244. package/src/assets/app/components/utils/admonition.tsx +52 -0
  245. package/src/assets/app/components/utils/client-name.tsx +45 -0
  246. package/src/assets/app/components/utils/error-card.tsx +93 -0
  247. package/src/assets/app/components/utils/error-message.tsx +62 -0
  248. package/src/assets/app/components/utils/help-card.tsx +46 -0
  249. package/src/assets/app/components/utils/icons.tsx +88 -0
  250. package/src/assets/app/components/utils/link-anchor.tsx +28 -0
  251. package/src/assets/app/components/utils/link-title.tsx +26 -0
  252. package/src/assets/app/components/utils/multi-lang-string.tsx +56 -0
  253. package/src/assets/app/components/utils/password-strength-label.tsx +37 -0
  254. package/src/assets/app/components/utils/password-strength-meter.tsx +58 -0
  255. package/src/assets/app/components/{url-viewer.tsx → utils/url-viewer.tsx} +9 -6
  256. package/src/assets/app/hooks/use-api.ts +128 -55
  257. package/src/assets/app/hooks/use-async-action.ts +120 -0
  258. package/src/assets/app/hooks/use-browser-color-scheme.ts +31 -0
  259. package/src/assets/app/hooks/use-csrf-token.ts +1 -1
  260. package/src/assets/app/hooks/use-random-string.ts +37 -0
  261. package/src/assets/app/hooks/use-stepper.ts +87 -0
  262. package/src/assets/app/index.html +182 -0
  263. package/src/assets/app/lib/api.ts +248 -79
  264. package/src/assets/app/lib/clsx.ts +5 -8
  265. package/src/assets/app/lib/json-client.ts +94 -0
  266. package/src/assets/app/lib/password.ts +98 -0
  267. package/src/assets/app/lib/ref.ts +17 -0
  268. package/src/assets/app/locales/an/messages.po +492 -0
  269. package/src/assets/app/locales/ast/messages.po +492 -0
  270. package/src/assets/app/locales/ca/messages.po +492 -0
  271. package/src/assets/app/locales/da/messages.po +492 -0
  272. package/src/assets/app/locales/de/messages.po +492 -0
  273. package/src/assets/app/locales/el/messages.po +492 -0
  274. package/src/assets/app/locales/en/messages.po +492 -0
  275. package/src/assets/app/locales/en-GB/messages.po +492 -0
  276. package/src/assets/app/locales/es/messages.po +492 -0
  277. package/src/assets/app/locales/eu/messages.po +492 -0
  278. package/src/assets/app/locales/fi/messages.po +492 -0
  279. package/src/assets/app/locales/fr/messages.po +492 -0
  280. package/src/assets/app/locales/ga/messages.po +492 -0
  281. package/src/assets/app/locales/gl/messages.po +492 -0
  282. package/src/assets/app/locales/hi/messages.po +492 -0
  283. package/src/assets/app/locales/hu/messages.po +492 -0
  284. package/src/assets/app/locales/ia/messages.po +492 -0
  285. package/src/assets/app/locales/id/messages.po +492 -0
  286. package/src/assets/app/locales/it/messages.po +492 -0
  287. package/src/assets/app/locales/ja/messages.po +492 -0
  288. package/src/assets/app/locales/km/messages.po +492 -0
  289. package/src/assets/app/locales/ko/messages.po +492 -0
  290. package/src/assets/app/locales/load.ts +8 -0
  291. package/src/assets/app/locales/locale-context.ts +19 -0
  292. package/src/assets/app/locales/locale-provider.tsx +112 -0
  293. package/src/assets/app/locales/locale-selector.tsx +58 -0
  294. package/src/assets/app/locales/locales.ts +168 -0
  295. package/src/assets/app/locales/ne/messages.po +492 -0
  296. package/src/assets/app/locales/nl/messages.po +492 -0
  297. package/src/assets/app/locales/pl/messages.po +492 -0
  298. package/src/assets/app/locales/pt-BR/messages.po +492 -0
  299. package/src/assets/app/locales/ro/messages.po +492 -0
  300. package/src/assets/app/locales/ru/messages.po +492 -0
  301. package/src/assets/app/locales/sv/messages.po +492 -0
  302. package/src/assets/app/locales/th/messages.po +492 -0
  303. package/src/assets/app/locales/tr/messages.po +492 -0
  304. package/src/assets/app/locales/uk/messages.po +492 -0
  305. package/src/assets/app/locales/vi/messages.po +492 -0
  306. package/src/assets/app/locales/zh-CN/messages.po +492 -0
  307. package/src/assets/app/locales/zh-HK/messages.po +492 -0
  308. package/src/assets/app/locales/zh-TW/messages.po +492 -0
  309. package/src/assets/app/main.css +23 -2
  310. package/src/assets/app/main.tsx +24 -8
  311. package/src/assets/app/views/authorize/accept/accept-form.tsx +150 -0
  312. package/src/assets/app/views/authorize/accept/accept-view.tsx +70 -0
  313. package/src/assets/app/views/authorize/authorize-view.tsx +180 -0
  314. package/src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx +88 -0
  315. package/src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx +80 -0
  316. package/src/assets/app/views/authorize/reset-password/reset-password-view.tsx +127 -0
  317. package/src/assets/app/views/authorize/sign-in/sign-in-form.tsx +244 -0
  318. package/src/assets/app/views/authorize/sign-in/sign-in-picker.tsx +116 -0
  319. package/src/assets/app/views/authorize/sign-in/sign-in-view.tsx +145 -0
  320. package/src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx +140 -0
  321. package/src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx +51 -0
  322. package/src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx +289 -0
  323. package/src/assets/app/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +108 -0
  324. package/src/assets/app/views/authorize/sign-up/sign-up-view.tsx +158 -0
  325. package/src/assets/app/views/authorize/welcome/welcome-view.tsx +56 -0
  326. package/src/assets/app/views/error/error-view.tsx +31 -0
  327. package/src/assets/asset.ts +1 -0
  328. package/src/assets/assets-middleware.ts +13 -8
  329. package/src/assets/index.ts +15 -2
  330. package/src/client/client-store.ts +10 -12
  331. package/src/device/device-manager.ts +8 -12
  332. package/src/device/device-store.ts +9 -15
  333. package/src/dpop/dpop-manager.ts +20 -8
  334. package/src/dpop/dpop-nonce.ts +58 -40
  335. package/src/errors/handle-unavailable-error.ts +18 -0
  336. package/src/errors/invalid-request-error.ts +10 -8
  337. package/src/lib/csp/index.ts +98 -0
  338. package/src/lib/hcaptcha.ts +182 -0
  339. package/src/lib/html/build-document.ts +60 -16
  340. package/src/lib/http/middleware.ts +4 -3
  341. package/src/lib/http/request.ts +31 -1
  342. package/src/lib/http/response.ts +22 -9
  343. package/src/lib/locale.ts +21 -0
  344. package/src/lib/util/function.ts +0 -3
  345. package/src/lib/util/type.ts +130 -1
  346. package/src/metadata/build-metadata.ts +2 -1
  347. package/src/oauth-errors.ts +1 -0
  348. package/src/oauth-hooks.ts +69 -3
  349. package/src/oauth-provider.ts +403 -307
  350. package/src/oauth-verifier.ts +3 -1
  351. package/src/output/build-authorize-data.ts +1 -3
  352. package/src/output/build-customization-data.ts +228 -0
  353. package/src/output/output-manager.ts +111 -48
  354. package/src/output/send-authorize-redirect.ts +43 -36
  355. package/src/output/send-web-page.ts +40 -26
  356. package/src/request/request-manager.ts +4 -4
  357. package/src/request/request-store.ts +12 -16
  358. package/src/token/token-store.ts +14 -18
  359. package/tailwind.config.js +5 -0
  360. package/tsconfig.backend.tsbuildinfo +1 -1
  361. package/tsconfig.frontend.tsbuildinfo +1 -1
  362. package/tsconfig.tools.tsbuildinfo +1 -1
  363. package/vite.config.mjs +16 -0
  364. package/.postcssrc.yml +0 -3
  365. package/dist/assets/app/main.css +0 -3
  366. package/dist/assets/app/main.js +0 -20
  367. package/dist/assets/app/main.js.map +0 -1
  368. package/dist/output/customization.d.ts +0 -27
  369. package/dist/output/customization.d.ts.map +0 -1
  370. package/dist/output/customization.js +0 -88
  371. package/dist/output/customization.js.map +0 -1
  372. package/src/assets/app/components/accept-form.tsx +0 -137
  373. package/src/assets/app/components/account-identifier.tsx +0 -18
  374. package/src/assets/app/components/account-picker.tsx +0 -127
  375. package/src/assets/app/components/button.tsx +0 -34
  376. package/src/assets/app/components/client-name.tsx +0 -37
  377. package/src/assets/app/components/fieldset.tsx +0 -26
  378. package/src/assets/app/components/form-card.tsx +0 -47
  379. package/src/assets/app/components/help-card.tsx +0 -42
  380. package/src/assets/app/components/icons/alert-icon.tsx +0 -5
  381. package/src/assets/app/components/icons/at-symbol-icon.tsx +0 -5
  382. package/src/assets/app/components/icons/caret-right-icon.tsx +0 -5
  383. package/src/assets/app/components/icons/lock-icon.tsx +0 -5
  384. package/src/assets/app/components/icons/token-icon.tsx +0 -5
  385. package/src/assets/app/components/icons/util.tsx +0 -17
  386. package/src/assets/app/components/info-card.tsx +0 -45
  387. package/src/assets/app/components/input-checkbox.tsx +0 -47
  388. package/src/assets/app/components/input-container.tsx +0 -37
  389. package/src/assets/app/components/input-layout.tsx +0 -47
  390. package/src/assets/app/components/input-text.tsx +0 -69
  391. package/src/assets/app/components/layout-title-page.tsx +0 -60
  392. package/src/assets/app/components/layout-welcome.tsx +0 -74
  393. package/src/assets/app/components/sign-in-form.tsx +0 -337
  394. package/src/assets/app/components/sign-up-account-form.tsx +0 -194
  395. package/src/assets/app/components/sign-up-disclaimer.tsx +0 -44
  396. package/src/assets/app/views/accept-view.tsx +0 -55
  397. package/src/assets/app/views/authorize-view.tsx +0 -106
  398. package/src/assets/app/views/error-view.tsx +0 -36
  399. package/src/assets/app/views/sign-in-view.tsx +0 -111
  400. package/src/assets/app/views/sign-up-view.tsx +0 -86
  401. package/src/assets/app/views/welcome-view.tsx +0 -54
  402. package/src/output/customization.ts +0 -118
@@ -1,31 +1,164 @@
1
- import { isOAuthClientIdLoopback } from '@atproto/oauth-types'
1
+ import {
2
+ OAuthIssuerIdentifier,
3
+ isOAuthClientIdLoopback,
4
+ } from '@atproto/oauth-types'
2
5
  import { Client } from '../client/client.js'
3
6
  import { DeviceId } from '../device/device-id.js'
7
+ import { InvalidRequestError } from '../errors/invalid-request-error.js'
8
+ import { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js'
9
+ import { callAsync } from '../lib/util/function.js'
4
10
  import { constantTime } from '../lib/util/time.js'
5
- import { InvalidRequestError } from '../oauth-errors.js'
11
+ import { OAuthHooks, RequestMetadata } from '../oauth-hooks.js'
12
+ import { Customization } from '../oauth-provider.js'
6
13
  import { Sub } from '../oidc/sub.js'
7
14
  import { ClientAuth } from '../token/token-store.js'
8
15
  import {
9
16
  Account,
10
17
  AccountInfo,
11
18
  AccountStore,
12
- SignInCredentials,
19
+ ResetPasswordConfirmData,
20
+ ResetPasswordRequestData,
13
21
  } from './account-store.js'
22
+ import { SignInData } from './sign-in-data.js'
23
+ import { SignUpData } from './sign-up-data.js'
14
24
 
15
25
  const TIMING_ATTACK_MITIGATION_DELAY = 400
26
+ const BRUTE_FORCE_MITIGATION_DELAY = 300
16
27
 
17
28
  export class AccountManager {
18
- constructor(protected readonly store: AccountStore) {}
29
+ protected readonly inviteCodeRequired: boolean
30
+ protected readonly hcaptchaClient?: HCaptchaClient
31
+
32
+ constructor(
33
+ issuer: OAuthIssuerIdentifier,
34
+ protected readonly store: AccountStore,
35
+ protected readonly hooks: OAuthHooks,
36
+ customization: Customization,
37
+ ) {
38
+ this.inviteCodeRequired = customization.inviteCodeRequired !== false
39
+ this.hcaptchaClient = customization.hcaptcha
40
+ ? new HCaptchaClient(new URL(issuer).hostname, customization.hcaptcha)
41
+ : undefined
42
+ }
43
+
44
+ protected async verifySignupData(
45
+ data: SignUpData,
46
+ deviceId: DeviceId,
47
+ deviceMetadata: RequestMetadata,
48
+ ): Promise<void> {
49
+ let hcaptchaResult: undefined | HcaptchaVerifyResult
50
+
51
+ if (this.inviteCodeRequired && !data.inviteCode) {
52
+ throw new InvalidRequestError('Invite code is required')
53
+ }
54
+
55
+ if (this.hcaptchaClient) {
56
+ if (!data.hcaptchaToken) {
57
+ throw new InvalidRequestError('hCaptcha token is required')
58
+ }
59
+
60
+ const { allowed, result } = await this.hcaptchaClient.verify(
61
+ 'signup',
62
+ data.hcaptchaToken,
63
+ deviceMetadata.ipAddress,
64
+ data.handle,
65
+ deviceMetadata.userAgent,
66
+ )
67
+
68
+ await callAsync(this.hooks.onSignupHcaptchaResult, {
69
+ data,
70
+ allowed,
71
+ result,
72
+ deviceId,
73
+ deviceMetadata,
74
+ })
75
+
76
+ if (!allowed) {
77
+ throw new InvalidRequestError('hCaptcha verification failed')
78
+ }
79
+
80
+ hcaptchaResult = result
81
+ }
82
+
83
+ await callAsync(this.hooks.onSignupAttempt, {
84
+ data,
85
+ deviceId,
86
+ deviceMetadata,
87
+ hcaptchaResult,
88
+ })
89
+ }
90
+
91
+ public async signUp(
92
+ data: SignUpData,
93
+ deviceId: DeviceId,
94
+ deviceMetadata: RequestMetadata,
95
+ ): Promise<AccountInfo> {
96
+ await this.verifySignupData(data, deviceId, deviceMetadata)
97
+
98
+ // Mitigation against brute forcing email of users.
99
+ // @TODO Add rate limit to all the OAuth routes.
100
+ return constantTime(BRUTE_FORCE_MITIGATION_DELAY, async () => {
101
+ let account: Account
102
+ try {
103
+ account = await this.store.createAccount(data)
104
+ } catch (err) {
105
+ throw InvalidRequestError.from(err, 'Account creation failed')
106
+ }
107
+
108
+ try {
109
+ const info = await this.store.addDeviceAccount(
110
+ deviceId,
111
+ account.sub,
112
+ false,
113
+ )
114
+
115
+ await callAsync(this.hooks.onSignedUp, {
116
+ data,
117
+ info,
118
+ account,
119
+ deviceId,
120
+ deviceMetadata,
121
+ })
122
+
123
+ return { account, info }
124
+ } catch (err) {
125
+ throw InvalidRequestError.from(
126
+ err,
127
+ 'Something went wrong, try singing-in',
128
+ )
129
+ }
130
+ })
131
+ }
19
132
 
20
133
  public async signIn(
21
- credentials: SignInCredentials,
134
+ data: SignInData,
22
135
  deviceId: DeviceId,
136
+ deviceMetadata: RequestMetadata,
23
137
  ): Promise<AccountInfo> {
24
138
  return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
25
- const result = await this.store.authenticateAccount(credentials, deviceId)
26
- if (result) return result
139
+ try {
140
+ const account = await this.store.authenticateAccount(data)
141
+ const info = await this.store.addDeviceAccount(
142
+ deviceId,
143
+ account.sub,
144
+ data.remember,
145
+ )
27
146
 
28
- throw new InvalidRequestError('Invalid credentials')
147
+ await callAsync(this.hooks.onSignedIn, {
148
+ data,
149
+ info,
150
+ account,
151
+ deviceId,
152
+ deviceMetadata,
153
+ })
154
+
155
+ return { account, info }
156
+ } catch (err) {
157
+ throw InvalidRequestError.from(
158
+ err,
159
+ 'Unable to sign-in due to an unexpected server error',
160
+ )
161
+ }
29
162
  })
30
163
  }
31
164
 
@@ -52,4 +185,22 @@ export class AccountManager {
52
185
  const results = await this.store.listDeviceAccounts(deviceId)
53
186
  return results.filter((result) => result.info.remembered)
54
187
  }
188
+
189
+ public async resetPasswordRequest(data: ResetPasswordRequestData) {
190
+ return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
191
+ await this.store.resetPasswordRequest(data)
192
+ })
193
+ }
194
+
195
+ public async resetPasswordConfirm(data: ResetPasswordConfirmData) {
196
+ return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
197
+ await this.store.resetPasswordConfirm(data)
198
+ })
199
+ }
200
+
201
+ public async verifyHandleAvailability(handle: string): Promise<void> {
202
+ return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
203
+ return this.store.verifyHandleAvailability(handle)
204
+ })
205
+ }
55
206
  }
@@ -1,25 +1,86 @@
1
+ import { isEmailValid } from '@hapi/address'
2
+ import { isDisposableEmail } from 'disposable-email-domains-js'
1
3
  import { z } from 'zod'
2
4
  import { ClientId } from '../client/client-id.js'
3
5
  import { DeviceId } from '../device/device-id.js'
4
- import { Awaitable } from '../lib/util/type.js'
6
+ import { localeSchema } from '../lib/locale.js'
7
+ import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'
8
+ import {
9
+ HandleUnavailableError,
10
+ InvalidRequestError,
11
+ SecondAuthenticationFactorRequiredError,
12
+ } from '../oauth-errors.js'
5
13
  import { Sub } from '../oidc/sub.js'
6
14
  import { Account } from './account.js'
7
15
 
8
- export const signInCredentialsSchema = z.object({
9
- username: z.string(),
10
- password: z.string(),
16
+ // @NOTE Change the length here to force stronger passwords (through a reset)
17
+ export const oldPasswordSchema = z.string().min(1)
18
+ export const newPasswordSchema = z.string().min(8)
19
+ export const tokenSchema = z.string().regex(/^[A-Z2-7]{5}-[A-Z2-7]{5}$/)
20
+ export const handleSchema = z
21
+ .string()
22
+ .min(3)
23
+ .max(30)
24
+ .regex(/^[a-z0-9][a-z0-9-]+[a-z0-9](?:\.[a-z0-9][a-z0-9-]+[a-z0-9])+$/)
25
+ export const emailSchema = z
26
+ .string()
27
+ .email()
28
+ // @NOTE using @hapi/address here, in addition to the email() check to ensure
29
+ // compatibility with the current email validation in the PDS's account
30
+ // manager
31
+ .refine(isEmailValid, {
32
+ message: 'Invalid email address',
33
+ })
34
+ .refine((email) => !isDisposableEmail(email), {
35
+ message: 'Disposable email addresses are not allowed',
36
+ })
11
37
 
12
- /**
13
- * If false, the account must not be returned from
14
- * {@link AccountStore.listDeviceAccounts}. Note that this only makes sense when
15
- * used with a device ID.
16
- */
17
- remember: z.boolean().optional().default(false),
38
+ export const authenticateAccountDataSchema = z
39
+ .object({
40
+ locale: localeSchema,
41
+ username: z.string(),
42
+ password: oldPasswordSchema,
43
+ emailOtp: z.string().optional(),
44
+ })
45
+ .strict()
46
+
47
+ export type AuthenticateAccountData = z.TypeOf<
48
+ typeof authenticateAccountDataSchema
49
+ >
50
+
51
+ export const createAccountDataSchema = z
52
+ .object({
53
+ locale: localeSchema,
54
+ handle: handleSchema,
55
+ email: emailSchema,
56
+ password: z.intersection(oldPasswordSchema, newPasswordSchema),
57
+ inviteCode: tokenSchema.optional(),
58
+ })
59
+ .strict()
60
+
61
+ export type CreateAccountData = z.TypeOf<typeof createAccountDataSchema>
62
+
63
+ export const resetPasswordRequestDataSchema = z
64
+ .object({
65
+ locale: localeSchema,
66
+ email: emailSchema,
67
+ })
68
+ .strict()
69
+
70
+ export type ResetPasswordRequestData = z.TypeOf<
71
+ typeof resetPasswordRequestDataSchema
72
+ >
18
73
 
19
- emailOtp: z.string().optional(),
20
- })
74
+ export const resetPasswordConfirmDataSchema = z
75
+ .object({
76
+ token: tokenSchema,
77
+ password: z.intersection(oldPasswordSchema, newPasswordSchema),
78
+ })
79
+ .strict()
21
80
 
22
- export type SignInCredentials = z.TypeOf<typeof signInCredentialsSchema>
81
+ export type ResetPasswordConfirmData = z.TypeOf<
82
+ typeof resetPasswordConfirmDataSchema
83
+ >
23
84
 
24
85
  export type DeviceAccountInfo = {
25
86
  remembered: boolean
@@ -28,7 +89,14 @@ export type DeviceAccountInfo = {
28
89
  }
29
90
 
30
91
  // Export all types needed to implement the AccountStore interface
31
- export type { Account, DeviceId, Sub }
92
+ export {
93
+ type Account,
94
+ type DeviceId,
95
+ HandleUnavailableError,
96
+ InvalidRequestError,
97
+ SecondAuthenticationFactorRequiredError,
98
+ type Sub,
99
+ }
32
100
 
33
101
  export type AccountInfo = {
34
102
  account: Account
@@ -36,10 +104,17 @@ export type AccountInfo = {
36
104
  }
37
105
 
38
106
  export interface AccountStore {
39
- authenticateAccount(
40
- credentials: SignInCredentials,
41
- deviceId: DeviceId,
42
- ): Awaitable<AccountInfo | null>
107
+ /**
108
+ * @throws {HandleUnavailableError} - To indicate that the handle is already taken
109
+ * @throws {InvalidRequestError} - To indicate that some data is invalid
110
+ */
111
+ createAccount(data: CreateAccountData): Awaitable<Account>
112
+
113
+ /**
114
+ * @throws {InvalidRequestError} - When the credentials are not valid
115
+ * @throws {SecondAuthenticationFactorRequiredError} - To indicate that an {@link SecondAuthenticationFactorRequiredError.type} is required in the credentials
116
+ */
117
+ authenticateAccount(data: AuthenticateAccountData): Awaitable<Account>
43
118
 
44
119
  addAuthorizedClient(
45
120
  deviceId: DeviceId,
@@ -47,6 +122,19 @@ export interface AccountStore {
47
122
  clientId: ClientId,
48
123
  ): Awaitable<void>
49
124
 
125
+ /**
126
+ * @param remember If false, the account must not be returned from
127
+ * {@link AccountStore.listDeviceAccounts}.
128
+ */
129
+ addDeviceAccount(
130
+ deviceId: DeviceId,
131
+ sub: Sub,
132
+ remember: boolean,
133
+ ): Awaitable<DeviceAccountInfo>
134
+
135
+ /**
136
+ * @returns The account info, whether the account, even if remember was false.
137
+ */
50
138
  getDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<AccountInfo | null>
51
139
  removeDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<void>
52
140
 
@@ -55,23 +143,30 @@ export interface AccountStore {
55
143
  * be returned. The others will be ignored.
56
144
  */
57
145
  listDeviceAccounts(deviceId: DeviceId): Awaitable<AccountInfo[]>
58
- }
59
146
 
60
- export function isAccountStore(
61
- implementation: Record<string, unknown> & Partial<AccountStore>,
62
- ): implementation is Record<string, unknown> & AccountStore {
63
- return (
64
- typeof implementation.authenticateAccount === 'function' &&
65
- typeof implementation.getDeviceAccount === 'function' &&
66
- typeof implementation.addAuthorizedClient === 'function' &&
67
- typeof implementation.listDeviceAccounts === 'function' &&
68
- typeof implementation.removeDeviceAccount === 'function'
69
- )
147
+ resetPasswordRequest(data: ResetPasswordRequestData): Awaitable<void>
148
+ resetPasswordConfirm(data: ResetPasswordConfirmData): Awaitable<void>
149
+
150
+ /**
151
+ * @throws {HandleUnavailableError} - To indicate that the handle is already taken
152
+ */
153
+ verifyHandleAvailability(handle: string): Awaitable<void>
70
154
  }
71
155
 
72
- export function asAccountStore(
73
- implementation?: Record<string, unknown> & Partial<AccountStore>,
74
- ): AccountStore {
156
+ export const isAccountStore = buildInterfaceChecker<AccountStore>([
157
+ 'createAccount',
158
+ 'authenticateAccount',
159
+ 'addAuthorizedClient',
160
+ 'addDeviceAccount',
161
+ 'getDeviceAccount',
162
+ 'removeDeviceAccount',
163
+ 'listDeviceAccounts',
164
+ 'resetPasswordRequest',
165
+ 'resetPasswordConfirm',
166
+ 'verifyHandleAvailability',
167
+ ])
168
+
169
+ export function asAccountStore<V>(implementation: V): V & AccountStore {
75
170
  if (!implementation || !isAccountStore(implementation)) {
76
171
  throw new Error('Invalid AccountStore implementation')
77
172
  }
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod'
2
+ import { authenticateAccountDataSchema } from './account-store.js'
3
+
4
+ export const signInDataSchema = authenticateAccountDataSchema
5
+ .extend({
6
+ /**
7
+ * If false, the account must not be returned from
8
+ * {@link AccountStore.listDeviceAccounts}. Note that this only makes sense when
9
+ * used with a device ID.
10
+ */
11
+ remember: z.boolean().optional().default(false),
12
+ })
13
+ .strict()
14
+
15
+ export type SignInData = z.TypeOf<typeof signInDataSchema>
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod'
2
+ import { hcaptchaTokenSchema } from '../lib/hcaptcha.js'
3
+ import { createAccountDataSchema } from './account-store.js'
4
+
5
+ export const signUpDataSchema = createAccountDataSchema
6
+ .extend({
7
+ hcaptchaToken: hcaptchaTokenSchema.optional(),
8
+ })
9
+ .strict()
10
+
11
+ export type SignUpData = z.TypeOf<typeof signUpDataSchema>
@@ -1,28 +1,43 @@
1
+ import { ErrorBoundary } from 'react-error-boundary'
1
2
  import type {
2
3
  AuthorizeData,
4
+ AvailableLocales,
3
5
  CustomizationData,
4
6
  ErrorData,
5
- } from './backend-data'
6
- import { AuthorizeView } from './views/authorize-view'
7
- import { ErrorView } from './views/error-view'
7
+ } from './backend-types.ts'
8
+ import { LocaleProvider } from './locales/locale-provider.tsx'
9
+ import { AuthorizeView } from './views/authorize/authorize-view.tsx'
10
+ import { ErrorView } from './views/error/error-view.tsx'
8
11
 
9
12
  export type AppProps = {
13
+ availableLocales?: AvailableLocales
10
14
  authorizeData?: AuthorizeData
11
15
  customizationData?: CustomizationData
12
16
  errorData?: ErrorData
13
17
  }
14
18
 
15
- export function App({ authorizeData, customizationData, errorData }: AppProps) {
16
- if (authorizeData && !errorData) {
17
- return (
18
- <AuthorizeView
19
- customizationData={customizationData}
20
- authorizeData={authorizeData}
21
- />
22
- )
23
- } else {
24
- return (
25
- <ErrorView customizationData={customizationData} errorData={errorData} />
26
- )
27
- }
19
+ export function App({
20
+ availableLocales,
21
+ authorizeData,
22
+ customizationData,
23
+ errorData,
24
+ }: AppProps) {
25
+ return (
26
+ <LocaleProvider availableLocales={availableLocales}>
27
+ <ErrorBoundary
28
+ fallbackRender={({ error }) => (
29
+ <ErrorView error={error} customizationData={customizationData} />
30
+ )}
31
+ >
32
+ {errorData || !authorizeData ? (
33
+ <ErrorView error={errorData} customizationData={customizationData} />
34
+ ) : (
35
+ <AuthorizeView
36
+ customizationData={customizationData}
37
+ authorizeData={authorizeData}
38
+ />
39
+ )}
40
+ </ErrorBoundary>
41
+ </LocaleProvider>
42
+ )
28
43
  }
@@ -1,72 +1,27 @@
1
- import { OAuthClientMetadata } from '@atproto/oauth-types'
2
-
3
- // TODO: Find a way to share these types with the backend code
4
-
5
- export type Account = {
6
- sub: string
7
- aud: string
8
-
9
- email?: string
10
- name?: string
11
- preferred_username?: string
12
- picture?: string
13
- }
14
-
15
- export type Session = {
16
- account: Account
17
- info?: never // Prevent relying on this in the frontend
18
-
19
- selected: boolean
20
- loginRequired: boolean
21
- consentRequired: boolean
22
- }
23
-
24
- export type LinkDefinition = {
25
- title: string
26
- href: string
27
- rel?: string
28
- }
29
-
30
- export type CustomizationData = {
31
- name?: string
32
- logo?: string
33
- links?: LinkDefinition[]
34
- }
35
-
36
- export type ErrorData = {
37
- error: string
38
- error_description: string
39
- }
40
-
41
- export type ScopeDetail = {
42
- scope: string
43
- description?: string
44
- }
45
-
46
- export type AuthorizeData = {
47
- clientId: string
48
- clientMetadata: OAuthClientMetadata
49
- clientTrusted: boolean
50
- requestUri: string
51
- csrfCookie: string
52
- loginHint?: string
53
- scopeDetails?: ScopeDetail[]
54
- newSessionsRequireConsent: boolean
55
- sessions: Session[]
56
- }
57
-
58
- // see "declareBackendData()" in the backend
59
- const readBackendData = <T>(key: string): T | undefined => {
1
+ import {
2
+ AuthorizeData,
3
+ AvailableLocales,
4
+ CustomizationData,
5
+ ErrorData,
6
+ } from './backend-types.ts'
7
+
8
+ function readBackendData<T>(key: string): T | undefined {
60
9
  const value = window[key] as T | undefined
61
10
  delete window[key] // Prevent accidental usage / potential leaks to dependencies
62
11
  return value
63
12
  }
64
13
 
65
14
  // These values are injected by the backend when it builds the
66
- // page HTML.
15
+ // page HTML. See "declareBackendData()" in the backend.
67
16
 
17
+ /** @deprecated Do not import directly. Only import this from main.tsx */
18
+ export const availableLocales =
19
+ readBackendData<AvailableLocales>('__availableLocales')
20
+ /** @deprecated Do not import directly. Only import this from main.tsx */
68
21
  export const customizationData = readBackendData<CustomizationData>(
69
22
  '__customizationData',
70
23
  )
24
+ /** @deprecated Do not import directly. Only import this from main.tsx */
71
25
  export const errorData = readBackendData<ErrorData>('__errorData')
26
+ /** @deprecated Do not import directly. Only import this from main.tsx */
72
27
  export const authorizeData = readBackendData<AuthorizeData>('__authorizeData')
@@ -0,0 +1,66 @@
1
+ import type { OAuthClientMetadata } from '@atproto/oauth-types'
2
+
3
+ // @TODO: Find a way to share these types with the backend code
4
+
5
+ export type Account = {
6
+ sub: string
7
+ aud: string | [string, ...string[]]
8
+
9
+ email?: string
10
+ email_verified?: boolean
11
+ name?: string
12
+ preferred_username?: string
13
+ picture?: string
14
+ }
15
+
16
+ export type Session = {
17
+ account: Account
18
+ info?: never // Prevent relying on this in the frontend
19
+
20
+ selected: boolean
21
+ loginRequired: boolean
22
+ consentRequired: boolean
23
+ }
24
+
25
+ export type LocalizedString = string | ({ en: string } & Record<string, string>)
26
+
27
+ export type AvailableLocales = readonly string[]
28
+
29
+ export type LinkDefinition = {
30
+ title: LocalizedString
31
+ href: string
32
+ rel?: string
33
+ }
34
+
35
+ export type CustomizationData = {
36
+ // Functional customization
37
+ hcaptchaSiteKey?: string
38
+ inviteCodeRequired?: boolean
39
+ availableUserDomains?: string[]
40
+
41
+ // Aesthetic customization
42
+ name?: string
43
+ logo?: string
44
+ links?: LinkDefinition[]
45
+ }
46
+
47
+ export type ErrorData = {
48
+ error: string
49
+ error_description: string
50
+ }
51
+
52
+ export type ScopeDetail = {
53
+ scope: string
54
+ description?: string
55
+ }
56
+
57
+ export type AuthorizeData = {
58
+ clientId: string
59
+ clientMetadata: OAuthClientMetadata
60
+ clientTrusted: boolean
61
+ requestUri: string
62
+ loginHint?: string
63
+ scopeDetails?: ScopeDetail[]
64
+ newSessionsRequireConsent: boolean
65
+ sessions: Session[]
66
+ }
@@ -0,0 +1,43 @@
1
+ import { useLingui } from '@lingui/react/macro'
2
+ import { Override } from '../../lib/util.ts'
3
+ import { EyeIcon, EyeSlashIcon } from '../utils/icons.tsx'
4
+ import { Button, ButtonProps } from './button.tsx'
5
+
6
+ export type ButtonToggleVisibilityProps = Override<
7
+ Omit<ButtonProps, 'aria-label' | 'square'>,
8
+ {
9
+ visible: boolean
10
+ toggleVisible: () => void
11
+ }
12
+ >
13
+
14
+ /**
15
+ * Generic button to toggle visibility of an item (e.g. password).
16
+ */
17
+ export function ButtonToggleVisibility({
18
+ visible,
19
+ toggleVisible,
20
+
21
+ // button
22
+ onClick,
23
+ ...props
24
+ }: ButtonToggleVisibilityProps) {
25
+ const { t } = useLingui()
26
+ return (
27
+ <Button
28
+ {...props}
29
+ square
30
+ onClick={(event) => {
31
+ onClick?.(event)
32
+ if (!event.defaultPrevented) toggleVisible()
33
+ }}
34
+ aria-label={visible ? t`Hide` : t`Make visible`}
35
+ >
36
+ {visible ? (
37
+ <EyeIcon className="w-5" aria-hidden />
38
+ ) : (
39
+ <EyeSlashIcon className="w-5" aria-hidden />
40
+ )}
41
+ </Button>
42
+ )
43
+ }