@atproto/oauth-provider 0.4.0 → 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 (402) hide show
  1. package/.linguirc +57 -0
  2. package/CHANGELOG.md +21 -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 +232 -0
  191. package/dist/output/build-customization-data.d.ts.map +1 -0
  192. package/dist/output/build-customization-data.js +145 -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 +399 -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 +189 -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
@@ -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");
@@ -60,7 +61,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
60
61
  requestManager;
61
62
  tokenManager;
62
63
  outputManager;
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
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 & DeviceManagerOptions
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.deviceManager = new device_manager_js_1.DeviceManager(deviceStore, rest);
99
+ this.deviceManager = new device_manager_js_1.DeviceManager(deviceStore, deviceManagerOptions);
83
100
  this.outputManager = new output_manager_js_1.OutputManager(customization);
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);
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,
@@ -484,44 +511,54 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
484
511
  const issuerOrigin = issuerUrl.origin;
485
512
  const router = new index_js_1.Router(issuerUrl);
486
513
  // Utils
487
- const csrfCookie = (uri) => `csrf-${uri}`;
514
+ const csrfCookie = (requestUri) => `csrf-${requestUri}`;
488
515
  const onError = options?.onError ??
489
516
  (process.env['NODE_ENV'] === 'development'
490
- ? (req, res, err, msg) => console.error(`OAuthProvider error (${msg}):`, err)
491
- : undefined);
492
- /**
493
- * Creates a middleware that will serve static JSON content.
494
- */
495
- const staticJson = (json) => (0, index_js_1.combineMiddlewares)([
496
- function (req, res, next) {
497
- res.setHeader('Access-Control-Allow-Origin', '*');
498
- res.setHeader('Access-Control-Allow-Headers', '*');
499
- res.setHeader('Cache-Control', 'max-age=300');
500
- 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();
501
548
  },
502
- (0, index_js_1.staticJsonMiddleware)(json),
503
549
  ]);
504
550
  /**
505
551
  * Wrap an OAuth endpoint in a middleware that will set the appropriate
506
552
  * response headers and format the response as JSON.
507
553
  */
508
554
  const jsonHandler = (buildJson, status) => async function (req, res) {
509
- res.setHeader('Access-Control-Allow-Origin', '*');
510
- res.setHeader('Access-Control-Allow-Headers', '*');
511
- // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
512
- res.setHeader('Cache-Control', 'no-store');
513
- res.setHeader('Pragma', 'no-cache');
514
- // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
515
- const dpopNonce = server.nextDpopNonce();
516
- if (dpopNonce) {
517
- const name = 'DPoP-Nonce';
518
- res.setHeader(name, dpopNonce);
519
- res.appendHeader('Access-Control-Expose-Headers', name);
520
- }
521
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');
522
559
  // Ensure we can agree on a content encoding & type before starting to
523
560
  // build the JSON response.
524
- if (!(0, accept_1.mediaType)(req.headers['accept'], ['application/json'])) {
561
+ if (!(0, request_js_1.negotiateResponseContent)(req, ['application/json'])) {
525
562
  throw (0, http_errors_1.default)(406, 'Unsupported media type');
526
563
  }
527
564
  const result = await buildJson.call(this, req, res);
@@ -533,12 +570,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
533
570
  }
534
571
  }
