@atproto/oauth-provider 0.3.1 → 0.5.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 (404) hide show
  1. package/.linguirc +57 -0
  2. package/CHANGELOG.md +29 -0
  3. package/LICENSE.txt +1 -1
  4. package/dist/account/account-manager.d.ts +17 -3
  5. package/dist/account/account-manager.d.ts.map +1 -1
  6. package/dist/account/account-manager.js +102 -8
  7. package/dist/account/account-manager.js.map +1 -1
  8. package/dist/account/account-store.d.ts +81 -15
  9. package/dist/account/account-store.d.ts.map +1 -1
  10. package/dist/account/account-store.js +70 -19
  11. package/dist/account/account-store.js.map +1 -1
  12. package/dist/account/sign-in-data.d.ts +28 -0
  13. package/dist/account/sign-in-data.d.ts.map +1 -0
  14. package/dist/account/sign-in-data.js +16 -0
  15. package/dist/account/sign-in-data.js.map +1 -0
  16. package/dist/account/sign-up-data.d.ts +26 -0
  17. package/dist/account/sign-up-data.d.ts.map +1 -0
  18. package/dist/account/sign-up-data.js +11 -0
  19. package/dist/account/sign-up-data.js.map +1 -0
  20. package/dist/assets/app/bundle-manifest.json +598 -6
  21. package/dist/assets/app/index-ItwwtJ8r.js +36 -0
  22. package/dist/assets/app/index-ItwwtJ8r.js.map +1 -0
  23. package/dist/assets/app/main-B_dNxQo_.js +4 -0
  24. package/dist/assets/app/main-B_dNxQo_.js.map +1 -0
  25. package/dist/assets/app/main-CSatvmRR.css +3 -0
  26. package/dist/assets/app/main-CSatvmRR.js +306 -0
  27. package/dist/assets/app/main-CSatvmRR.js.map +1 -0
  28. package/dist/assets/app/messages-BQeltXSF.js +4 -0
  29. package/dist/assets/app/messages-BQeltXSF.js.map +1 -0
  30. package/dist/assets/app/messages-BQkEhfjg.js +4 -0
  31. package/dist/assets/app/messages-BQkEhfjg.js.map +1 -0
  32. package/dist/assets/app/messages-BUjKj_UJ.js +4 -0
  33. package/dist/assets/app/messages-BUjKj_UJ.js.map +1 -0
  34. package/dist/assets/app/messages-BWIQa8fO.js +4 -0
  35. package/dist/assets/app/messages-BWIQa8fO.js.map +1 -0
  36. package/dist/assets/app/messages-BaNVb0bp.js +4 -0
  37. package/dist/assets/app/messages-BaNVb0bp.js.map +1 -0
  38. package/dist/assets/app/messages-BaizVXcF.js +4 -0
  39. package/dist/assets/app/messages-BaizVXcF.js.map +1 -0
  40. package/dist/assets/app/messages-BfoClA1Y.js +4 -0
  41. package/dist/assets/app/messages-BfoClA1Y.js.map +1 -0
  42. package/dist/assets/app/messages-BsKGDZnC.js +4 -0
  43. package/dist/assets/app/messages-BsKGDZnC.js.map +1 -0
  44. package/dist/assets/app/messages-Bu-TJhml.js +4 -0
  45. package/dist/assets/app/messages-Bu-TJhml.js.map +1 -0
  46. package/dist/assets/app/messages-BvOKnBQk.js +4 -0
  47. package/dist/assets/app/messages-BvOKnBQk.js.map +1 -0
  48. package/dist/assets/app/messages-BxDzCiWz.js +4 -0
  49. package/dist/assets/app/messages-BxDzCiWz.js.map +1 -0
  50. package/dist/assets/app/messages-CDgFOy4S.js +4 -0
  51. package/dist/assets/app/messages-CDgFOy4S.js.map +1 -0
  52. package/dist/assets/app/messages-CLbTz0o9.js +4 -0
  53. package/dist/assets/app/messages-CLbTz0o9.js.map +1 -0
  54. package/dist/assets/app/messages-CNwSh0t7.js +4 -0
  55. package/dist/assets/app/messages-CNwSh0t7.js.map +1 -0
  56. package/dist/assets/app/messages-CSMNJ6P8.js +4 -0
  57. package/dist/assets/app/messages-CSMNJ6P8.js.map +1 -0
  58. package/dist/assets/app/messages-CZQUw3mp.js +4 -0
  59. package/dist/assets/app/messages-CZQUw3mp.js.map +1 -0
  60. package/dist/assets/app/messages-CZT41oVp.js +4 -0
  61. package/dist/assets/app/messages-CZT41oVp.js.map +1 -0
  62. package/dist/assets/app/messages-C_b-d3t8.js +4 -0
  63. package/dist/assets/app/messages-C_b-d3t8.js.map +1 -0
  64. package/dist/assets/app/messages-C_u3MTc2.js +4 -0
  65. package/dist/assets/app/messages-C_u3MTc2.js.map +1 -0
  66. package/dist/assets/app/messages-Cn8nHZic.js +4 -0
  67. package/dist/assets/app/messages-Cn8nHZic.js.map +1 -0
  68. package/dist/assets/app/messages-CtDywJUm.js +4 -0
  69. package/dist/assets/app/messages-CtDywJUm.js.map +1 -0
  70. package/dist/assets/app/messages-CurtIjBF.js +4 -0
  71. package/dist/assets/app/messages-CurtIjBF.js.map +1 -0
  72. package/dist/assets/app/messages-Cv6zIbaP.js +4 -0
  73. package/dist/assets/app/messages-Cv6zIbaP.js.map +1 -0
  74. package/dist/assets/app/messages-D1eLQuPE.js +4 -0
  75. package/dist/assets/app/messages-D1eLQuPE.js.map +1 -0
  76. package/dist/assets/app/messages-D8vHEaYW.js +4 -0
  77. package/dist/assets/app/messages-D8vHEaYW.js.map +1 -0
  78. package/dist/assets/app/messages-DJ1Q4GeC.js +4 -0
  79. package/dist/assets/app/messages-DJ1Q4GeC.js.map +1 -0
  80. package/dist/assets/app/messages-DRL3exqd.js +4 -0
  81. package/dist/assets/app/messages-DRL3exqd.js.map +1 -0
  82. package/dist/assets/app/messages-DWLPQRTp.js +4 -0
  83. package/dist/assets/app/messages-DWLPQRTp.js.map +1 -0
  84. package/dist/assets/app/messages-DjVaE9YE.js +4 -0
  85. package/dist/assets/app/messages-DjVaE9YE.js.map +1 -0
  86. package/dist/assets/app/messages-DqpMfFJR.js +4 -0
  87. package/dist/assets/app/messages-DqpMfFJR.js.map +1 -0
  88. package/dist/assets/app/messages-ETjhJBEN.js +4 -0
  89. package/dist/assets/app/messages-ETjhJBEN.js.map +1 -0
  90. package/dist/assets/app/messages-EUKrgrGn.js +4 -0
  91. package/dist/assets/app/messages-EUKrgrGn.js.map +1 -0
  92. package/dist/assets/app/messages-QQrOUcPW.js +4 -0
  93. package/dist/assets/app/messages-QQrOUcPW.js.map +1 -0
  94. package/dist/assets/app/messages-e2QGqFL6.js +4 -0
  95. package/dist/assets/app/messages-e2QGqFL6.js.map +1 -0
  96. package/dist/assets/app/messages-p61py7gD.js +4 -0
  97. package/dist/assets/app/messages-p61py7gD.js.map +1 -0
  98. package/dist/assets/asset.d.ts +1 -0
  99. package/dist/assets/asset.d.ts.map +1 -1
  100. package/dist/assets/assets-middleware.d.ts.map +1 -1
  101. package/dist/assets/assets-middleware.js +12 -7
  102. package/dist/assets/assets-middleware.js.map +1 -1
  103. package/dist/assets/index.d.ts +3 -2
  104. package/dist/assets/index.d.ts.map +1 -1
  105. package/dist/assets/index.js +13 -1
  106. package/dist/assets/index.js.map +1 -1
  107. package/dist/client/client-store.d.ts +3 -3
  108. package/dist/client/client-store.d.ts.map +1 -1
  109. package/dist/client/client-store.js +6 -5
  110. package/dist/client/client-store.js.map +1 -1
  111. package/dist/device/device-manager.d.ts +12 -13
  112. package/dist/device/device-manager.d.ts.map +1 -1
  113. package/dist/device/device-manager.js +5 -3
  114. package/dist/device/device-manager.js.map +1 -1
  115. package/dist/device/device-store.d.ts +3 -3
  116. package/dist/device/device-store.d.ts.map +1 -1
  117. package/dist/device/device-store.js +10 -9
  118. package/dist/device/device-store.js.map +1 -1
  119. package/dist/dpop/dpop-manager.d.ts +15 -7
  120. package/dist/dpop/dpop-manager.d.ts.map +1 -1
  121. package/dist/dpop/dpop-manager.js +17 -3
  122. package/dist/dpop/dpop-manager.js.map +1 -1
  123. package/dist/dpop/dpop-nonce.d.ts +11 -5
  124. package/dist/dpop/dpop-nonce.d.ts.map +1 -1
  125. package/dist/dpop/dpop-nonce.js +47 -38
  126. package/dist/dpop/dpop-nonce.js.map +1 -1
  127. package/dist/errors/handle-unavailable-error.d.ts +11 -0
  128. package/dist/errors/handle-unavailable-error.d.ts.map +1 -0
  129. package/dist/errors/handle-unavailable-error.js +19 -0
  130. package/dist/errors/handle-unavailable-error.js.map +1 -0
  131. package/dist/errors/invalid-request-error.d.ts +6 -8
  132. package/dist/errors/invalid-request-error.d.ts.map +1 -1
  133. package/dist/errors/invalid-request-error.js +10 -8
  134. package/dist/errors/invalid-request-error.js.map +1 -1
  135. package/dist/lib/csp/index.d.ts +18 -0
  136. package/dist/lib/csp/index.d.ts.map +1 -0
  137. package/dist/lib/csp/index.js +72 -0
  138. package/dist/lib/csp/index.js.map +1 -0
  139. package/dist/lib/hcaptcha.d.ts +177 -0
  140. package/dist/lib/hcaptcha.d.ts.map +1 -0
  141. package/dist/lib/hcaptcha.js +155 -0
  142. package/dist/lib/hcaptcha.js.map +1 -0
  143. package/dist/lib/html/build-document.d.ts +11 -3
  144. package/dist/lib/html/build-document.d.ts.map +1 -1
  145. package/dist/lib/html/build-document.js +51 -15
  146. package/dist/lib/html/build-document.js.map +1 -1
  147. package/dist/lib/http/middleware.d.ts.map +1 -1
  148. package/dist/lib/http/middleware.js +4 -1
  149. package/dist/lib/http/middleware.js.map +1 -1
  150. package/dist/lib/http/request.d.ts +18 -3
  151. package/dist/lib/http/request.d.ts.map +1 -1
  152. package/dist/lib/http/request.js +56 -23
  153. package/dist/lib/http/request.js.map +1 -1
  154. package/dist/lib/http/response.d.ts +4 -2
  155. package/dist/lib/http/response.d.ts.map +1 -1
  156. package/dist/lib/http/response.js +23 -5
  157. package/dist/lib/http/response.js.map +1 -1
  158. package/dist/lib/locale.d.ts +15 -0
  159. package/dist/lib/locale.d.ts.map +1 -0
  160. package/dist/lib/locale.js +17 -0
  161. package/dist/lib/locale.js.map +1 -0
  162. package/dist/lib/util/function.d.ts +2 -2
  163. package/dist/lib/util/function.d.ts.map +1 -1
  164. package/dist/lib/util/function.js.map +1 -1
  165. package/dist/lib/util/type.d.ts +88 -1
  166. package/dist/lib/util/type.d.ts.map +1 -1
  167. package/dist/lib/util/type.js +41 -0
  168. package/dist/lib/util/type.js.map +1 -1
  169. package/dist/metadata/build-metadata.d.ts +2 -2
  170. package/dist/metadata/build-metadata.d.ts.map +1 -1
  171. package/dist/metadata/build-metadata.js.map +1 -1
  172. package/dist/oauth-errors.d.ts +1 -0
  173. package/dist/oauth-errors.d.ts.map +1 -1
  174. package/dist/oauth-errors.js +3 -1
  175. package/dist/oauth-errors.js.map +1 -1
  176. package/dist/oauth-hooks.d.ts +60 -3
  177. package/dist/oauth-hooks.d.ts.map +1 -1
  178. package/dist/oauth-hooks.js +3 -3
  179. package/dist/oauth-hooks.js.map +1 -1
  180. package/dist/oauth-provider.d.ts +28 -22
  181. package/dist/oauth-provider.d.ts.map +1 -1
  182. package/dist/oauth-provider.js +212 -211
  183. package/dist/oauth-provider.js.map +1 -1
  184. package/dist/oauth-verifier.d.ts +1 -1
  185. package/dist/oauth-verifier.d.ts.map +1 -1
  186. package/dist/oauth-verifier.js +2 -1
  187. package/dist/oauth-verifier.js.map +1 -1
  188. package/dist/output/build-authorize-data.d.ts +0 -1
  189. package/dist/output/build-authorize-data.d.ts.map +1 -1
  190. package/dist/output/build-authorize-data.js +0 -1
  191. package/dist/output/build-authorize-data.js.map +1 -1
  192. package/dist/output/build-customization-data.d.ts +232 -0
  193. package/dist/output/build-customization-data.d.ts.map +1 -0
  194. package/dist/output/build-customization-data.js +145 -0
  195. package/dist/output/build-customization-data.js.map +1 -0
  196. package/dist/output/output-manager.d.ts +16 -9
  197. package/dist/output/output-manager.d.ts.map +1 -1
  198. package/dist/output/output-manager.js +78 -42
  199. package/dist/output/output-manager.js.map +1 -1
  200. package/dist/output/send-authorize-redirect.d.ts +9 -6
  201. package/dist/output/send-authorize-redirect.d.ts.map +1 -1
  202. package/dist/output/send-authorize-redirect.js +20 -14
  203. package/dist/output/send-authorize-redirect.js.map +1 -1
  204. package/dist/output/send-web-page.d.ts +7 -2
  205. package/dist/output/send-web-page.d.ts.map +1 -1
  206. package/dist/output/send-web-page.js +37 -21
  207. package/dist/output/send-web-page.js.map +1 -1
  208. package/dist/request/request-manager.d.ts +1 -1
  209. package/dist/request/request-manager.d.ts.map +1 -1
  210. package/dist/request/request-manager.js +4 -4
  211. package/dist/request/request-manager.js.map +1 -1
  212. package/dist/request/request-store.d.ts +3 -3
  213. package/dist/request/request-store.d.ts.map +1 -1
  214. package/dist/request/request-store.js +11 -10
  215. package/dist/request/request-store.js.map +1 -1
  216. package/dist/token/token-store.d.ts +4 -4
  217. package/dist/token/token-store.d.ts.map +1 -1
  218. package/dist/token/token-store.js +13 -12
  219. package/dist/token/token-store.js.map +1 -1
  220. package/package.json +46 -21
  221. package/rollup.config.js +61 -17
  222. package/src/account/account-manager.ts +159 -8
  223. package/src/account/account-store.ts +127 -32
  224. package/src/account/sign-in-data.ts +15 -0
  225. package/src/account/sign-up-data.ts +11 -0
  226. package/src/assets/app/app.tsx +31 -16
  227. package/src/assets/app/backend-data.ts +15 -60
  228. package/src/assets/app/backend-types.ts +66 -0
  229. package/src/assets/app/components/forms/button-toggle-visibility.tsx +43 -0
  230. package/src/assets/app/components/forms/button.tsx +60 -0
  231. package/src/assets/app/components/forms/fieldset.tsx +55 -0
  232. package/src/assets/app/components/forms/form-card-async.tsx +103 -0
  233. package/src/assets/app/components/forms/form-card.tsx +49 -0
  234. package/src/assets/app/components/forms/input-checkbox.tsx +73 -0
  235. package/src/assets/app/components/forms/input-container.tsx +107 -0
  236. package/src/assets/app/components/forms/input-email-address.tsx +66 -0
  237. package/src/assets/app/components/forms/input-new-password.tsx +62 -0
  238. package/src/assets/app/components/forms/input-password.tsx +88 -0
  239. package/src/assets/app/components/forms/input-text.tsx +76 -0
  240. package/src/assets/app/components/forms/input-token.tsx +94 -0
  241. package/src/assets/app/components/forms/wizard-card.tsx +116 -0
  242. package/src/assets/app/components/layouts/layout-title-page.tsx +77 -0
  243. package/src/assets/app/components/layouts/layout-welcome.tsx +73 -0
  244. package/src/assets/app/components/utils/account-identifier.tsx +23 -0
  245. package/src/assets/app/components/utils/account-image.tsx +33 -0
  246. package/src/assets/app/components/utils/admonition.tsx +52 -0
  247. package/src/assets/app/components/utils/client-name.tsx +45 -0
  248. package/src/assets/app/components/utils/error-card.tsx +93 -0
  249. package/src/assets/app/components/utils/error-message.tsx +62 -0
  250. package/src/assets/app/components/utils/help-card.tsx +46 -0
  251. package/src/assets/app/components/utils/icons.tsx +88 -0
  252. package/src/assets/app/components/utils/link-anchor.tsx +28 -0
  253. package/src/assets/app/components/utils/link-title.tsx +26 -0
  254. package/src/assets/app/components/utils/multi-lang-string.tsx +56 -0
  255. package/src/assets/app/components/utils/password-strength-label.tsx +37 -0
  256. package/src/assets/app/components/utils/password-strength-meter.tsx +58 -0
  257. package/src/assets/app/components/{url-viewer.tsx → utils/url-viewer.tsx} +9 -6
  258. package/src/assets/app/hooks/use-api.ts +128 -55
  259. package/src/assets/app/hooks/use-async-action.ts +120 -0
  260. package/src/assets/app/hooks/use-browser-color-scheme.ts +31 -0
  261. package/src/assets/app/hooks/use-csrf-token.ts +1 -1
  262. package/src/assets/app/hooks/use-random-string.ts +37 -0
  263. package/src/assets/app/hooks/use-stepper.ts +87 -0
  264. package/src/assets/app/index.html +182 -0
  265. package/src/assets/app/lib/api.ts +248 -79
  266. package/src/assets/app/lib/clsx.ts +5 -8
  267. package/src/assets/app/lib/json-client.ts +94 -0
  268. package/src/assets/app/lib/password.ts +98 -0
  269. package/src/assets/app/lib/ref.ts +17 -0
  270. package/src/assets/app/locales/an/messages.po +492 -0
  271. package/src/assets/app/locales/ast/messages.po +492 -0
  272. package/src/assets/app/locales/ca/messages.po +492 -0
  273. package/src/assets/app/locales/da/messages.po +492 -0
  274. package/src/assets/app/locales/de/messages.po +492 -0
  275. package/src/assets/app/locales/el/messages.po +492 -0
  276. package/src/assets/app/locales/en/messages.po +492 -0
  277. package/src/assets/app/locales/en-GB/messages.po +492 -0
  278. package/src/assets/app/locales/es/messages.po +492 -0
  279. package/src/assets/app/locales/eu/messages.po +492 -0
  280. package/src/assets/app/locales/fi/messages.po +492 -0
  281. package/src/assets/app/locales/fr/messages.po +492 -0
  282. package/src/assets/app/locales/ga/messages.po +492 -0
  283. package/src/assets/app/locales/gl/messages.po +492 -0
  284. package/src/assets/app/locales/hi/messages.po +492 -0
  285. package/src/assets/app/locales/hu/messages.po +492 -0
  286. package/src/assets/app/locales/ia/messages.po +492 -0
  287. package/src/assets/app/locales/id/messages.po +492 -0
  288. package/src/assets/app/locales/it/messages.po +492 -0
  289. package/src/assets/app/locales/ja/messages.po +492 -0
  290. package/src/assets/app/locales/km/messages.po +492 -0
  291. package/src/assets/app/locales/ko/messages.po +492 -0
  292. package/src/assets/app/locales/load.ts +8 -0
  293. package/src/assets/app/locales/locale-context.ts +19 -0
  294. package/src/assets/app/locales/locale-provider.tsx +112 -0
  295. package/src/assets/app/locales/locale-selector.tsx +58 -0
  296. package/src/assets/app/locales/locales.ts +168 -0
  297. package/src/assets/app/locales/ne/messages.po +492 -0
  298. package/src/assets/app/locales/nl/messages.po +492 -0
  299. package/src/assets/app/locales/pl/messages.po +492 -0
  300. package/src/assets/app/locales/pt-BR/messages.po +492 -0
  301. package/src/assets/app/locales/ro/messages.po +492 -0
  302. package/src/assets/app/locales/ru/messages.po +492 -0
  303. package/src/assets/app/locales/sv/messages.po +492 -0
  304. package/src/assets/app/locales/th/messages.po +492 -0
  305. package/src/assets/app/locales/tr/messages.po +492 -0
  306. package/src/assets/app/locales/uk/messages.po +492 -0
  307. package/src/assets/app/locales/vi/messages.po +492 -0
  308. package/src/assets/app/locales/zh-CN/messages.po +492 -0
  309. package/src/assets/app/locales/zh-HK/messages.po +492 -0
  310. package/src/assets/app/locales/zh-TW/messages.po +492 -0
  311. package/src/assets/app/main.css +23 -2
  312. package/src/assets/app/main.tsx +24 -8
  313. package/src/assets/app/views/authorize/accept/accept-form.tsx +150 -0
  314. package/src/assets/app/views/authorize/accept/accept-view.tsx +70 -0
  315. package/src/assets/app/views/authorize/authorize-view.tsx +180 -0
  316. package/src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx +88 -0
  317. package/src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx +80 -0
  318. package/src/assets/app/views/authorize/reset-password/reset-password-view.tsx +127 -0
  319. package/src/assets/app/views/authorize/sign-in/sign-in-form.tsx +244 -0
  320. package/src/assets/app/views/authorize/sign-in/sign-in-picker.tsx +116 -0
  321. package/src/assets/app/views/authorize/sign-in/sign-in-view.tsx +145 -0
  322. package/src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx +140 -0
  323. package/src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx +51 -0
  324. package/src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx +289 -0
  325. package/src/assets/app/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +108 -0
  326. package/src/assets/app/views/authorize/sign-up/sign-up-view.tsx +158 -0
  327. package/src/assets/app/views/authorize/welcome/welcome-view.tsx +56 -0
  328. package/src/assets/app/views/error/error-view.tsx +31 -0
  329. package/src/assets/asset.ts +1 -0
  330. package/src/assets/assets-middleware.ts +13 -8
  331. package/src/assets/index.ts +15 -2
  332. package/src/client/client-store.ts +10 -12
  333. package/src/device/device-manager.ts +14 -15
  334. package/src/device/device-store.ts +9 -15
  335. package/src/dpop/dpop-manager.ts +20 -8
  336. package/src/dpop/dpop-nonce.ts +58 -40
  337. package/src/errors/handle-unavailable-error.ts +18 -0
  338. package/src/errors/invalid-request-error.ts +10 -8
  339. package/src/lib/csp/index.ts +98 -0
  340. package/src/lib/hcaptcha.ts +182 -0
  341. package/src/lib/html/build-document.ts +60 -16
  342. package/src/lib/http/middleware.ts +4 -3
  343. package/src/lib/http/request.ts +81 -28
  344. package/src/lib/http/response.ts +22 -9
  345. package/src/lib/locale.ts +21 -0
  346. package/src/lib/util/function.ts +0 -3
  347. package/src/lib/util/type.ts +130 -1
  348. package/src/metadata/build-metadata.ts +2 -1
  349. package/src/oauth-errors.ts +1 -0
  350. package/src/oauth-hooks.ts +69 -3
  351. package/src/oauth-provider.ts +410 -315
  352. package/src/oauth-verifier.ts +3 -1
  353. package/src/output/build-authorize-data.ts +1 -3
  354. package/src/output/build-customization-data.ts +189 -0
  355. package/src/output/output-manager.ts +111 -48
  356. package/src/output/send-authorize-redirect.ts +43 -36
  357. package/src/output/send-web-page.ts +40 -26
  358. package/src/request/request-manager.ts +4 -4
  359. package/src/request/request-store.ts +12 -16
  360. package/src/token/token-store.ts +14 -18
  361. package/tailwind.config.js +5 -0
  362. package/tsconfig.backend.tsbuildinfo +1 -1
  363. package/tsconfig.frontend.tsbuildinfo +1 -1
  364. package/tsconfig.tools.tsbuildinfo +1 -1
  365. package/vite.config.mjs +16 -0
  366. package/.postcssrc.yml +0 -3
  367. package/dist/assets/app/main.css +0 -3
  368. package/dist/assets/app/main.js +0 -20
  369. package/dist/assets/app/main.js.map +0 -1
  370. package/dist/output/customization.d.ts +0 -27
  371. package/dist/output/customization.d.ts.map +0 -1
  372. package/dist/output/customization.js +0 -88
  373. package/dist/output/customization.js.map +0 -1
  374. package/src/assets/app/components/accept-form.tsx +0 -137
  375. package/src/assets/app/components/account-identifier.tsx +0 -18
  376. package/src/assets/app/components/account-picker.tsx +0 -127
  377. package/src/assets/app/components/button.tsx +0 -34
  378. package/src/assets/app/components/client-name.tsx +0 -37
  379. package/src/assets/app/components/fieldset.tsx +0 -26
  380. package/src/assets/app/components/form-card.tsx +0 -47
  381. package/src/assets/app/components/help-card.tsx +0 -42
  382. package/src/assets/app/components/icons/alert-icon.tsx +0 -5
  383. package/src/assets/app/components/icons/at-symbol-icon.tsx +0 -5
  384. package/src/assets/app/components/icons/caret-right-icon.tsx +0 -5
  385. package/src/assets/app/components/icons/lock-icon.tsx +0 -5
  386. package/src/assets/app/components/icons/token-icon.tsx +0 -5
  387. package/src/assets/app/components/icons/util.tsx +0 -17
  388. package/src/assets/app/components/info-card.tsx +0 -45
  389. package/src/assets/app/components/input-checkbox.tsx +0 -47
  390. package/src/assets/app/components/input-container.tsx +0 -37
  391. package/src/assets/app/components/input-layout.tsx +0 -47
  392. package/src/assets/app/components/input-text.tsx +0 -69
  393. package/src/assets/app/components/layout-title-page.tsx +0 -60
  394. package/src/assets/app/components/layout-welcome.tsx +0 -74
  395. package/src/assets/app/components/sign-in-form.tsx +0 -337
  396. package/src/assets/app/components/sign-up-account-form.tsx +0 -194
  397. package/src/assets/app/components/sign-up-disclaimer.tsx +0 -44
  398. package/src/assets/app/views/accept-view.tsx +0 -55
  399. package/src/assets/app/views/authorize-view.tsx +0 -106
  400. package/src/assets/app/views/error-view.tsx +0 -36
  401. package/src/assets/app/views/sign-in-view.tsx +0 -111
  402. package/src/assets/app/views/sign-up-view.tsx +0 -86
  403. package/src/assets/app/views/welcome-view.tsx +0 -54
  404. package/src/output/customization.ts +0 -118
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.OAuthProvider = exports.Keyset = void 0;
7
- const accept_1 = require("@hapi/accept");
8
7
  const http_errors_1 = __importDefault(require("http-errors"));
