@atproto/oauth-provider 0.5.2 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/account/account-manager.d.ts +7 -5
  3. package/dist/account/account-manager.d.ts.map +1 -1
  4. package/dist/account/account-manager.js +34 -25
  5. package/dist/account/account-manager.js.map +1 -1
  6. package/dist/account/account-store.d.ts +7 -0
  7. package/dist/account/account-store.d.ts.map +1 -1
  8. package/dist/account/account-store.js.map +1 -1
  9. package/dist/account/account.d.ts +1 -11
  10. package/dist/account/account.d.ts.map +1 -1
  11. package/dist/account/{sign-up-data.d.ts → sign-up-input.d.ts} +3 -3
  12. package/dist/account/sign-up-input.d.ts.map +1 -0
  13. package/dist/account/{sign-up-data.js → sign-up-input.js} +3 -3
  14. package/dist/account/sign-up-input.js.map +1 -0
  15. package/dist/assets/assets-middleware.d.ts +2 -0
  16. package/dist/assets/assets-middleware.d.ts.map +1 -1
  17. package/dist/assets/assets-middleware.js +12 -14
  18. package/dist/assets/assets-middleware.js.map +1 -1
  19. package/dist/lib/csp/index.d.ts +5 -6
  20. package/dist/lib/csp/index.d.ts.map +1 -1
  21. package/dist/lib/csp/index.js +14 -11
  22. package/dist/lib/csp/index.js.map +1 -1
  23. package/dist/lib/hcaptcha.d.ts +15 -12
  24. package/dist/lib/hcaptcha.d.ts.map +1 -1
  25. package/dist/lib/hcaptcha.js +11 -7
  26. package/dist/lib/hcaptcha.js.map +1 -1
  27. package/dist/lib/html/build-document.d.ts +2 -2
  28. package/dist/lib/html/build-document.d.ts.map +1 -1
  29. package/dist/lib/html/build-document.js +11 -7
  30. package/dist/lib/html/build-document.js.map +1 -1
  31. package/dist/lib/html/html.d.ts.map +1 -1
  32. package/dist/lib/html/html.js +10 -13
  33. package/dist/lib/html/html.js.map +1 -1
  34. package/dist/lib/html/util.d.ts +0 -1
  35. package/dist/lib/html/util.d.ts.map +1 -1
  36. package/dist/lib/html/util.js +0 -4
  37. package/dist/lib/html/util.js.map +1 -1
  38. package/dist/lib/http/response.d.ts +3 -1
  39. package/dist/lib/http/response.d.ts.map +1 -1
  40. package/dist/lib/http/response.js +3 -0
  41. package/dist/lib/http/response.js.map +1 -1
  42. package/dist/lib/http/security-headers.d.ts +48 -0
  43. package/dist/lib/http/security-headers.d.ts.map +1 -0
  44. package/dist/lib/http/security-headers.js +62 -0
  45. package/dist/lib/http/security-headers.js.map +1 -0
  46. package/dist/lib/util/type.d.ts +8 -0
  47. package/dist/lib/util/type.d.ts.map +1 -1
  48. package/dist/lib/util/type.js.map +1 -1
  49. package/dist/oauth-hooks.d.ts +4 -25
  50. package/dist/oauth-hooks.d.ts.map +1 -1
  51. package/dist/oauth-provider.js +2 -2
  52. package/dist/oauth-provider.js.map +1 -1
  53. package/dist/output/backend-data.d.ts +4 -0
  54. package/dist/output/backend-data.d.ts.map +1 -0
  55. package/dist/output/backend-data.js +19 -0
  56. package/dist/output/backend-data.js.map +1 -0
  57. package/dist/output/build-authorize-data.d.ts +3 -19
  58. package/dist/output/build-authorize-data.d.ts.map +1 -1
  59. package/dist/output/build-authorize-data.js.map +1 -1
  60. package/dist/output/build-customization-data.d.ts +11 -18
  61. package/dist/output/build-customization-data.d.ts.map +1 -1
  62. package/dist/output/build-customization-data.js +1 -1
  63. package/dist/output/build-customization-data.js.map +1 -1
  64. package/dist/output/build-error-data.d.ts +3 -0
  65. package/dist/output/build-error-data.d.ts.map +1 -0
  66. package/dist/output/build-error-data.js +10 -0
  67. package/dist/output/build-error-data.js.map +1 -0
  68. package/dist/output/build-error-payload.d.ts +2 -1
  69. package/dist/output/build-error-payload.d.ts.map +1 -1
  70. package/dist/output/build-error-payload.js.map +1 -1
  71. package/dist/output/output-manager.d.ts +10 -4
  72. package/dist/output/output-manager.d.ts.map +1 -1
  73. package/dist/output/output-manager.js +68 -39
  74. package/dist/output/output-manager.js.map +1 -1
  75. package/dist/output/send-web-page.d.ts +6 -10
  76. package/dist/output/send-web-page.d.ts.map +1 -1
  77. package/dist/output/send-web-page.js +27 -47
  78. package/dist/output/send-web-page.js.map +1 -1
  79. package/dist/signer/signed-token-payload.d.ts +3 -3
  80. package/dist/signer/signer.d.ts +2 -2
  81. package/package.json +8 -41
  82. package/src/account/account-manager.ts +55 -34
  83. package/src/account/account-store.ts +8 -0
  84. package/src/account/account.ts +1 -14
  85. package/src/account/{sign-up-data.ts → sign-up-input.ts} +2 -2
  86. package/src/assets/assets-middleware.ts +11 -17
  87. package/src/lib/csp/index.ts +16 -13
  88. package/src/lib/hcaptcha.ts +14 -10
  89. package/src/lib/html/build-document.ts +15 -8
  90. package/src/lib/html/html.ts +11 -18
  91. package/src/lib/html/util.ts +0 -4
  92. package/src/lib/http/response.ts +9 -1
  93. package/src/lib/http/security-headers.ts +91 -0
  94. package/src/lib/util/type.ts +18 -0
  95. package/src/oauth-hooks.ts +4 -25
  96. package/src/oauth-provider.ts +2 -2
  97. package/src/output/backend-data.ts +18 -0
  98. package/src/output/build-authorize-data.ts +3 -26
  99. package/src/output/build-customization-data.ts +2 -13
  100. package/src/output/build-error-data.ts +8 -0
  101. package/src/output/build-error-payload.ts +4 -2
  102. package/src/output/output-manager.ts +86 -47
  103. package/src/output/send-web-page.ts +29 -58
  104. package/tsconfig.backend.json +1 -2
  105. package/tsconfig.backend.tsbuildinfo +1 -1
  106. package/tsconfig.json +1 -5
  107. package/.linguirc +0 -57
  108. package/dist/account/sign-up-data.d.ts.map +0 -1
  109. package/dist/account/sign-up-data.js.map +0 -1
  110. package/dist/assets/app/bundle-manifest.json +0 -614
  111. package/dist/assets/app/index-DZHZ9kCP.js +0 -36
  112. package/dist/assets/app/index-DZHZ9kCP.js.map +0 -1
  113. package/dist/assets/app/main-B_dNxQo_.js +0 -4
  114. package/dist/assets/app/main-B_dNxQo_.js.map +0 -1
  115. package/dist/assets/app/main-Dr6y26KY.css +0 -3
  116. package/dist/assets/app/main-Dr6y26KY.js +0 -306
  117. package/dist/assets/app/main-Dr6y26KY.js.map +0 -1
  118. package/dist/assets/app/messages-6_mYuGzB.js +0 -4
  119. package/dist/assets/app/messages-6_mYuGzB.js.map +0 -1
  120. package/dist/assets/app/messages-7wdeBTpD.js +0 -4
  121. package/dist/assets/app/messages-7wdeBTpD.js.map +0 -1
  122. package/dist/assets/app/messages-B-YFoWKc.js +0 -4
  123. package/dist/assets/app/messages-B-YFoWKc.js.map +0 -1
  124. package/dist/assets/app/messages-B10DUOE-.js +0 -4
  125. package/dist/assets/app/messages-B10DUOE-.js.map +0 -1
  126. package/dist/assets/app/messages-B4AwFEeZ.js +0 -4
  127. package/dist/assets/app/messages-B4AwFEeZ.js.map +0 -1
  128. package/dist/assets/app/messages-BDP8MyEC.js +0 -4
  129. package/dist/assets/app/messages-BDP8MyEC.js.map +0 -1
  130. package/dist/assets/app/messages-BIS87lxQ.js +0 -4
  131. package/dist/assets/app/messages-BIS87lxQ.js.map +0 -1
  132. package/dist/assets/app/messages-BI_Wbjdt.js +0 -4
  133. package/dist/assets/app/messages-BI_Wbjdt.js.map +0 -1
  134. package/dist/assets/app/messages-BMAouhRx.js +0 -4
  135. package/dist/assets/app/messages-BMAouhRx.js.map +0 -1
  136. package/dist/assets/app/messages-BdckMnJj.js +0 -4
  137. package/dist/assets/app/messages-BdckMnJj.js.map +0 -1
  138. package/dist/assets/app/messages-BgBLzc46.js +0 -4
  139. package/dist/assets/app/messages-BgBLzc46.js.map +0 -1
  140. package/dist/assets/app/messages-BobD78yK.js +0 -4
  141. package/dist/assets/app/messages-BobD78yK.js.map +0 -1
  142. package/dist/assets/app/messages-BtThT9UZ.js +0 -4
  143. package/dist/assets/app/messages-BtThT9UZ.js.map +0 -1
  144. package/dist/assets/app/messages-BwKHkbeh.js +0 -4
  145. package/dist/assets/app/messages-BwKHkbeh.js.map +0 -1
  146. package/dist/assets/app/messages-C417YUvA.js +0 -4
  147. package/dist/assets/app/messages-C417YUvA.js.map +0 -1
  148. package/dist/assets/app/messages-C4CxO4bO.js +0 -4
  149. package/dist/assets/app/messages-C4CxO4bO.js.map +0 -1
  150. package/dist/assets/app/messages-C5vd04e6.js +0 -4
  151. package/dist/assets/app/messages-C5vd04e6.js.map +0 -1
  152. package/dist/assets/app/messages-CAri2Wnz.js +0 -4
  153. package/dist/assets/app/messages-CAri2Wnz.js.map +0 -1
  154. package/dist/assets/app/messages-CPtWTZeG.js +0 -4
  155. package/dist/assets/app/messages-CPtWTZeG.js.map +0 -1
  156. package/dist/assets/app/messages-CiaM5zm8.js +0 -4
  157. package/dist/assets/app/messages-CiaM5zm8.js.map +0 -1
  158. package/dist/assets/app/messages-CkL-L2R6.js +0 -4
  159. package/dist/assets/app/messages-CkL-L2R6.js.map +0 -1
  160. package/dist/assets/app/messages-Cy_4XLNe.js +0 -4
  161. package/dist/assets/app/messages-Cy_4XLNe.js.map +0 -1
  162. package/dist/assets/app/messages-D5_ad-Eo.js +0 -4
  163. package/dist/assets/app/messages-D5_ad-Eo.js.map +0 -1
  164. package/dist/assets/app/messages-DChMl9mT.js +0 -4
  165. package/dist/assets/app/messages-DChMl9mT.js.map +0 -1
  166. package/dist/assets/app/messages-DWX-DIfv.js +0 -4
  167. package/dist/assets/app/messages-DWX-DIfv.js.map +0 -1
  168. package/dist/assets/app/messages-DgfsOphe.js +0 -4
  169. package/dist/assets/app/messages-DgfsOphe.js.map +0 -1
  170. package/dist/assets/app/messages-Dj5B_DR6.js +0 -4
  171. package/dist/assets/app/messages-Dj5B_DR6.js.map +0 -1
  172. package/dist/assets/app/messages-Dwzqo4eA.js +0 -4
  173. package/dist/assets/app/messages-Dwzqo4eA.js.map +0 -1
  174. package/dist/assets/app/messages-ESCIXJR7.js +0 -4
  175. package/dist/assets/app/messages-ESCIXJR7.js.map +0 -1
  176. package/dist/assets/app/messages-dglB2edb.js +0 -4
  177. package/dist/assets/app/messages-dglB2edb.js.map +0 -1
  178. package/dist/assets/app/messages-e_ClRrWc.js +0 -4
  179. package/dist/assets/app/messages-e_ClRrWc.js.map +0 -1
  180. package/dist/assets/app/messages-evvDxmrP.js +0 -4
  181. package/dist/assets/app/messages-evvDxmrP.js.map +0 -1
  182. package/dist/assets/app/messages-pPbdLb5B.js +0 -4
  183. package/dist/assets/app/messages-pPbdLb5B.js.map +0 -1
  184. package/dist/assets/app/messages-tJv8gHL2.js +0 -4
  185. package/dist/assets/app/messages-tJv8gHL2.js.map +0 -1
  186. package/dist/assets/app/messages-vLRVEw96.js +0 -4
  187. package/dist/assets/app/messages-vLRVEw96.js.map +0 -1
  188. package/dist/assets/asset.d.ts +0 -9
  189. package/dist/assets/asset.d.ts.map +0 -1
  190. package/dist/assets/asset.js +0 -3
  191. package/dist/assets/asset.js.map +0 -1
  192. package/dist/assets/index.d.ts +0 -5
  193. package/dist/assets/index.d.ts.map +0 -1
  194. package/dist/assets/index.js +0 -78
  195. package/dist/assets/index.js.map +0 -1
  196. package/rollup.config.js +0 -98
  197. package/src/assets/app/app.tsx +0 -43
  198. package/src/assets/app/backend-data.ts +0 -27
  199. package/src/assets/app/backend-types.ts +0 -66
  200. package/src/assets/app/components/forms/button-toggle-visibility.tsx +0 -43
  201. package/src/assets/app/components/forms/button.tsx +0 -60
  202. package/src/assets/app/components/forms/fieldset.tsx +0 -55
  203. package/src/assets/app/components/forms/form-card-async.tsx +0 -103
  204. package/src/assets/app/components/forms/form-card.tsx +0 -49
  205. package/src/assets/app/components/forms/input-checkbox.tsx +0 -78
  206. package/src/assets/app/components/forms/input-container.tsx +0 -107
  207. package/src/assets/app/components/forms/input-email-address.tsx +0 -65
  208. package/src/assets/app/components/forms/input-new-password.tsx +0 -62
  209. package/src/assets/app/components/forms/input-password.tsx +0 -87
  210. package/src/assets/app/components/forms/input-text.tsx +0 -82
  211. package/src/assets/app/components/forms/input-token.tsx +0 -94
  212. package/src/assets/app/components/forms/wizard-card.tsx +0 -116
  213. package/src/assets/app/components/layouts/layout-title-page.tsx +0 -77
  214. package/src/assets/app/components/layouts/layout-welcome.tsx +0 -73
  215. package/src/assets/app/components/utils/account-identifier.tsx +0 -23
  216. package/src/assets/app/components/utils/account-image.tsx +0 -33
  217. package/src/assets/app/components/utils/admonition.tsx +0 -52
  218. package/src/assets/app/components/utils/client-name.tsx +0 -45
  219. package/src/assets/app/components/utils/error-card.tsx +0 -93
  220. package/src/assets/app/components/utils/error-message.tsx +0 -88
  221. package/src/assets/app/components/utils/help-card.tsx +0 -46
  222. package/src/assets/app/components/utils/icons.tsx +0 -88
  223. package/src/assets/app/components/utils/link-anchor.tsx +0 -28
  224. package/src/assets/app/components/utils/link-title.tsx +0 -26
  225. package/src/assets/app/components/utils/multi-lang-string.tsx +0 -56
  226. package/src/assets/app/components/utils/password-strength-label.tsx +0 -37
  227. package/src/assets/app/components/utils/password-strength-meter.tsx +0 -58
  228. package/src/assets/app/components/utils/url-viewer.tsx +0 -73
  229. package/src/assets/app/cookies.ts +0 -11
  230. package/src/assets/app/hooks/use-api.ts +0 -178
  231. package/src/assets/app/hooks/use-async-action.ts +0 -120
  232. package/src/assets/app/hooks/use-bound-dispatch.ts +0 -5
  233. package/src/assets/app/hooks/use-browser-color-scheme.ts +0 -31
  234. package/src/assets/app/hooks/use-csrf-token.ts +0 -5
  235. package/src/assets/app/hooks/use-random-string.ts +0 -37
  236. package/src/assets/app/hooks/use-stepper.ts +0 -87
  237. package/src/assets/app/index.html +0 -182
  238. package/src/assets/app/lib/api.ts +0 -289
  239. package/src/assets/app/lib/clsx.ts +0 -6
  240. package/src/assets/app/lib/json-client.ts +0 -94
  241. package/src/assets/app/lib/password.ts +0 -98
  242. package/src/assets/app/lib/ref.ts +0 -17
  243. package/src/assets/app/lib/util.ts +0 -13
  244. package/src/assets/app/locales/an/messages.po +0 -490
  245. package/src/assets/app/locales/ast/messages.po +0 -490
  246. package/src/assets/app/locales/ca/messages.po +0 -490
  247. package/src/assets/app/locales/da/messages.po +0 -490
  248. package/src/assets/app/locales/de/messages.po +0 -490
  249. package/src/assets/app/locales/el/messages.po +0 -490
  250. package/src/assets/app/locales/en/messages.po +0 -490
  251. package/src/assets/app/locales/en-GB/messages.po +0 -490
  252. package/src/assets/app/locales/es/messages.po +0 -490
  253. package/src/assets/app/locales/eu/messages.po +0 -490
  254. package/src/assets/app/locales/fi/messages.po +0 -490
  255. package/src/assets/app/locales/fr/messages.po +0 -490
  256. package/src/assets/app/locales/ga/messages.po +0 -490
  257. package/src/assets/app/locales/gl/messages.po +0 -490
  258. package/src/assets/app/locales/hi/messages.po +0 -490
  259. package/src/assets/app/locales/hu/messages.po +0 -490
  260. package/src/assets/app/locales/ia/messages.po +0 -490
  261. package/src/assets/app/locales/id/messages.po +0 -490
  262. package/src/assets/app/locales/it/messages.po +0 -490
  263. package/src/assets/app/locales/ja/messages.po +0 -490
  264. package/src/assets/app/locales/km/messages.po +0 -490
  265. package/src/assets/app/locales/ko/messages.po +0 -490
  266. package/src/assets/app/locales/load.ts +0 -8
  267. package/src/assets/app/locales/locale-context.ts +0 -19
  268. package/src/assets/app/locales/locale-provider.tsx +0 -112
  269. package/src/assets/app/locales/locale-selector.tsx +0 -58
  270. package/src/assets/app/locales/locales.ts +0 -168
  271. package/src/assets/app/locales/ne/messages.po +0 -490
  272. package/src/assets/app/locales/nl/messages.po +0 -490
  273. package/src/assets/app/locales/pl/messages.po +0 -490
  274. package/src/assets/app/locales/pt-BR/messages.po +0 -490
  275. package/src/assets/app/locales/ro/messages.po +0 -490
  276. package/src/assets/app/locales/ru/messages.po +0 -490
  277. package/src/assets/app/locales/sv/messages.po +0 -490
  278. package/src/assets/app/locales/th/messages.po +0 -490
  279. package/src/assets/app/locales/tr/messages.po +0 -490
  280. package/src/assets/app/locales/uk/messages.po +0 -490
  281. package/src/assets/app/locales/vi/messages.po +0 -490
  282. package/src/assets/app/locales/zh-CN/messages.po +0 -490
  283. package/src/assets/app/locales/zh-HK/messages.po +0 -490
  284. package/src/assets/app/locales/zh-TW/messages.po +0 -490
  285. package/src/assets/app/main.css +0 -33
  286. package/src/assets/app/main.tsx +0 -44
  287. package/src/assets/app/views/authorize/accept/accept-form.tsx +0 -150
  288. package/src/assets/app/views/authorize/accept/accept-view.tsx +0 -70
  289. package/src/assets/app/views/authorize/authorize-view.tsx +0 -180
  290. package/src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx +0 -88
  291. package/src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx +0 -80
  292. package/src/assets/app/views/authorize/reset-password/reset-password-view.tsx +0 -127
  293. package/src/assets/app/views/authorize/sign-in/sign-in-form.tsx +0 -242
  294. package/src/assets/app/views/authorize/sign-in/sign-in-picker.tsx +0 -116
  295. package/src/assets/app/views/authorize/sign-in/sign-in-view.tsx +0 -145
  296. package/src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx +0 -142
  297. package/src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx +0 -51
  298. package/src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx +0 -287
  299. package/src/assets/app/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +0 -108
  300. package/src/assets/app/views/authorize/sign-up/sign-up-view.tsx +0 -158
  301. package/src/assets/app/views/authorize/welcome/welcome-view.tsx +0 -56
  302. package/src/assets/app/views/error/error-view.tsx +0 -31
  303. package/src/assets/asset.ts +0 -9
  304. package/src/assets/index.ts +0 -86
  305. package/tailwind.config.js +0 -31
  306. package/tsconfig.frontend.json +0 -11
  307. package/tsconfig.frontend.tsbuildinfo +0 -1
  308. package/tsconfig.tools.json +0 -8
  309. package/tsconfig.tools.tsbuildinfo +0 -1
  310. package/vite.config.mjs +0 -16