535
572
  catch (err) {
573
+ onError?.(req, res, err, 'OAuth request error');
536
574
  if (!res.headersSent) {
537
- if (err instanceof www_authenticate_error_js_1.WWWAuthenticateError) {
538
- const name = 'WWW-Authenticate';
539
- res.setHeader(name, err.wwwAuthenticateHeader);
540
- res.appendHeader('Access-Control-Expose-Headers', name);
541
- }
542
575
  const payload = (0, build_error_payload_js_1.buildErrorPayload)(err);
543
576
  const status = (0, build_error_payload_js_1.buildErrorStatus)(err);
544
577
  (0, index_js_1.writeJson)(res, payload, { status });
@@ -546,19 +579,52 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
546
579
  else {
547
580
  res.destroy();
548
581
  }
549
- // OAuthError are used to build expected responses, so we don't log
550
- // them as errors.
551
- if (!(err instanceof oauth_error_js_1.OAuthError) || err.statusCode >= 500) {
552
- onError?.(req, res, err, 'Unexpected error');
553
- }
554
582
  }
555
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);
556
623
  const navigationHandler = (handler) => async function (req, res) {
557
- res.setHeader('Access-Control-Allow-Origin', '*');
558
- res.setHeader('Access-Control-Allow-Headers', '*');
559
- res.setHeader('Cache-Control', 'no-store');
560
- res.setHeader('Pragma', 'no-cache');
561
624
  try {
625
+ res.setHeader('Cache-Control', 'no-store');
626
+ res.setHeader('Pragma', 'no-cache');
627
+ res.setHeader('Referrer-Policy', 'same-origin');
562
628
  (0, index_js_1.validateFetchMode)(req, res, ['navigate']);
563
629
  (0, index_js_1.validateFetchDest)(req, res, ['document']);
564
630
  (0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
@@ -567,10 +633,42 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
567
633
  catch (err) {
568
634
  onError?.(req, res, err, `Failed to handle navigation request to "${req.url}"`);
569
635
  if (!res.headersSent) {
570
- await server.outputManager.sendErrorPage(res, err);
636
+ await server.outputManager.sendErrorPage(res, err, {
637
+ preferredLocales: (0, request_js_1.extractLocales)(req),
638
+ });
571
639
  }
572
640
  }
573
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
+ });
574
672
  /**
575
673
  * Provides a better UX when a request is denied by redirecting to the
576
674
  * client with the error details. This will also log any error that caused
@@ -590,36 +688,12 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
590
688
  throw err;
591
689
  };
592
690
  //- Public OAuth endpoints
593
- router.get('/.well-known/oauth-authorization-server', staticJson(server.metadata));
594
- // CORS preflight
595
- const corsPreflight = function (req, res, _next) {
596
- res
597
- .writeHead(204, {
598
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
599
- //
600
- // > For requests without credentials, the literal value "*" can be
601
- // > specified as a wildcard; the value tells browsers to allow
602
- // > requesting code from any origin to access the resource.
603
- // > Attempting to use the wildcard with credentials results in an
604
- // > error.
605
- //
606
- // A "*" is safer to use than reflecting the request origin.
607
- 'Access-Control-Allow-Origin': '*',
608
- // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
609
- // > The value "*" only counts as a special wildcard value for
610
- // > requests without credentials (requests without HTTP cookies or
611
- // > HTTP authentication information). In requests with credentials,
612
- // > it is treated as the literal method name "*" without special
613
- // > semantics.
614
- 'Access-Control-Allow-Methods': '*',
615
- 'Access-Control-Allow-Headers': 'Content-Type,Authorization,DPoP',
616
- 'Access-Control-Max-Age': '86400', // 1 day
617
- })
618
- .end();
619
- };
620
- 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));
621
695
  router.options('/oauth/par', corsPreflight);
622
- router.post('/oauth/par', jsonHandler(async function (req, _res) {
696
+ router.post('/oauth/par', oauthHandler(async function (req, _res) {
623
697
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
624
698
  const credentials = await oauth_types_1.oauthClientCredentialsSchema
625
699
  .parseAsync(payload, { path: ['body'] })
@@ -633,12 +707,11 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
633
707
  // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3
634
708
  // > If the request did not use the POST method, the authorization server
635
709
  // > responds with an HTTP 405 (Method Not Allowed) status code.
636
- router.options('/oauth/par', corsPreflight);
637
710
  router.all('/oauth/par', (req, res) => {
638
711
  res.writeHead(405).end();
639
712
  });
640
713
  router.options('/oauth/token', corsPreflight);
641
- router.post('/oauth/token', jsonHandler(async function (req, _res) {
714
+ router.post('/oauth/token', oauthHandler(async function (req, _res) {
642
715
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
643
716
  const clientMetadata = await server.deviceManager.getRequestMetadata(req);
644
717
  const clientCredentials = await oauth_types_1.oauthClientCredentialsSchema
@@ -651,7 +724,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
651
724
  return server.token(clientCredentials, clientMetadata, tokenRequest, dpopJkt);
652
725
  }));
653
726
  router.options('/oauth/revoke', corsPreflight);
654
- router.post('/oauth/revoke', jsonHandler(async function (req, res) {
727
+ router.post('/oauth/revoke', oauthHandler(async function (req, res) {
655
728
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
656
729
  const tokenIdentification = await oauth_types_1.oauthTokenIdentificationSchema
657
730
  .parseAsync(payload, { path: ['body'] })
@@ -663,7 +736,6 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
663
736
  onError?.(req, res, err, 'Failed to revoke token');
664
737
  }
665
738
  }));
666
- router.options('/oauth/revoke', corsPreflight);
667
739
  router.get('/oauth/revoke', navigationHandler(async function (req, res) {
668
740
  const query = Object.fromEntries(this.url.searchParams);
669
741
  const tokenIdentification = await oauth_types_1.oauthTokenIdentificationSchema
@@ -679,7 +751,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
679
751
  // todo: generate JSONP response (if "callback" is provided)
680
752
  throw new Error('You are successfully logged out. Redirect not implemented');
681
753
  }));
682
- 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) {
683
756
  const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
684
757
  const credentials = await oauth_types_1.oauthClientCredentialsSchema
685
758
  .parseAsync(payload, { path: ['body'] })
@@ -695,7 +768,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
695
768
  (0, index_js_1.validateFetchSite)(req, res, ['cross-site', 'none']);
696
769
  const query = Object.fromEntries(this.url.searchParams);
697
770
  const clientCredentials = await oauth_types_1.oauthClientCredentialsSchema
698
- .parseAsync(query, { path: ['body'] })
771
+ .parseAsync(query, { path: ['query'] })
699
772
  .catch(throwInvalidRequest);
700
773
  if ('client_secret' in clientCredentials) {
701
774
  throw new invalid_request_error_js_1.InvalidRequestError('Client secret must not be provided');
@@ -704,116 +777,46 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
704
777
  .parseAsync(query, { path: ['query'] })
705
778
  .catch(throwInvalidRequest);
706
779
  const { deviceId, deviceMetadata } = await server.deviceManager.load(req, res);
707
- const data = await server
780
+ const result = await server
708
781
  .authorize(clientCredentials, authorizationRequest, deviceId, deviceMetadata)
709
782
  .catch((err) => accessDeniedToRedirectCatcher(req, res, err));
710
- switch (true) {
711
- case 'redirect' in data: {
712
- return (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
713
- }
714
- case 'authorize' in data: {
715
- await (0, index_js_1.setupCsrfToken)(req, res, csrfCookie(data.authorize.uri));
716
- return server.outputManager.sendAuthorizePage(res, data);
717
- }
718
- default: {
719
- // Should never happen
720
- throw new Error('Unexpected authorization result');
721
- }
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
+ });
722
791
  }
723
792
  }));
724
- const signInPayloadSchema = zod_1.z.object({
725
- csrf_token: zod_1.z.string(),
726
- request_uri: request_uri_js_1.requestUriSchema,
727
- client_id: client_id_js_1.clientIdSchema,
728
- credentials: account_store_js_1.signInCredentialsSchema,
729
- });
730
- router.options('/oauth/authorize/sign-in', corsPreflight);
731
- router.post('/oauth/authorize/sign-in', jsonHandler(async function (req, res) {
732
- (0, index_js_1.validateFetchMode)(req, res, ['same-origin']);
733
- (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
734
- (0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
735
- const payload = await (0, index_js_1.parseHttpRequest)(req, ['json']);
736
- const input = await signInPayloadSchema.parseAsync(payload, {
737
- path: ['body'],
738
- });
739
- (0, index_js_1.validateReferer)(req, res, {
740
- origin: issuerOrigin,
741
- pathname: '/oauth/authorize',
742
- });
743
- (0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri));
744
- const { deviceId } = await server.deviceManager.load(req, res, true);
745
- 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);
746
795
  }));
747
- const acceptQuerySchema = zod_1.z.object({
748
- csrf_token: zod_1.z.string(),
749
- request_uri: request_uri_js_1.requestUriSchema,
750
- client_id: client_id_js_1.clientIdSchema,
751
- account_sub: zod_1.z.string(),
752
- });
753
- // Though this is a "no-cors" request, meaning that the browser will allow
754
- // any cross-origin request, with credentials, to be sent, the handler will
755
- // 1) validate the request origin,
756
- // 2) validate the CSRF token,
757
- // 3) validate the referer,
758
- // 4) validate the sec-fetch-site header,
759
- // 4) validate the sec-fetch-mode header,
760
- // 5) validate the sec-fetch-dest header (see navigationHandler).
761
- // And will error if any of these checks fail.
762
- router.get('/oauth/authorize/accept', navigationHandler(async function (req, res) {
763
- (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
764
- const query = Object.fromEntries(this.url.searchParams);
765
- const input = await acceptQuerySchema.parseAsync(query, {
766
- path: ['query'],
767
- });
768
- (0, index_js_1.validateReferer)(req, res, {
769
- origin: issuerOrigin,
770
- pathname: '/oauth/authorize',
771
- searchParams: [
772
- ['request_uri', input.request_uri],
773
- ['client_id', input.client_id],
774
- ],
775
- });
776
- (0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri), true);
777
- const { deviceId, deviceMetadata } = await server.deviceManager.load(req, res);
778
- const data = await server
779
- .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)
780
814
  .catch((err) => accessDeniedToRedirectCatcher(req, res, err));
781
- return await (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
782
815
  }));
783
- const rejectQuerySchema = zod_1.z.object({
784
- csrf_token: zod_1.z.string(),
785
- request_uri: request_uri_js_1.requestUriSchema,
786
- client_id: client_id_js_1.clientIdSchema,
787
- });
788
- // Though this is a "no-cors" request, meaning that the browser will allow
789
- // any cross-origin request, with credentials, to be sent, the handler will
790
- // 1) validate the request origin,
791
- // 2) validate the CSRF token,
792
- // 3) validate the referer,
793
- // 4) validate the sec-fetch-site header,
794
- // 4) validate the sec-fetch-mode header,
795
- // 5) validate the sec-fetch-dest header (see navigationHandler).
796
- // And will error if any of these checks fail.
797
- router.get('/oauth/authorize/reject', navigationHandler(async function (req, res) {
798
- (0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
799
- const query = Object.fromEntries(this.url.searchParams);
800
- const input = await rejectQuerySchema.parseAsync(query, {
801
- path: ['query'],
802
- });
803
- (0, index_js_1.validateReferer)(req, res, {
804
- origin: issuerOrigin,
805
- pathname: '/oauth/authorize',
806
- searchParams: [
807
- ['request_uri', input.request_uri],
808
- ['client_id', input.client_id],
809
- ],
810
- });
811
- (0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri), true);
812
- const { deviceId } = await server.deviceManager.load(req, res);
813
- const data = await server
814
- .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)
815
819
  .catch((err) => accessDeniedToRedirectCatcher(req, res, err));
816
- return await (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
817
820
  }));
818
821
  return router;
819
822
  }