9
8
  const zod_1 = require("zod");
10
9
  const jwk_1 = require("@atproto/jwk");
@@ -15,9 +14,10 @@ const simple_store_memory_1 = require("@atproto-labs/simple-store-memory");
15
14
  const access_token_type_js_1 = require("./access-token/access-token-type.js");
16
15
  const account_manager_js_1 = require("./account/account-manager.js");
17
16
  const account_store_js_1 = require("./account/account-store.js");
17
+ const sign_in_data_js_1 = require("./account/sign-in-data.js");
18
+ const sign_up_data_js_1 = require("./account/sign-up-data.js");
18
19
  const assets_middleware_js_1 = require("./assets/assets-middleware.js");
19
20
  const client_auth_js_1 = require("./client/client-auth.js");
20
- const client_id_js_1 = require("./client/client-id.js");
21
21
  const client_manager_js_1 = require("./client/client-manager.js");
22
22
  const client_store_js_1 = require("./client/client-store.js");
23
23
  const constants_js_1 = require("./constants.js");
@@ -31,13 +31,14 @@ const invalid_grant_error_js_1 = require("./errors/invalid-grant-error.js");
31
31
  const invalid_parameters_error_js_1 = require("./errors/invalid-parameters-error.js");
32
32
  const invalid_request_error_js_1 = require("./errors/invalid-request-error.js");