@@ -1,8 +1,10 @@
1
1
  import type { ServerResponse } from 'node:http'
2
- import { Asset } from '../assets/asset.js'
3
- import { enumerateAssets } from '../assets/index.js'
2
+ import { CustomizationData } from '@atproto/oauth-provider-api'
3
+ import { assets } from '@atproto/oauth-provider-ui'
4
+ import { buildAssetUrl } from '../assets/assets-middleware.js'
4
5
  import { CspConfig, mergeCsp } from '../lib/csp/index.js'
5
6
  import {
7
+ AssetRef,
6
8
  Html,
7
9
  LinkAttrs,
8
10
  MetaAttrs,
@@ -10,7 +12,9 @@ import {
10
12
  html,
11
13
  isLinkRel,
12
14
  } from '../lib/html/index.js'
15
+ import { CrossOriginEmbedderPolicy } from '../lib/http/security-headers.js'
13
16
  import { AVAILABLE_LOCALES, Locale, isAvailableLocale } from '../lib/locale.js'
17
+ import { declareBackendData } from './backend-data.js'
14
18
  import {
15
19
  AuthorizationResultAuthorize,
16
20
  buildAuthorizeData,
@@ -21,15 +25,28 @@ import {
21
25
  buildCustomizationCss,
22
26
  buildCustomizationData,
23
27
  } from './build-customization-data.js'
24
- import { buildErrorPayload, buildErrorStatus } from './build-error-payload.js'
25
- import { assetToCsp, declareBackendData, sendWebPage } from './send-web-page.js'
28
+ import { buildErrorData } from './build-error-data.js'
29
+ import { buildErrorStatus } from './build-error-payload.js'
30
+ import { sendWebPage } from './send-web-page.js'
31
+
32
+ const BASE_CSP: CspConfig = {
33
+ // API calls are made to the same origin
34
+ 'connect-src': ["'self'"],
35
+ // Allow loading of PDS logo & User avatars
36
+ 'img-src': ['data:', 'https:'],
37
+ // Prevent embedding in iframes
38
+ 'frame-ancestors': ["'none'"],
39
+ }
26
40
 
27
- const HCAPTCHA_CSP = {
41
+ /**
42
+ * @see {@link https://docs.hcaptcha.com/#content-security-policy-settings}
43
+ */
44
+ const HCAPTCHA_CSP: CspConfig = {
28
45
  'script-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
29
46
  'frame-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
30
47
  'style-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
31
48
  'connect-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
32
- } as const satisfies CspConfig
49
+ }
33
50
 
34
51
  export type SendPageOptions = {
35
52
  preferredLocales?: readonly string[]
@@ -41,43 +58,57 @@ export class OutputManager {
41
58
  { name: 'robots', content: 'noindex' },
42
59
  { name: 'description', content: 'ATProto OAuth authorization page' },
43
60
  ]
44
- readonly scripts: readonly (Asset | Html)[]
45
- readonly styles: readonly (Asset | Html)[]
46
61
  readonly csp: CspConfig
62
+ readonly coep: CrossOriginEmbedderPolicy
63
+ readonly customizationData: CustomizationData
64
+ readonly customizationCss: Html
47
65
 
48
66
  constructor(customization: Customization) {
49
67
  this.links = customization.branding?.links
50
68
 
51
- const scripts = Array.from(enumerateAssets('application/javascript'))
52
- const styles = Array.from(enumerateAssets('text/css'))
53
-
54
- // Note: building scripts/styles/csp here for two reasons:
55
- // 1. To avoid re-building it on every request
56
- // 2. To throw during init if the customization/config is invalid
57
-
58
- this.scripts = [
59
- declareBackendData('__availableLocales', AVAILABLE_LOCALES),
60
- declareBackendData(
61
- '__customizationData',
62
- buildCustomizationData(customization),
63
- ),
64
- // Last (to be able to read the "backend data" variables)
65
- ...scripts.filter((asset) => asset.isEntry),
66
- ]
67
-
68
- this.styles = [
69
- // First (to be overridden by customization)
70
- ...styles,
71
- cssCode(buildCustomizationCss(customization)),
72
- ]
73
-
74
- const customizationCsp = customization?.hcaptcha ? HCAPTCHA_CSP : undefined
75
- const assetsCsp: CspConfig = {
76
- 'script-src': scripts.map(assetToCsp),
77
- 'style-src': styles.map(assetToCsp),
78
- }
69
+ // "cache" these:
70
+ this.customizationData = buildCustomizationData(customization)
71
+ this.customizationCss = cssCode(buildCustomizationCss(customization))
79
72
 
80
- this.csp = mergeCsp(customizationCsp, assetsCsp)
73
+ this.csp = mergeCsp(
74
+ BASE_CSP,
75
+ customization?.hcaptcha ? HCAPTCHA_CSP : undefined,
76
+ )
77
+ // Because we are loading avatar images from external sources, that might
78
+ // not have CORP headers set, we need to use at least "credentialless".
79
+ this.coep = customization?.hcaptcha
80
+ ? // https://github.com/hCaptcha/react-hcaptcha/issues/259
81
+ // @TODO Remove the use of `unsafeNone` once the issue above is resolved
82
+ CrossOriginEmbedderPolicy.unsafeNone
83
+ : CrossOriginEmbedderPolicy.credentialless
84
+ }
85
+
86
+ buildAssets(
87
+ name: string,
88
+ backendData: Record<string, unknown>,
89
+ ): {
90
+ scripts: (AssetRef | Html)[]
91
+ styles: (AssetRef | Html)[]
92
+ } {
93
+ return {
94
+ scripts: [
95
+ declareBackendData(backendData),
96
+ // After backend injected data
97
+ ...Array.from(assets)
98
+ .filter(
99
+ ([, item]) =>
100
+ item.type === 'chunk' && item.isEntry && item.name === name,
101
+ )
102
+ .map(([filename]) => ({ url: buildAssetUrl(filename) })),
103
+ ],
104
+ styles: [
105
+ ...Array.from(assets)
106
+ .filter(([, item]) => item.mime === 'text/css')
107
+ .map(([filename]) => ({ url: buildAssetUrl(filename) })),
108
+ // Last (to be able to override the default styles)
109
+ this.customizationCss,
110
+ ],
111
+ }
81
112
  }
82
113
 
83
114
  async sendAuthorizePage(
@@ -89,17 +120,21 @@ export class OutputManager {
89
120
  data.parameters.ui_locales?.split(' ') ?? options?.preferredLocales,
90
121
  )
91
122
 
123
+ const { scripts, styles } = this.buildAssets('authorization-page', {
124
+ __availableLocales: AVAILABLE_LOCALES,
125
+ __customizationData: this.customizationData,
126
+ __authorizeData: buildAuthorizeData(data),
127
+ })
128
+
92
129
  return sendWebPage(res, {
93
- scripts: [
94
- declareBackendData('__authorizeData', buildAuthorizeData(data)),
95
- ...this.scripts,
96
- ],
97
- styles: this.styles,
130
+ scripts,
131
+ styles,
98
132
  meta: this.meta,
99
133
  links: this.buildLinks(locale),
100
134
  htmlAttrs: { lang: locale },
101
135
  body: html`<div id="root"></div>`,
102
136
  csp: this.csp,
137
+ coep: this.coep,
103
138
  })
104
139
  }
105
140
 
@@ -110,18 +145,22 @@ export class OutputManager {
110
145
  ): Promise<void> {
111
146
  const locale = negotiateLocale(options?.preferredLocales)
112
147
 
148
+ const { scripts, styles } = this.buildAssets('error-page', {
149
+ __availableLocales: AVAILABLE_LOCALES,
150
+ __customizationData: this.customizationData,
151
+ __errorData: buildErrorData(err),
152
+ })
153
+
113
154
  return sendWebPage(res, {
114
155
  status: buildErrorStatus(err),
115
- scripts: [
116
- declareBackendData('__errorData', buildErrorPayload(err)),
117
- ...this.scripts,
118
- ],
119
- styles: this.styles,
156
+ scripts,
157
+ styles,
120
158
  meta: this.meta,
121
159
  links: this.buildLinks(locale),
122
160
  htmlAttrs: { lang: locale },
123
161
  body: html`<div id="root"></div>`,
124
162
  csp: this.csp,
163
+ coep: this.coep,
125
164
  })
126
165
  }
127
166
 
@@ -1,82 +1,53 @@
1
1
  import { createHash } from 'node:crypto'
2
2
  import type { ServerResponse } from 'node:http'
3
- import { CspConfig, CspValue, buildCsp, mergeCsp } from '../lib/csp/index.js'
3
+ import { CspConfig, CspValue, mergeCsp } from '../lib/csp/index.js'
4
4
  import {
5
5
  AssetRef,
6
6
  BuildDocumentOptions,
7
7
  Html,
8
8
  buildDocument,
9
- js,
10
9
  } from '../lib/html/index.js'
11
- import { WriteResponseOptions, writeHtml } from '../lib/http/response.js'
10
+ import { WriteHtmlOptions, writeHtml } from '../lib/http/response.js'
12
11
 
13
- export function declareBackendData(name: string, data: unknown) {
14
- // The script tag is removed after the data is assigned to the global variable
15
- // to prevent other scripts from deducing the value of the variable. The "app"
16
- // script will read the global variable and then unset it. See
17
- // "readBackendData" in "src/assets/app/backend-types.ts".
18
- return js`window[${name}]=${data};document.currentScript.remove();`
12
+ export const DEFAULT_CSP: CspConfig = {
13
+ 'upgrade-insecure-requests': true,
14
+ 'default-src': ["'none'"],
19
15
  }
20
16
 
21
- export type SendWebPageOptions = BuildDocumentOptions &
22
- WriteResponseOptions & {
23
- csp?: CspConfig
24
- }
17
+ export type SendWebPageOptions = BuildDocumentOptions & WriteHtmlOptions
25
18
 
26
19
  export async function sendWebPage(
27
20
  res: ServerResponse,
28
- options: SendWebPageOptions,
21
+ { csp: inputCsp, ...options }: SendWebPageOptions,
29
22
  ): Promise<void> {
30
- const csp = mergeCsp(options.csp, {
31
- 'default-src': ["'none'"],
23
+ // @NOTE the csp string might be quite long. In that case it might be tempting
24
+ // to set it through the http-equiv <meta> in the HTML. However, some
25
+ // directives cannot be enforced by browsers when set through the meta tag
26
+ // (e.g. 'frame-ancestors'). Therefore, it's better to set the CSP through the
27
+ // HTTP header.
28
+ const csp = mergeCsp(DEFAULT_CSP, inputCsp, {
32
29
  'base-uri': options.base?.origin as undefined | `https://${string}`,
33
- 'script-src': ["'self'", ...assetsToCsp(options.scripts)],
34
- 'style-src': ["'self'", ...assetsToCsp(options.styles)],
35
- 'img-src': ["'self'", 'data:', 'https:'],
36
- 'connect-src': ["'self'"],
37
- 'upgrade-insecure-requests': true,
38
-
39
- // Prevents the CSP to be embedded in a page <meta>:
40
- 'frame-ancestors': ["'none'"],
30
+ 'script-src': options.scripts?.map(assetToCsp),
31
+ 'style-src': options.styles?.map(assetToCsp),
41
32
  })
42
33
 
43
- // @NOTE the csp string might become too long. However, since we need to
44
- // specify the "frame-ancestors" directive, we can't use a meta tag. For that
45
- // reason, we won't try to avoid too long headers and let the proxy throw
46
- // in case of a too long header.
47
- res.setHeader('Content-Security-Policy', buildCsp(csp))
48
-
49
- // @TODO: make these headers configurable (?)
50
- res.setHeader('Permissions-Policy', 'otp-credentials=*, document-domain=()')
51
- res.setHeader('Cross-Origin-Embedder-Policy', 'credentialless')
52
- res.setHeader('Cross-Origin-Resource-Policy', 'same-origin')
53
- res.setHeader('Cross-Origin-Opener-Policy', 'same-origin')
54
- res.setHeader('Referrer-Policy', 'same-origin')
55
- res.setHeader('X-Frame-Options', 'DENY')
56
- res.setHeader('X-Content-Type-Options', 'nosniff')
57
- res.setHeader('X-XSS-Protection', '0')
58
- res.setHeader('Strict-Transport-Security', 'max-age=63072000')
59
-
60
- const html = buildDocument(options)
61
-
62
- return writeHtml(res, html.toString(), options)
34
+ const html = buildDocument(options).toString()
35
+ return writeHtml(res, html, { ...options, csp })
63
36
  }
64
37
 
65
- export function* assetsToCsp(
66
- assets?: Iterable<Html | AssetRef>,
67
- ): Generator<CspValue> {
68
- if (assets) {
69
- for (const asset of assets) {
70
- yield assetToCsp(asset)
71
- }
72
- }
73
- }
74
-
75
- export function assetToCsp(asset: Html | AssetRef): CspValue {
38
+ function assetToCsp(asset: Html | AssetRef): CspValue {
76
39
  if (asset instanceof Html) {
77
- const hash = createHash('sha256').update(asset.toString()).digest('base64')
78
- return `'sha256-${hash}'`
40
+ // Inline assets are "allowed" by their hash
41
+ const hash = createHash('sha256')
42
+ for (const fragment of asset) hash.update(fragment)
43
+ return `'sha256-${hash.digest('base64')}'`
79
44
  } else {
80
- return `'sha256-${asset.sha256}'`
45
+ // External assets are referenced by their origin
46
+ if (asset.url.startsWith('https:') || asset.url.startsWith('http:')) {
47
+ return new URL(asset.url).origin as `https:${string}` | `http:${string}`
48
+ }
49
+
50
+ // Internal assets are served from the same origin
51
+ return `'self'`
81
52
  }
82
53
  }
@@ -4,6 +4,5 @@
4
4
  "outDir": "dist",
5
5
  "rootDir": "src"
6
6
  },
7
- "include": ["src"],
8
- "exclude": ["src/assets/app"]
7
+ "include": ["src"]
9
8
  }
@@ -1 +1 @@
1
- {"root":["./src/constants.ts","./src/index.ts","./src/oauth-client.ts","./src/oauth-dpop.ts","./src/oauth-errors.ts","./src/oauth-hooks.ts","./src/oauth-provider.ts","./src/oauth-store.ts","./src/oauth-verifier.ts","./src/access-token/access-token-type.ts","./src/account/account-manager.ts","./src/account/account-store.ts","./src/account/account.ts","./src/account/sign-in-data.ts","./src/account/sign-up-data.ts","./src/assets/asset.ts","./src/assets/assets-middleware.ts","./src/assets/index.ts","./src/client/client-auth.ts","./src/client/client-data.ts","./src/client/client-id.ts","./src/client/client-info.ts","./src/client/client-manager.ts","./src/client/client-store.ts","./src/client/client-utils.ts","./src/client/client.ts","./src/device/device-data.ts","./src/device/device-id.ts","./src/device/device-manager.ts","./src/device/device-store.ts","./src/device/session-id.ts","./src/dpop/dpop-manager.ts","./src/dpop/dpop-nonce.ts","./src/errors/access-denied-error.ts","./src/errors/account-selection-required-error.ts","./src/errors/consent-required-error.ts","./src/errors/handle-unavailable-error.ts","./src/errors/invalid-authorization-details-error.ts","./src/errors/invalid-client-error.ts","./src/errors/invalid-client-id-error.ts","./src/errors/invalid-client-metadata-error.ts","./src/errors/invalid-dpop-key-binding-error.ts","./src/errors/invalid-dpop-proof-error.ts","./src/errors/invalid-grant-error.ts","./src/errors/invalid-invite-code-error.ts","./src/errors/invalid-parameters-error.ts","./src/errors/invalid-redirect-uri-error.ts","./src/errors/invalid-request-error.ts","./src/errors/invalid-scope-error.ts","./src/errors/invalid-token-error.ts","./src/errors/login-required-error.ts","./src/errors/oauth-error.ts","./src/errors/second-authentication-factor-required-error.ts","./src/errors/unauthorized-client-error.ts","./src/errors/use-dpop-nonce-error.ts","./src/errors/www-authenticate-error.ts","./src/lib/hcaptcha.ts","./src/lib/locale.ts","./src/lib/redis.ts","./src/lib/csp/index.ts","./src/lib/html/build-document.ts","./src/lib/html/escapers.ts","./src/lib/html/html.ts","./src/lib/html/index.ts","./src/lib/html/tags.ts","./src/lib/html/util.ts","./src/lib/http/accept.ts","./src/lib/http/context.ts","./src/lib/http/index.ts","./src/lib/http/method.ts","./src/lib/http/middleware.ts","./src/lib/http/parser.ts","./src/lib/http/path.ts","./src/lib/http/request.ts","./src/lib/http/response.ts","./src/lib/http/route.ts","./src/lib/http/router.ts","./src/lib/http/stream.ts","./src/lib/http/types.ts","./src/lib/http/url.ts","./src/lib/util/authorization-header.ts","./src/lib/util/cast.ts","./src/lib/util/crypto.ts","./src/lib/util/date.ts","./src/lib/util/function.ts","./src/lib/util/hostname.ts","./src/lib/util/redirect-uri.ts","./src/lib/util/time.ts","./src/lib/util/type.ts","./src/lib/util/well-known.ts","./src/metadata/build-metadata.ts","./src/oidc/sub.ts","./src/output/build-authorize-data.ts","./src/output/build-customization-data.ts","./src/output/build-error-payload.ts","./src/output/output-manager.ts","./src/output/send-authorize-redirect.ts","./src/output/send-web-page.ts","./src/replay/replay-manager.ts","./src/replay/replay-store-memory.ts","./src/replay/replay-store-redis.ts","./src/replay/replay-store.ts","./src/request/code.ts","./src/request/request-data.ts","./src/request/request-id.ts","./src/request/request-info.ts","./src/request/request-manager.ts","./src/request/request-store-memory.ts","./src/request/request-store-redis.ts","./src/request/request-store.ts","./src/request/request-uri.ts","./src/signer/signed-token-payload.ts","./src/signer/signer.ts","./src/token/refresh-token.ts","./src/token/token-claims.ts","./src/token/token-data.ts","./src/token/token-id.ts","./src/token/token-manager.ts","./src/token/token-store.ts","./src/token/verify-token-claims.ts"],"version":"5.6.3"}
1
+ {"root":["./src/constants.ts","./src/index.ts","./src/oauth-client.ts","./src/oauth-dpop.ts","./src/oauth-errors.ts","./src/oauth-hooks.ts","./src/oauth-provider.ts","./src/oauth-store.ts","./src/oauth-verifier.ts","./src/access-token/access-token-type.ts","./src/account/account-manager.ts","./src/account/account-store.ts","./src/account/account.ts","./src/account/sign-in-data.ts","./src/account/sign-up-input.ts","./src/assets/assets-middleware.ts","./src/client/client-auth.ts","./src/client/client-data.ts","./src/client/client-id.ts","./src/client/client-info.ts","./src/client/client-manager.ts","./src/client/client-store.ts","./src/client/client-utils.ts","./src/client/client.ts","./src/device/device-data.ts","./src/device/device-id.ts","./src/device/device-manager.ts","./src/device/device-store.ts","./src/device/session-id.ts","./src/dpop/dpop-manager.ts","./src/dpop/dpop-nonce.ts","./src/errors/access-denied-error.ts","./src/errors/account-selection-required-error.ts","./src/errors/consent-required-error.ts","./src/errors/handle-unavailable-error.ts","./src/errors/invalid-authorization-details-error.ts","./src/errors/invalid-client-error.ts","./src/errors/invalid-client-id-error.ts","./src/errors/invalid-client-metadata-error.ts","./src/errors/invalid-dpop-key-binding-error.ts","./src/errors/invalid-dpop-proof-error.ts","./src/errors/invalid-grant-error.ts","./src/errors/invalid-invite-code-error.ts","./src/errors/invalid-parameters-error.ts","./src/errors/invalid-redirect-uri-error.ts","./src/errors/invalid-request-error.ts","./src/errors/invalid-scope-error.ts","./src/errors/invalid-token-error.ts","./src/errors/login-required-error.ts","./src/errors/oauth-error.ts","./src/errors/second-authentication-factor-required-error.ts","./src/errors/unauthorized-client-error.ts","./src/errors/use-dpop-nonce-error.ts","./src/errors/www-authenticate-error.ts","./src/lib/hcaptcha.ts","./src/lib/locale.ts","./src/lib/redis.ts","./src/lib/csp/index.ts","./src/lib/html/build-document.ts","./src/lib/html/escapers.ts","./src/lib/html/html.ts","./src/lib/html/index.ts","./src/lib/html/tags.ts","./src/lib/html/util.ts","./src/lib/http/accept.ts","./src/lib/http/context.ts","./src/lib/http/index.ts","./src/lib/http/method.ts","./src/lib/http/middleware.ts","./src/lib/http/parser.ts","./src/lib/http/path.ts","./src/lib/http/request.ts","./src/lib/http/response.ts","./src/lib/http/route.ts","./src/lib/http/router.ts","./src/lib/http/security-headers.ts","./src/lib/http/stream.ts","./src/lib/http/types.ts","./src/lib/http/url.ts","./src/lib/util/authorization-header.ts","./src/lib/util/cast.ts","./src/lib/util/crypto.ts","./src/lib/util/date.ts","./src/lib/util/function.ts","./src/lib/util/hostname.ts","./src/lib/util/redirect-uri.ts","./src/lib/util/time.ts","./src/lib/util/type.ts","./src/lib/util/well-known.ts","./src/metadata/build-metadata.ts","./src/oidc/sub.ts","./src/output/backend-data.ts","./src/output/build-authorize-data.ts","./src/output/build-customization-data.ts","./src/output/build-error-data.ts","./src/output/build-error-payload.ts","./src/output/output-manager.ts","./src/output/send-authorize-redirect.ts","./src/output/send-web-page.ts","./src/replay/replay-manager.ts","./src/replay/replay-store-memory.ts","./src/replay/replay-store-redis.ts","./src/replay/replay-store.ts","./src/request/code.ts","./src/request/request-data.ts","./src/request/request-id.ts","./src/request/request-info.ts","./src/request/request-manager.ts","./src/request/request-store-memory.ts","./src/request/request-store-redis.ts","./src/request/request-store.ts","./src/request/request-uri.ts","./src/signer/signed-token-payload.ts","./src/signer/signer.ts","./src/token/refresh-token.ts","./src/token/token-claims.ts","./src/token/token-data.ts","./src/token/token-id.ts","./src/token/token-manager.ts","./src/token/token-store.ts","./src/token/verify-token-claims.ts"],"version":"5.6.3"}
package/tsconfig.json CHANGED
@@ -1,8 +1,4 @@
1
1
  {
2
2
  "include": [],
3
- "references": [
4
- { "path": "./tsconfig.frontend.json" },
5
- { "path": "./tsconfig.backend.json" },
6
- { "path": "./tsconfig.tools.json" }
7
- ]
3
+ "references": [{ "path": "./tsconfig.backend.json" }]
8
4
  }
package/.linguirc DELETED
@@ -1,57 +0,0 @@
1
- {
2
- "format": "po",
3
- "sourceLocale": "en",
4
- "locales": [
5
- "en",
6
- "an",
7
- "ast",
8
- "ca",
9
- "da",
10
- "de",
11
- "el",
12
- "en-GB",
13
- "es",
14
- "eu",
15
- "fi",
16
- "fr",
17
- "ga",
18
- "gl",
19
- "hi",
20
- "hu",
21
- "ia",
22
- "id",
23
- "it",
24
- "ja",
25
- "km",
26
- "ko",
27
- "ne",
28
- "nl",
29
- "pl",
30
- "pt-BR",
31
- "ro",
32
- "ru",
33
- "sv",
34
- "th",
35
- "tr",
36
- "uk",
37
- "vi",
38
- "zh-CN",
39
- "zh-HK",
40
- "zh-TW"
41
- ],
42
- "fallbackLocales": {
43
- "default": "en"
44
- },
45
- "catalogs": [
46
- {
47
- "path": "<rootDir>/src/assets/app/locales/{locale}/messages",
48
- "include": [
49
- "<rootDir>/src/assets/app"
50
- ],
51
- "exclude": [
52
- "**/dist/**",
53
- "**/node_modules/**"
54
- ]
55
- }
56
- ]
57
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"sign-up-data.d.ts","sourceRoot":"","sources":["../../src/account/sign-up-data.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;EAIlB,CAAA;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sign-up-data.js","sourceRoot":"","sources":["../../src/account/sign-up-data.ts"],"names":[],"mappings":";;;AACA,oDAAwD;AACxD,yDAA4D;AAE/C,QAAA,gBAAgB,GAAG,0CAAuB;KACpD,MAAM,CAAC;IACN,aAAa,EAAE,iCAAmB,CAAC,QAAQ,EAAE;CAC9C,CAAC;KACD,MAAM,EAAE,CAAA"}