33
33
  const login_required_error_js_1 = require("./errors/login-required-error.js");
34
- const oauth_error_js_1 = require("./errors/oauth-error.js");
35
34
  const unauthorized_client_error_js_1 = require("./errors/unauthorized-client-error.js");
36
35
  const www_authenticate_error_js_1 = require("./errors/www-authenticate-error.js");
37
36
  const index_js_1 = require("./lib/http/index.js");
37
+ const request_js_1 = require("./lib/http/request.js");
38
38
  const date_js_1 = require("./lib/util/date.js");
39
39
  const build_metadata_js_1 = require("./metadata/build-metadata.js");
40
40
  const oauth_verifier_js_1 = require("./oauth-verifier.js");
41
+ const build_customization_data_js_1 = require("./output/build-customization-data.js");
41
42
  const build_error_payload_js_1 = require("./output/build-error-payload.js");
42
43
  const output_manager_js_1 = require("./output/output-manager.js");
43
44
  const send_authorize_redirect_js_1 = require("./output/send-authorize-redirect.js");
@@ -53,14 +54,14 @@ const token_manager_js_1 = require("./token/token-manager.js");
53
54
  const token_store_js_1 = require("./token/token-store.js");
54
55
  class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
55
56
  metadata;
56
- customization;
57
57
  authenticationMaxAge;
58
58
  accountManager;
59
- deviceStore;
59
+ deviceManager;
60
60
  clientManager;
61
61
  requestManager;
62
62
  tokenManager;
63
- constructor({ metadata, customization = undefined, authenticationMaxAge = constants_js_1.AUTHENTICATION_MAX_AGE, tokenMaxAge = constants_js_1.TOKEN_MAX_AGE, safeFetch = (0, fetch_node_1.safeFetchWrap)(), redis, store, // compound store implementation
63
+ outputManager;
64
+ constructor({ metadata, authenticationMaxAge = constants_js_1.AUTHENTICATION_MAX_AGE, tokenMaxAge = constants_js_1.TOKEN_MAX_AGE, safeFetch = (0, fetch_node_1.safeFetchWrap)(), redis, store, // compound store implementation
64
65
  // Requires stores
65
66
  accountStore = (0, account_store_js_1.asAccountStore)(store), deviceStore = (0, device_store_js_1.asDeviceStore)(store), tokenStore = (0, token_store_js_1.asTokenStore)(store),
66
67
  // These are optional
@@ -71,20 +72,36 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
71
72
  maxSize: 50_000_000,
72
73
  ttl: 600e3,
73
74
  }), loopbackMetadata = oauth_types_1.atprotoLoopbackClientMetadata,
74
- // OAuthHooks & OAuthVerifierOptions
75
+ // OAuthHooks &
76
+ // OAuthVerifierOptions &
77
+ // DeviceManagerOptions &
78
+ // Customization
75
79
  ...rest }) {
76
- super({ replayStore, redis, ...rest });
80
+ const customization = build_customization_data_js_1.customizationSchema.parse(rest);
81
+ const deviceManagerOptions = device_manager_js_1.deviceManagerOptionsSchema.parse(rest);
82
+ // @NOTE: hooks don't really need a type parser, as all zod can actually
83
+ // check at runtime is the fact that the values are functions. The only way
84
+ // we would benefit from zod here would be to wrap the functions with a
85
+ // validator for the provided function's return types, which we do not add
86
+ // because it would impact runtime performance and we trust the users of
87
+ // this lib (basically ourselves) to rely on the typing system to ensure the
88
+ // correct types are returned.
89
+ const hooks = rest;
90
+ // @NOTE: validation of super params (if we wanted to implement it) should
91
+ // be the responsibility of the super class.
92
+ const superOptions = rest;
93
+ super({ replayStore, redis, ...superOptions });
77
94
  requestStore ??= redis
78
95
  ? new request_store_redis_js_1.RequestStoreRedis({ redis })
79
96
  : new request_store_memory_js_1.RequestStoreMemory();
80
97
  this.authenticationMaxAge = authenticationMaxAge;
81
98
  this.metadata = (0, build_metadata_js_1.buildMetadata)(this.issuer, this.keyset, metadata);
82
- this.customization = customization;
83
- this.deviceStore = deviceStore;
84
- this.accountManager = new account_manager_js_1.AccountManager(accountStore);
85
- this.clientManager = new client_manager_js_1.ClientManager(this.metadata, this.keyset, rest, clientStore || null, loopbackMetadata || null, safeFetch, clientJwksCache, clientMetadataCache);
86
- this.requestManager = new request_manager_js_1.RequestManager(requestStore, this.signer, this.metadata, rest);
87
- this.tokenManager = new token_manager_js_1.TokenManager(tokenStore, this.signer, rest, this.accessTokenType, tokenMaxAge);
99
+ this.deviceManager = new device_manager_js_1.DeviceManager(deviceStore, deviceManagerOptions);
100
+ this.outputManager = new output_manager_js_1.OutputManager(customization);
101
+ this.accountManager = new account_manager_js_1.AccountManager(this.issuer, accountStore, hooks, customization);
102
+ this.clientManager = new client_manager_js_1.ClientManager(this.metadata, this.keyset, hooks, clientStore || null, loopbackMetadata || null, safeFetch, clientJwksCache, clientMetadataCache);
103
+ this.requestManager = new request_manager_js_1.RequestManager(requestStore, this.signer, this.metadata, hooks);
104
+ this.tokenManager = new token_manager_js_1.TokenManager(tokenStore, this.signer, hooks, this.accessTokenType, tokenMaxAge);
88
105
  }
89
106
  get jwks() {
90
107
  return this.keyset.publicJwks;
@@ -170,8 +187,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
170
187
  // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3-1
171
188
  // > Since initial processing of the pushed authorization request does not
172
189
  // > involve resource owner interaction, error codes related to user
173
- // > interaction, such as consent_required defined by [OIDC], are never
174
- // > returned.
190
+ // > interaction, such as "access_denied", are never returned.
175
191
  if (err instanceof access_denied_error_js_1.AccessDeniedError) {
176
192
  throw new invalid_request_error_js_1.InvalidRequestError(err.error_description, err);
177
193
  }
@@ -183,7 +199,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
183
199
  const requestUri = await request_uri_js_1.requestUriSchema
184
200
  .parseAsync(query.request_uri, { path: ['query', 'request_uri'] })
185
201
  .catch(throwInvalidRequest);
186
- return this.requestManager.get(requestUri, client.id, deviceId);
202
+ return this.requestManager.get(requestUri, deviceId, client.id);
187
203
  }
188
204
  if ('request' in query) {
189
205
  const requestObject = await this.decodeJAR(client, query);
@@ -202,9 +218,9 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
202
218
  }
203
219
  return this.requestManager.createAuthorizationRequest(client, { method: 'none' }, query, deviceId, null);
204
220
  }
205
- async deleteRequest(uri, parameters) {
221
+ async deleteRequest(requestUri, parameters) {
206
222
  try {
207
- await this.requestManager.delete(uri);
223
+ await this.requestManager.delete(requestUri);
208
224
  }
209
225
  catch (err) {
210
226
  throw access_denied_error_js_1.AccessDeniedError.from(parameters, err);
@@ -310,12 +326,21 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
310
326
  matchesHint: hint == null || matchesHint(account),
311
327
  }));
312
328
  }
313
- async signIn(deviceId, uri, clientId, credentials) {
329
+ async signUp({ requestUri, deviceId, deviceMetadata }, data) {
330
+ const { clientId } = await this.requestManager.get(requestUri, deviceId);
314
331
  const client = await this.clientManager.getClient(clientId);
332
+ const { account } = await this.accountManager.signUp(data, deviceId, deviceMetadata);
333
+ return {
334
+ account,
335
+ consentRequired: !client.info.isFirstParty,
336
+ };
337
+ }
338
+ async signIn({ requestUri, deviceId, deviceMetadata }, data) {
315
339
  // Ensure the request is still valid (and update the request expiration)
316
340
  // @TODO use the returned scopes to determine if consent is required
317
- await this.requestManager.get(uri, clientId, deviceId);
318
- const { account, info } = await this.accountManager.signIn(credentials, deviceId);
341
+ const { clientId } = await this.requestManager.get(requestUri, deviceId);
342
+ const client = await this.clientManager.getClient(clientId);
343
+ const { account, info } = await this.accountManager.signIn(data, deviceId, deviceMetadata);
319
344
  return {
320
345
  account,
321
346
  consentRequired: client.info.isFirstParty
@@ -326,28 +351,30 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
326
351
  !info.authorizedClients.includes(client.id),
327
352
  };
328
353
  }
329
- async acceptRequest(uri, clientId, sub, deviceId, deviceMetadata) {
354
+ async acceptRequest({ requestUri, deviceId, deviceMetadata }, sub) {
330
355
  const { issuer } = this;
356
+ const { parameters, clientId, clientAuth } = await this.requestManager.get(requestUri, deviceId);
331
357
  const client = await this.clientManager.getClient(clientId);
332
- const { parameters, clientAuth } = await this.requestManager.get(uri, clientId, deviceId);
333
358
  try {
359
+ // @TODO Currently, a user can "accept" a request for any did that sing-in
360
+ // on the device, even if "remember" was set to false.
334
361
  const { account, info } = await this.accountManager.get(deviceId, sub);
335
362
  // The user is trying to authorize without a fresh login
336
363
  if (this.loginRequired(client, parameters, info)) {
337
364
  throw new login_required_error_js_1.LoginRequiredError(parameters, 'Account authentication required.');
338
365
  }
339
- const code = await this.requestManager.setAuthorized(uri, client, account, deviceId, deviceMetadata);
366
+ const code = await this.requestManager.setAuthorized(requestUri, client, account, deviceId, deviceMetadata);
340
367
  await this.accountManager.addAuthorizedClient(deviceId, account, client, clientAuth);
341
368
  return { issuer, parameters, redirect: { code } };
342
369
  }
343
370
  catch (err) {
344
- await this.deleteRequest(uri, parameters);
371
+ await this.deleteRequest(requestUri, parameters);
345
372
  throw access_denied_error_js_1.AccessDeniedError.from(parameters, err);
346
373
  }
347
374
  }
348
- async rejectRequest(deviceId, uri, clientId) {
349
- const { parameters } = await this.requestManager.get(uri, clientId, deviceId);
350
- await this.deleteRequest(uri, parameters);
375
+ async rejectRequest({ requestUri, deviceId, }) {
376
+ const { parameters } = await this.requestManager.get(requestUri, deviceId);
377
+ await this.deleteRequest(requestUri, parameters);
351
378
  return {
352
379
  issuer: this.issuer,
353
380
  parameters: parameters,
@@ -478,52 +505,60 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
478
505
  return router.buildHandler();
479
506
  }
480
507
  buildRouter(options) {
481
- const deviceManager = new device_manager_js_1.DeviceManager(this.deviceStore, options);
482
- const outputManager = new output_manager_js_1.OutputManager(this.customization);
483
508
  // eslint-disable-next-line @typescript-eslint/no-this-alias
484
509
  const server = this;
485
510
  const issuerUrl = new URL(server.issuer);
486
511
  const issuerOrigin = issuerUrl.origin;
487
512
  const router = new index_js_1.Router(issuerUrl);
488
513
  // Utils
489
- const csrfCookie = (uri) => `csrf-${uri}`;
514
+ const csrfCookie = (requestUri) => `csrf-${requestUri}`;
490
515
  const onError = options?.onError ??
491
516
  (process.env['NODE_ENV'] === 'development'
492
- ? (req, res, err, msg) => console.error(`OAuthProvider error (${msg}):`, err)
493
- : undefined);
494
- /**
495
- * Creates a middleware that will serve static JSON content.
496
- */
497
- const staticJson = (json) => (0, index_js_1.combineMiddlewares)([
498
- function (req, res, next) {
499
- res.setHeader('Access-Control-Allow-Origin', '*');
500
- res.setHeader('Access-Control-Allow-Headers', '*');
501
- res.setHeader('Cache-Control', 'max-age=300');
502
- next();
517
+ ? (req, res, err, msg) => {
518
+ console.error(`OAuthProvider error (${msg}):`, err);
519
+ }
520
+ : null);
521
+ // CORS preflight
522
+ const corsHeaders = function (req, res, next) {
523
+ res.setHeader('Access-Control-Max-Age', '86400'); // 1 day
524
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
525
+ //
526
+ // > For requests without credentials, the literal value "*" can be
527
+ // > specified as a wildcard; the value tells browsers to allow
528
+ // > requesting code from any origin to access the resource.
529
+ // > Attempting to use the wildcard with credentials results in an
530
+ // > error.
531
+ //
532
+ // A "*" is safer to use than reflecting the request origin.
533
+ res.setHeader('Access-Control-Allow-Origin', '*');
534
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
535
+ // > The value "*" only counts as a special wildcard value for
536
+ // > requests without credentials (requests without HTTP cookies or
537
+ // > HTTP authentication information). In requests with credentials,
538
+ // > it is treated as the literal method name "*" without special
539
+ // > semantics.
540
+ res.setHeader('Access-Control-Allow-Methods', '*');
541
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type,DPoP');
542
+ next();
543
+ };
544
+ const corsPreflight = (0, index_js_1.combineMiddlewares)([
545
+ corsHeaders,
546
+ (req, res) => {
547
+ res.writeHead(200).end();
503
548
  },
504
- (0, index_js_1.staticJsonMiddleware)(json),
505
549
  ]);
506
550
  /**
507
551
  * Wrap an OAuth endpoint in a middleware that will set the appropriate
508
552
  * response headers and format the response as JSON.
509
553
  */
510
554
  const jsonHandler = (buildJson, status) => async function (req, res) {
511
- res.setHeader('Access-Control-Allow-Origin', '*');
512
- res.setHeader('Access-Control-Allow-Headers', '*');
513
- // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
514
- res.setHeader('Cache-Control', 'no-store');
515
- res.setHeader('Pragma', 'no-cache');
516
- // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
517
- const dpopNonce = server.nextDpopNonce();
518
- if (dpopNonce) {
519
- const name = 'DPoP-Nonce';
520
- res.setHeader(name, dpopNonce);
521
- res.appendHeader('Access-Control-Expose-Headers', name);
522
- }
523
555
  try {
556
+ // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
557
+ res.setHeader('Cache-Control', 'no-store');
558
+ res.setHeader('Pragma', 'no-cache');
524
559
  // Ensure we can agree on a content encoding & type before starting to
525
560
  // build the JSON response.
526
- if (!(0, accept_1.mediaType)(req.headers['accept'], ['application/json'])) {
561
+ if (!(0, request_js_1.negotiateResponseContent)(req, ['application/json'])) {
527
562
  throw (0, http_errors_1.default)(406, 'Unsupported media type');
528
563
  }
529
564
  const result = await buildJson.call(this, req, res);
@@ -535,12 +570,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
535
570
  }
536
571
  }
537
572
  catch (err) {
573
+ onError?.(req, res, err, 'OAuth request error');
538
574
  if (!res.headersSent) {
539
- if (err instanceof www_authenticate_error_js_1.WWWAuthenticateError) {
540
- const name = 'WWW-Authenticate';
541
- res.setHeader(name, err.wwwAuthenticateHeader);
542
- res.appendHeader('Access-Control-Expose-Headers', name);
543
- }
544
575
  const payload = (0, build_error_payload_js_1.buildErrorPayload)(err);
545
576
  const status = (0, build_error_payload_js_1.buildErrorStatus)(err);
546
577
  (0, index_js_1.writeJson)(res, payload, { status });
@@ -548,19 +579,52 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
548
579
  else {
549
580
  res.destroy();
550
581
  }
551
- // OAuthError are used to build expected responses, so we don't log
552
- // them as errors.
553
- if (!(err instanceof oauth_error_js_1.OAuthError) || err.statusCode >= 500) {
554
- onError?.(req, res, err, 'Unexpected error');
555
- }
556
582
  }
557
583
  };
584
+ const oauthHandler = (buildOAuthResponse, status) => (0, index_js_1.combineMiddlewares)([
585
+ corsHeaders,
586
+ jsonHandler(async function (req, res) {
587
+ try {
588
+ // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
589
+ const dpopNonce = server.nextDpopNonce();
590
+ if (dpopNonce) {
591
+ const name = 'DPoP-Nonce';
592
+ res.setHeader(name, dpopNonce);
593
+ res.appendHeader('Access-Control-Expose-Headers', name);
594
+ }
595
+ return await buildOAuthResponse.call(this, req, res);
596
+ }
597
+ catch (err) {
598
+ if (!res.headersSent && err instanceof www_authenticate_error_js_1.WWWAuthenticateError) {
599
+ const name = 'WWW-Authenticate';
600
+ res.setHeader(name, err.wwwAuthenticateHeader);
601
+ res.appendHeader('Access-Control-Expose-Headers', name);
602
+ }
603
+ throw err;
604
+ }
605
+ }, status),
606
+ ]);
607
+ const apiHandler = (inputSchema, buildJson, status) => jsonHandler(async function (req, res) {
608
+ (0, index_js_1.validateFetchMode)(req, res, ['same-origin']);
609
+ (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
610
+ (0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
611
+ const referer = (0, index_js_1.validateReferer)(req, res, {
612
+ origin: issuerOrigin,
613
+ pathname: '/oauth/authorize',
614
+ });
615
+ const requestUri = await request_uri_js_1.requestUriSchema.parseAsync(referer.searchParams.get('request_uri'), { path: ['query', 'request_uri'] });
616
+ (0, index_js_1.validateCsrfToken)(req, res, req.headers['x-csrf-token'], csrfCookie(requestUri));
617
+ const { deviceId, deviceMetadata } = await server.deviceManager.load(req, res);
618
+ const payload = await (0, index_js_1.parseHttpRequest)(req, ['json']);
619
+ const input = await inputSchema.parseAsync(payload, { path: ['body'] });
620
+ const context = { requestUri, deviceId, deviceMetadata };
621
+ return buildJson.call(this, req, res, input, context);
622
+ }, status);
558
623
  const navigationHandler = (handler) => async function (req, res) {
559
- res.setHeader('Access-Control-Allow-Origin', '*');
560
- res.setHeader('Access-Control-Allow-Headers', '*');
561
- res.setHeader('Cache-Control', 'no-store');
562
- res.setHeader('Pragma', 'no-cache');
563
624
  try {
625
+ res.setHeader('Cache-Control', 'no-store');
626
+ res.setHeader('Pragma', 'no-cache');
627
+ res.setHeader('Referrer-Policy', 'same-origin');
564
628
  (0, index_js_1.validateFetchMode)(req, res, ['navigate']);
565
629
  (0, index_js_1.validateFetchDest)(req, res, ['document']);
566
630
  (0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
@@ -569,10 +633,42 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
569
633
  catch (err) {
570
634
  onError?.(req, res, err, `Failed to handle navigation request to "${req.url}"`);
571
635
  if (!res.headersSent) {
572
- await outputManager.sendErrorPage(res, err);
636
+ await server.outputManager.sendErrorPage(res, err, {
637
+ preferredLocales: (0, request_js_1.extractLocales)(req),
638
+ });
573
639
  }
574
640
  }
575
641
  };
642
+ // Simple GET requests fall under the category of "no-cors" request, meaning
643
+ // that the browser will allow any cross-origin request, with credentials,
644
+ // to be sent to the oauth server. The OAuth Server will, however:
645
+ // 1) validate the request origin (see navigationHandler),
646
+ // 2) validate the CSRF token,
647
+ // 3) validate the referer,
648
+ // 4) validate the sec-fetch-site header,
649
+ // 4) validate the sec-fetch-mode header (see navigationHandler),
650
+ // 5) validate the sec-fetch-dest header (see navigationHandler).
651
+ // And will error (refuse to serve the request) if any of these checks fail.
652
+ const sameOriginNavigationHandler = (handler) => navigationHandler(async function (req, res) {
653
+ (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
654
+ const deviceInfo = await server.deviceManager.load(req, res);
655
+ return handler.call(this, req, res, deviceInfo);
656
+ });
657
+ const authorizeRedirectNavigationHandler = (handler) => sameOriginNavigationHandler(async function (req, res, deviceInfo) {
658
+ const referer = (0, index_js_1.validateReferer)(req, res, {
659
+ origin: issuerOrigin,
660
+ pathname: '/oauth/authorize',
661
+ });
662
+ const requestUri = await request_uri_js_1.requestUriSchema.parseAsync(referer.searchParams.get('request_uri'));
663
+ const csrfToken = this.url.searchParams.get('csrf_token');
664
+ const csrfCookieName = csrfCookie(requestUri);
665
+ // Next line will "clear" the CSRF token cookie, preventing replay of
666
+ // this request (navigating "back" will result in an error).
667
+ (0, index_js_1.validateCsrfToken)(req, res, csrfToken, csrfCookieName, true);
668
+ const context = { ...deviceInfo, requestUri };
669
+ const redirect = await handler.call(this, req, res, context);
670
+ return (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, redirect);
671
+ });
576
672
  /**
577
673
  * Provides a better UX when a request is denied by redirecting to the
578
674
  * client with the error details. This will also log any error that caused
@@ -592,36 +688,12 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
592
688
  throw err;
593
689
  };
594
690
  //- Public OAuth endpoints
595
- router.get('/.well-known/oauth-authorization-server', staticJson(server.metadata));
596
- // CORS preflight
597
- const corsPreflight = function (req, res, _next) {
598
- res
599
- .writeHead(204, {
600
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
601
- //
602
- // > For requests without credentials, the literal value "*" can be
603
- // > specified as a wildcard; the value tells browsers to allow
604
- // > requesting code from any origin to access the resource.
605
- // > Attempting to use the wildcard with credentials results in an
606
- // > error.
607
- //
608
- // A "*" is safer to use than reflecting the request origin.
609
- 'Access-Control-Allow-Origin': '*',
610
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
611
- // > The value "*" only counts as a special wildcard value for
612
- // > requests without credentials (requests without HTTP cookies or
613
- // > HTTP authentication information). In requests with credentials,
614
- // > it is treated as the literal method name "*" without special
615
- // > semantics.
616
- 'Access-Control-Allow-Methods': '*',
617
- 'Access-Control-Allow-Headers': 'Content-Type,Authorization,DPoP',
618
- 'Access-Control-Max-Age': '86400', // 1 day
619
- })
620
- .end();
621
- };
622
- router.get('/oauth/jwks', staticJson(server.jwks));
691
+ router.options('/.well-known/oauth-authorization-server', corsPreflight);
692
+ router.get('/.well-known/oauth-authorization-server', corsHeaders, (0, index_js_1.cacheControlMiddleware)(300), (0, index_js_1.staticJsonMiddleware)(server.metadata));
693
+ router.options('/oauth/jwks', corsPreflight);
694
+ router.get('/oauth/jwks', corsHeaders, (0, index_js_1.cacheControlMiddleware)(300), (0, index_js_1.staticJsonMiddleware)(server.jwks));
623
695
  router.options('/oauth/par', corsPreflight);
624
- router.post('/oauth/par', jsonHandler(async function (req, _res) {
696
+ router.post('/oauth/par', oauthHandler(async function (req, _res) {
625
697
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
626
698
  const credentials = await oauth_types_1.oauthClientCredentialsSchema
627
699
  .parseAsync(payload, { path: ['body'] })
@@ -635,14 +707,13 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
635
707
  // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3
636
708
  // > If the request did not use the POST method, the authorization server
637
709
  // > responds with an HTTP 405 (Method Not Allowed) status code.
638
- router.options('/oauth/par', corsPreflight);
639
710
  router.all('/oauth/par', (req, res) => {
640
711
  res.writeHead(405).end();
641
712
  });
642
713
  router.options('/oauth/token', corsPreflight);
643
- router.post('/oauth/token', jsonHandler(async function (req, _res) {
714
+ router.post('/oauth/token', oauthHandler(async function (req, _res) {
644
715
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
645
- const clientMetadata = await deviceManager.getRequestMetadata(req);
716
+ const clientMetadata = await server.deviceManager.getRequestMetadata(req);
646
717
  const clientCredentials = await oauth_types_1.oauthClientCredentialsSchema
647
718
  .parseAsync(payload, { path: ['body'] })
648
719
  .catch(throwInvalidClient);
@@ -653,7 +724,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
653
724
  return server.token(clientCredentials, clientMetadata, tokenRequest, dpopJkt);
654
725
  }));
655
726
  router.options('/oauth/revoke', corsPreflight);
656
- router.post('/oauth/revoke', jsonHandler(async function (req, res) {
727
+ router.post('/oauth/revoke', oauthHandler(async function (req, res) {
657
728
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
658
729
  const tokenIdentification = await oauth_types_1.oauthTokenIdentificationSchema
659
730
  .parseAsync(payload, { path: ['body'] })
@@ -665,7 +736,6 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
665
736
  onError?.(req, res, err, 'Failed to revoke token');
666
737
  }
667
738
  }));
668
- router.options('/oauth/revoke', corsPreflight);
669
739
  router.get('/oauth/revoke', navigationHandler(async function (req, res) {
670
740
  const query = Object.fromEntries(this.url.searchParams);
671
741
  const tokenIdentification = await oauth_types_1.oauthTokenIdentificationSchema
@@ -681,7 +751,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
681
751
  // todo: generate JSONP response (if "callback" is provided)
682
752
  throw new Error('You are successfully logged out. Redirect not implemented');
683
753
  }));
684
- router.post('/oauth/introspect', jsonHandler(async function (req, _res) {
754
+ router.options('/oauth/introspect', corsPreflight);
755
+ router.post('/oauth/introspect', oauthHandler(async function (req, _res) {
685
756
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
686
757
  const credentials = await oauth_types_1.oauthClientCredentialsSchema
687
758
  .parseAsync(payload, { path: ['body'] })
@@ -697,7 +768,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
697
768
  (0, index_js_1.validateFetchSite)(req, res, ['cross-site', 'none']);
698
769
  const query = Object.fromEntries(this.url.searchParams);
699
770
  const clientCredentials = await oauth_types_1.oauthClientCredentialsSchema
700
- .parseAsync(query, { path: ['body'] })
771
+ .parseAsync(query, { path: ['query'] })
701
772
  .catch(throwInvalidRequest);
702
773
  if ('client_secret' in clientCredentials) {
703
774
  throw new invalid_request_error_js_1.InvalidRequestError('Client secret must not be provided');
@@ -705,117 +776,47 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
705
776
  const authorizationRequest = await oauth_types_1.oauthAuthorizationRequestQuerySchema
706
777
  .parseAsync(query, { path: ['query'] })
707
778
  .catch(throwInvalidRequest);
708
- const { deviceId, deviceMetadata } = await deviceManager.load(req, res);
709
- const data = await server
779
+ const { deviceId, deviceMetadata } = await server.deviceManager.load(req, res);
780
+ const result = await server
710
781
  .authorize(clientCredentials, authorizationRequest, deviceId, deviceMetadata)
711
782
  .catch((err) => accessDeniedToRedirectCatcher(req, res, err));
712
- switch (true) {
713
- case 'redirect' in data: {
714
- return (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
715
- }
716
- case 'authorize' in data: {
717
- await (0, index_js_1.setupCsrfToken)(req, res, csrfCookie(data.authorize.uri));
718
- return outputManager.sendAuthorizePage(res, data);
719
- }
720
- default: {
721
- // Should never happen
722
- throw new Error('Unexpected authorization result');
723
- }
783
+ if ('redirect' in result) {
784
+ return (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, result);
785
+ }
786
+ else {
787
+ await (0, index_js_1.setupCsrfToken)(req, res, csrfCookie(result.authorize.uri));
788
+ return server.outputManager.sendAuthorizePage(res, result, {
789
+ preferredLocales: (0, request_js_1.extractLocales)(req),
790
+ });
724
791
  }
725
792
  }));
726
- const signInPayloadSchema = zod_1.z.object({
727
- csrf_token: zod_1.z.string(),
728
- request_uri: request_uri_js_1.requestUriSchema,
729
- client_id: client_id_js_1.clientIdSchema,
730
- credentials: account_store_js_1.signInCredentialsSchema,
731
- });
732
- router.options('/oauth/authorize/sign-in', corsPreflight);
733
- router.post('/oauth/authorize/sign-in', jsonHandler(async function (req, res) {
734
- (0, index_js_1.validateFetchMode)(req, res, ['same-origin']);
735
- (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
736
- (0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
737
- const payload = await (0, index_js_1.parseHttpRequest)(req, ['json']);
738
- const input = await signInPayloadSchema.parseAsync(payload, {
739
- path: ['body'],
740
- });
741
- (0, index_js_1.validateReferer)(req, res, {
742
- origin: issuerOrigin,
743
- pathname: '/oauth/authorize',
744
- });
745
- (0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri));
746
- const { deviceId } = await deviceManager.load(req, res, true);
747
- return server.signIn(deviceId, input.request_uri, input.client_id, input.credentials);
793
+ router.post('/oauth/authorize/verify-handle-availability', apiHandler(zod_1.z.object({ handle: account_store_js_1.handleSchema }).strict(), async function (req, res, data) {
794
+ return server.accountManager.verifyHandleAvailability(data.handle);
748
795
  }));
749
- const acceptQuerySchema = zod_1.z.object({
750
- csrf_token: zod_1.z.string(),
751
- request_uri: request_uri_js_1.requestUriSchema,
752
- client_id: client_id_js_1.clientIdSchema,
753
- account_sub: zod_1.z.string(),
754
- });
755
- // Though this is a "no-cors" request, meaning that the browser will allow
756
- // any cross-origin request, with credentials, to be sent, the handler will
757
- // 1) validate the request origin,
758
- // 2) validate the CSRF token,
759
- // 3) validate the referer,
760
- // 4) validate the sec-fetch-site header,
761
- // 4) validate the sec-fetch-mode header,
762
- // 5) validate the sec-fetch-dest header (see navigationHandler).
763
- // And will error if any of these checks fail.
764
- router.get('/oauth/authorize/accept', navigationHandler(async function (req, res) {
765
- (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
766
- const query = Object.fromEntries(this.url.searchParams);
767
- const input = await acceptQuerySchema.parseAsync(query, {
768
- path: ['query'],
769
- });
770
- (0, index_js_1.validateReferer)(req, res, {
771
- origin: issuerOrigin,
772
- pathname: '/oauth/authorize',
773
- searchParams: [
774
- ['request_uri', input.request_uri],
775
- ['client_id', input.client_id],
776
- ],
777
- });
778
- (0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri), true);
779
- const { deviceId, deviceMetadata } = await deviceManager.load(req, res);
780
- const data = await server
781
- .acceptRequest(input.request_uri, input.client_id, input.account_sub, deviceId, deviceMetadata)
796
+ router.post('/oauth/authorize/sign-up', apiHandler(sign_up_data_js_1.signUpDataSchema, async function (req, res, data, ctx) {
797
+ return server.signUp(ctx, data);
798
+ }));
799
+ router.post('/oauth/authorize/sign-in', apiHandler(sign_in_data_js_1.signInDataSchema, async function (req, res, data, ctx) {
800
+ return server.signIn(ctx, data);
801
+ }));
802
+ router.post('/oauth/authorize/reset-password-request', apiHandler(account_store_js_1.resetPasswordRequestDataSchema, async function (req, res, data) {
803
+ await server.accountManager.resetPasswordRequest(data);
804
+ }));
805
+ router.post('/oauth/authorize/reset-password-confirm', apiHandler(account_store_js_1.resetPasswordConfirmDataSchema, async function (req, res, data) {
806
+ await server.accountManager.resetPasswordConfirm(data);
807
+ }));
808
+ router.get('/oauth/authorize/accept', authorizeRedirectNavigationHandler(async function (req, res, ctx) {
809
+ const sub = this.url.searchParams.get('account_sub');
810
+ if (!sub)
811
+ throw new invalid_request_error_js_1.InvalidRequestError('Account sub not provided');
812
+ return server
813
+ .acceptRequest(ctx, sub)
782
814
  .catch((err) => accessDeniedToRedirectCatcher(req, res, err));
783
- return await (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
784
815
  }));
785
- const rejectQuerySchema = zod_1.z.object({
786
- csrf_token: zod_1.z.string(),
787
- request_uri: request_uri_js_1.requestUriSchema,
788
- client_id: client_id_js_1.clientIdSchema,
789
- });
790
- // Though this is a "no-cors" request, meaning that the browser will allow
791
- // any cross-origin request, with credentials, to be sent, the handler will
792
- // 1) validate the request origin,
793
- // 2) validate the CSRF token,
794
- // 3) validate the referer,
795
- // 4) validate the sec-fetch-site header,
796
- // 4) validate the sec-fetch-mode header,
797
- // 5) validate the sec-fetch-dest header (see navigationHandler).
798
- // And will error if any of these checks fail.
799
- router.get('/oauth/authorize/reject', navigationHandler(async function (req, res) {
800
- (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
801
- const query = Object.fromEntries(this.url.searchParams);
802
- const input = await rejectQuerySchema.parseAsync(query, {
803
- path: ['query'],
804
- });
805
- (0, index_js_1.validateReferer)(req, res, {
806
- origin: issuerOrigin,
807
- pathname: '/oauth/authorize',
808
- searchParams: [
809
- ['request_uri', input.request_uri],
810
- ['client_id', input.client_id],
811
- ],
812
- });
813
- (0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri), true);
814
- const { deviceId } = await deviceManager.load(req, res);
815
- const data = await server
816
- .rejectRequest(deviceId, input.request_uri, input.client_id)
816
+ router.get('/oauth/authorize/reject', authorizeRedirectNavigationHandler(async function (req, res, ctx) {
817
+ return server
818
+ .rejectRequest(ctx)
817
819
  .catch((err) => accessDeniedToRedirectCatcher(req, res, err));
818
- return await (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
819
820
  }));
820
821
  return router;
821
822
  }