@atproto/oauth-provider 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.linguirc +57 -0
- package/CHANGELOG.md +29 -0
- package/dist/account/account-manager.d.ts +17 -3
- package/dist/account/account-manager.d.ts.map +1 -1
- package/dist/account/account-manager.js +102 -8
- package/dist/account/account-manager.js.map +1 -1
- package/dist/account/account-store.d.ts +81 -15
- package/dist/account/account-store.d.ts.map +1 -1
- package/dist/account/account-store.js +70 -19
- package/dist/account/account-store.js.map +1 -1
- package/dist/account/sign-in-data.d.ts +28 -0
- package/dist/account/sign-in-data.d.ts.map +1 -0
- package/dist/account/sign-in-data.js +16 -0
- package/dist/account/sign-in-data.js.map +1 -0
- package/dist/account/sign-up-data.d.ts +26 -0
- package/dist/account/sign-up-data.d.ts.map +1 -0
- package/dist/account/sign-up-data.js +11 -0
- package/dist/account/sign-up-data.js.map +1 -0
- package/dist/assets/app/bundle-manifest.json +598 -6
- package/dist/assets/app/index-ItwwtJ8r.js +36 -0
- package/dist/assets/app/index-ItwwtJ8r.js.map +1 -0
- package/dist/assets/app/main-B_dNxQo_.js +4 -0
- package/dist/assets/app/main-B_dNxQo_.js.map +1 -0
- package/dist/assets/app/main-CSatvmRR.css +3 -0
- package/dist/assets/app/main-CSatvmRR.js +306 -0
- package/dist/assets/app/main-CSatvmRR.js.map +1 -0
- package/dist/assets/app/messages-BQeltXSF.js +4 -0
- package/dist/assets/app/messages-BQeltXSF.js.map +1 -0
- package/dist/assets/app/messages-BQkEhfjg.js +4 -0
- package/dist/assets/app/messages-BQkEhfjg.js.map +1 -0
- package/dist/assets/app/messages-BUjKj_UJ.js +4 -0
- package/dist/assets/app/messages-BUjKj_UJ.js.map +1 -0
- package/dist/assets/app/messages-BWIQa8fO.js +4 -0
- package/dist/assets/app/messages-BWIQa8fO.js.map +1 -0
- package/dist/assets/app/messages-BaNVb0bp.js +4 -0
- package/dist/assets/app/messages-BaNVb0bp.js.map +1 -0
- package/dist/assets/app/messages-BaizVXcF.js +4 -0
- package/dist/assets/app/messages-BaizVXcF.js.map +1 -0
- package/dist/assets/app/messages-BfoClA1Y.js +4 -0
- package/dist/assets/app/messages-BfoClA1Y.js.map +1 -0
- package/dist/assets/app/messages-BsKGDZnC.js +4 -0
- package/dist/assets/app/messages-BsKGDZnC.js.map +1 -0
- package/dist/assets/app/messages-Bu-TJhml.js +4 -0
- package/dist/assets/app/messages-Bu-TJhml.js.map +1 -0
- package/dist/assets/app/messages-BvOKnBQk.js +4 -0
- package/dist/assets/app/messages-BvOKnBQk.js.map +1 -0
- package/dist/assets/app/messages-BxDzCiWz.js +4 -0
- package/dist/assets/app/messages-BxDzCiWz.js.map +1 -0
- package/dist/assets/app/messages-CDgFOy4S.js +4 -0
- package/dist/assets/app/messages-CDgFOy4S.js.map +1 -0
- package/dist/assets/app/messages-CLbTz0o9.js +4 -0
- package/dist/assets/app/messages-CLbTz0o9.js.map +1 -0
- package/dist/assets/app/messages-CNwSh0t7.js +4 -0
- package/dist/assets/app/messages-CNwSh0t7.js.map +1 -0
- package/dist/assets/app/messages-CSMNJ6P8.js +4 -0
- package/dist/assets/app/messages-CSMNJ6P8.js.map +1 -0
- package/dist/assets/app/messages-CZQUw3mp.js +4 -0
- package/dist/assets/app/messages-CZQUw3mp.js.map +1 -0
- package/dist/assets/app/messages-CZT41oVp.js +4 -0
- package/dist/assets/app/messages-CZT41oVp.js.map +1 -0
- package/dist/assets/app/messages-C_b-d3t8.js +4 -0
- package/dist/assets/app/messages-C_b-d3t8.js.map +1 -0
- package/dist/assets/app/messages-C_u3MTc2.js +4 -0
- package/dist/assets/app/messages-C_u3MTc2.js.map +1 -0
- package/dist/assets/app/messages-Cn8nHZic.js +4 -0
- package/dist/assets/app/messages-Cn8nHZic.js.map +1 -0
- package/dist/assets/app/messages-CtDywJUm.js +4 -0
- package/dist/assets/app/messages-CtDywJUm.js.map +1 -0
- package/dist/assets/app/messages-CurtIjBF.js +4 -0
- package/dist/assets/app/messages-CurtIjBF.js.map +1 -0
- package/dist/assets/app/messages-Cv6zIbaP.js +4 -0
- package/dist/assets/app/messages-Cv6zIbaP.js.map +1 -0
- package/dist/assets/app/messages-D1eLQuPE.js +4 -0
- package/dist/assets/app/messages-D1eLQuPE.js.map +1 -0
- package/dist/assets/app/messages-D8vHEaYW.js +4 -0
- package/dist/assets/app/messages-D8vHEaYW.js.map +1 -0
- package/dist/assets/app/messages-DJ1Q4GeC.js +4 -0
- package/dist/assets/app/messages-DJ1Q4GeC.js.map +1 -0
- package/dist/assets/app/messages-DRL3exqd.js +4 -0
- package/dist/assets/app/messages-DRL3exqd.js.map +1 -0
- package/dist/assets/app/messages-DWLPQRTp.js +4 -0
- package/dist/assets/app/messages-DWLPQRTp.js.map +1 -0
- package/dist/assets/app/messages-DjVaE9YE.js +4 -0
- package/dist/assets/app/messages-DjVaE9YE.js.map +1 -0
- package/dist/assets/app/messages-DqpMfFJR.js +4 -0
- package/dist/assets/app/messages-DqpMfFJR.js.map +1 -0
- package/dist/assets/app/messages-ETjhJBEN.js +4 -0
- package/dist/assets/app/messages-ETjhJBEN.js.map +1 -0
- package/dist/assets/app/messages-EUKrgrGn.js +4 -0
- package/dist/assets/app/messages-EUKrgrGn.js.map +1 -0
- package/dist/assets/app/messages-QQrOUcPW.js +4 -0
- package/dist/assets/app/messages-QQrOUcPW.js.map +1 -0
- package/dist/assets/app/messages-e2QGqFL6.js +4 -0
- package/dist/assets/app/messages-e2QGqFL6.js.map +1 -0
- package/dist/assets/app/messages-p61py7gD.js +4 -0
- package/dist/assets/app/messages-p61py7gD.js.map +1 -0
- package/dist/assets/asset.d.ts +1 -0
- package/dist/assets/asset.d.ts.map +1 -1
- package/dist/assets/assets-middleware.d.ts.map +1 -1
- package/dist/assets/assets-middleware.js +12 -7
- package/dist/assets/assets-middleware.js.map +1 -1
- package/dist/assets/index.d.ts +3 -2
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +13 -1
- package/dist/assets/index.js.map +1 -1
- package/dist/client/client-store.d.ts +3 -3
- package/dist/client/client-store.d.ts.map +1 -1
- package/dist/client/client-store.js +6 -5
- package/dist/client/client-store.js.map +1 -1
- package/dist/device/device-manager.d.ts +9 -8
- package/dist/device/device-manager.d.ts.map +1 -1
- package/dist/device/device-manager.js.map +1 -1
- package/dist/device/device-store.d.ts +3 -3
- package/dist/device/device-store.d.ts.map +1 -1
- package/dist/device/device-store.js +10 -9
- package/dist/device/device-store.js.map +1 -1
- package/dist/dpop/dpop-manager.d.ts +15 -7
- package/dist/dpop/dpop-manager.d.ts.map +1 -1
- package/dist/dpop/dpop-manager.js +17 -3
- package/dist/dpop/dpop-manager.js.map +1 -1
- package/dist/dpop/dpop-nonce.d.ts +11 -5
- package/dist/dpop/dpop-nonce.d.ts.map +1 -1
- package/dist/dpop/dpop-nonce.js +47 -38
- package/dist/dpop/dpop-nonce.js.map +1 -1
- package/dist/errors/handle-unavailable-error.d.ts +11 -0
- package/dist/errors/handle-unavailable-error.d.ts.map +1 -0
- package/dist/errors/handle-unavailable-error.js +19 -0
- package/dist/errors/handle-unavailable-error.js.map +1 -0
- package/dist/errors/invalid-request-error.d.ts +6 -8
- package/dist/errors/invalid-request-error.d.ts.map +1 -1
- package/dist/errors/invalid-request-error.js +10 -8
- package/dist/errors/invalid-request-error.js.map +1 -1
- package/dist/lib/csp/index.d.ts +18 -0
- package/dist/lib/csp/index.d.ts.map +1 -0
- package/dist/lib/csp/index.js +72 -0
- package/dist/lib/csp/index.js.map +1 -0
- package/dist/lib/hcaptcha.d.ts +177 -0
- package/dist/lib/hcaptcha.d.ts.map +1 -0
- package/dist/lib/hcaptcha.js +155 -0
- package/dist/lib/hcaptcha.js.map +1 -0
- package/dist/lib/html/build-document.d.ts +11 -3
- package/dist/lib/html/build-document.d.ts.map +1 -1
- package/dist/lib/html/build-document.js +51 -15
- package/dist/lib/html/build-document.js.map +1 -1
- package/dist/lib/http/middleware.d.ts.map +1 -1
- package/dist/lib/http/middleware.js +4 -1
- package/dist/lib/http/middleware.js.map +1 -1
- package/dist/lib/http/request.d.ts +5 -2
- package/dist/lib/http/request.d.ts.map +1 -1
- package/dist/lib/http/request.js +16 -1
- package/dist/lib/http/request.js.map +1 -1
- package/dist/lib/http/response.d.ts +4 -2
- package/dist/lib/http/response.d.ts.map +1 -1
- package/dist/lib/http/response.js +23 -5
- package/dist/lib/http/response.js.map +1 -1
- package/dist/lib/locale.d.ts +15 -0
- package/dist/lib/locale.d.ts.map +1 -0
- package/dist/lib/locale.js +17 -0
- package/dist/lib/locale.js.map +1 -0
- package/dist/lib/util/function.d.ts +2 -2
- package/dist/lib/util/function.d.ts.map +1 -1
- package/dist/lib/util/function.js.map +1 -1
- package/dist/lib/util/type.d.ts +88 -1
- package/dist/lib/util/type.d.ts.map +1 -1
- package/dist/lib/util/type.js +41 -0
- package/dist/lib/util/type.js.map +1 -1
- package/dist/metadata/build-metadata.d.ts +2 -2
- package/dist/metadata/build-metadata.d.ts.map +1 -1
- package/dist/metadata/build-metadata.js.map +1 -1
- package/dist/oauth-errors.d.ts +1 -0
- package/dist/oauth-errors.d.ts.map +1 -1
- package/dist/oauth-errors.js +3 -1
- package/dist/oauth-errors.js.map +1 -1
- package/dist/oauth-hooks.d.ts +60 -3
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-hooks.js +3 -3
- package/dist/oauth-hooks.js.map +1 -1
- package/dist/oauth-provider.d.ts +23 -18
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +207 -204
- package/dist/oauth-provider.js.map +1 -1
- package/dist/oauth-verifier.d.ts +1 -1
- package/dist/oauth-verifier.d.ts.map +1 -1
- package/dist/oauth-verifier.js +2 -1
- package/dist/oauth-verifier.js.map +1 -1
- package/dist/output/build-authorize-data.d.ts +0 -1
- package/dist/output/build-authorize-data.d.ts.map +1 -1
- package/dist/output/build-authorize-data.js +0 -1
- package/dist/output/build-authorize-data.js.map +1 -1
- package/dist/output/build-customization-data.d.ts +241 -0
- package/dist/output/build-customization-data.d.ts.map +1 -0
- package/dist/output/build-customization-data.js +174 -0
- package/dist/output/build-customization-data.js.map +1 -0
- package/dist/output/output-manager.d.ts +16 -9
- package/dist/output/output-manager.d.ts.map +1 -1
- package/dist/output/output-manager.js +78 -42
- package/dist/output/output-manager.js.map +1 -1
- package/dist/output/send-authorize-redirect.d.ts +9 -6
- package/dist/output/send-authorize-redirect.d.ts.map +1 -1
- package/dist/output/send-authorize-redirect.js +20 -14
- package/dist/output/send-authorize-redirect.js.map +1 -1
- package/dist/output/send-web-page.d.ts +7 -2
- package/dist/output/send-web-page.d.ts.map +1 -1
- package/dist/output/send-web-page.js +37 -21
- package/dist/output/send-web-page.js.map +1 -1
- package/dist/request/request-manager.d.ts +1 -1
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +4 -4
- package/dist/request/request-manager.js.map +1 -1
- package/dist/request/request-store.d.ts +3 -3
- package/dist/request/request-store.d.ts.map +1 -1
- package/dist/request/request-store.js +11 -10
- package/dist/request/request-store.js.map +1 -1
- package/dist/token/token-store.d.ts +4 -4
- package/dist/token/token-store.d.ts.map +1 -1
- package/dist/token/token-store.js +13 -12
- package/dist/token/token-store.js.map +1 -1
- package/package.json +43 -20
- package/rollup.config.js +61 -17
- package/src/account/account-manager.ts +159 -8
- package/src/account/account-store.ts +127 -32
- package/src/account/sign-in-data.ts +15 -0
- package/src/account/sign-up-data.ts +11 -0
- package/src/assets/app/app.tsx +31 -16
- package/src/assets/app/backend-data.ts +15 -60
- package/src/assets/app/backend-types.ts +66 -0
- package/src/assets/app/components/forms/button-toggle-visibility.tsx +43 -0
- package/src/assets/app/components/forms/button.tsx +60 -0
- package/src/assets/app/components/forms/fieldset.tsx +55 -0
- package/src/assets/app/components/forms/form-card-async.tsx +103 -0
- package/src/assets/app/components/forms/form-card.tsx +49 -0
- package/src/assets/app/components/forms/input-checkbox.tsx +73 -0
- package/src/assets/app/components/forms/input-container.tsx +107 -0
- package/src/assets/app/components/forms/input-email-address.tsx +66 -0
- package/src/assets/app/components/forms/input-new-password.tsx +62 -0
- package/src/assets/app/components/forms/input-password.tsx +88 -0
- package/src/assets/app/components/forms/input-text.tsx +76 -0
- package/src/assets/app/components/forms/input-token.tsx +94 -0
- package/src/assets/app/components/forms/wizard-card.tsx +116 -0
- package/src/assets/app/components/layouts/layout-title-page.tsx +77 -0
- package/src/assets/app/components/layouts/layout-welcome.tsx +73 -0
- package/src/assets/app/components/utils/account-identifier.tsx +23 -0
- package/src/assets/app/components/utils/account-image.tsx +33 -0
- package/src/assets/app/components/utils/admonition.tsx +52 -0
- package/src/assets/app/components/utils/client-name.tsx +45 -0
- package/src/assets/app/components/utils/error-card.tsx +93 -0
- package/src/assets/app/components/utils/error-message.tsx +62 -0
- package/src/assets/app/components/utils/help-card.tsx +46 -0
- package/src/assets/app/components/utils/icons.tsx +88 -0
- package/src/assets/app/components/utils/link-anchor.tsx +28 -0
- package/src/assets/app/components/utils/link-title.tsx +26 -0
- package/src/assets/app/components/utils/multi-lang-string.tsx +56 -0
- package/src/assets/app/components/utils/password-strength-label.tsx +37 -0
- package/src/assets/app/components/utils/password-strength-meter.tsx +58 -0
- package/src/assets/app/components/{url-viewer.tsx → utils/url-viewer.tsx} +9 -6
- package/src/assets/app/hooks/use-api.ts +128 -55
- package/src/assets/app/hooks/use-async-action.ts +120 -0
- package/src/assets/app/hooks/use-browser-color-scheme.ts +31 -0
- package/src/assets/app/hooks/use-csrf-token.ts +1 -1
- package/src/assets/app/hooks/use-random-string.ts +37 -0
- package/src/assets/app/hooks/use-stepper.ts +87 -0
- package/src/assets/app/index.html +182 -0
- package/src/assets/app/lib/api.ts +248 -79
- package/src/assets/app/lib/clsx.ts +5 -8
- package/src/assets/app/lib/json-client.ts +94 -0
- package/src/assets/app/lib/password.ts +98 -0
- package/src/assets/app/lib/ref.ts +17 -0
- package/src/assets/app/locales/an/messages.po +492 -0
- package/src/assets/app/locales/ast/messages.po +492 -0
- package/src/assets/app/locales/ca/messages.po +492 -0
- package/src/assets/app/locales/da/messages.po +492 -0
- package/src/assets/app/locales/de/messages.po +492 -0
- package/src/assets/app/locales/el/messages.po +492 -0
- package/src/assets/app/locales/en/messages.po +492 -0
- package/src/assets/app/locales/en-GB/messages.po +492 -0
- package/src/assets/app/locales/es/messages.po +492 -0
- package/src/assets/app/locales/eu/messages.po +492 -0
- package/src/assets/app/locales/fi/messages.po +492 -0
- package/src/assets/app/locales/fr/messages.po +492 -0
- package/src/assets/app/locales/ga/messages.po +492 -0
- package/src/assets/app/locales/gl/messages.po +492 -0
- package/src/assets/app/locales/hi/messages.po +492 -0
- package/src/assets/app/locales/hu/messages.po +492 -0
- package/src/assets/app/locales/ia/messages.po +492 -0
- package/src/assets/app/locales/id/messages.po +492 -0
- package/src/assets/app/locales/it/messages.po +492 -0
- package/src/assets/app/locales/ja/messages.po +492 -0
- package/src/assets/app/locales/km/messages.po +492 -0
- package/src/assets/app/locales/ko/messages.po +492 -0
- package/src/assets/app/locales/load.ts +8 -0
- package/src/assets/app/locales/locale-context.ts +19 -0
- package/src/assets/app/locales/locale-provider.tsx +112 -0
- package/src/assets/app/locales/locale-selector.tsx +58 -0
- package/src/assets/app/locales/locales.ts +168 -0
- package/src/assets/app/locales/ne/messages.po +492 -0
- package/src/assets/app/locales/nl/messages.po +492 -0
- package/src/assets/app/locales/pl/messages.po +492 -0
- package/src/assets/app/locales/pt-BR/messages.po +492 -0
- package/src/assets/app/locales/ro/messages.po +492 -0
- package/src/assets/app/locales/ru/messages.po +492 -0
- package/src/assets/app/locales/sv/messages.po +492 -0
- package/src/assets/app/locales/th/messages.po +492 -0
- package/src/assets/app/locales/tr/messages.po +492 -0
- package/src/assets/app/locales/uk/messages.po +492 -0
- package/src/assets/app/locales/vi/messages.po +492 -0
- package/src/assets/app/locales/zh-CN/messages.po +492 -0
- package/src/assets/app/locales/zh-HK/messages.po +492 -0
- package/src/assets/app/locales/zh-TW/messages.po +492 -0
- package/src/assets/app/main.css +23 -2
- package/src/assets/app/main.tsx +24 -8
- package/src/assets/app/views/authorize/accept/accept-form.tsx +150 -0
- package/src/assets/app/views/authorize/accept/accept-view.tsx +70 -0
- package/src/assets/app/views/authorize/authorize-view.tsx +180 -0
- package/src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx +88 -0
- package/src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx +80 -0
- package/src/assets/app/views/authorize/reset-password/reset-password-view.tsx +127 -0
- package/src/assets/app/views/authorize/sign-in/sign-in-form.tsx +244 -0
- package/src/assets/app/views/authorize/sign-in/sign-in-picker.tsx +116 -0
- package/src/assets/app/views/authorize/sign-in/sign-in-view.tsx +145 -0
- package/src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx +140 -0
- package/src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx +51 -0
- package/src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx +289 -0
- package/src/assets/app/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +108 -0
- package/src/assets/app/views/authorize/sign-up/sign-up-view.tsx +158 -0
- package/src/assets/app/views/authorize/welcome/welcome-view.tsx +56 -0
- package/src/assets/app/views/error/error-view.tsx +31 -0
- package/src/assets/asset.ts +1 -0
- package/src/assets/assets-middleware.ts +13 -8
- package/src/assets/index.ts +15 -2
- package/src/client/client-store.ts +10 -12
- package/src/device/device-manager.ts +8 -12
- package/src/device/device-store.ts +9 -15
- package/src/dpop/dpop-manager.ts +20 -8
- package/src/dpop/dpop-nonce.ts +58 -40
- package/src/errors/handle-unavailable-error.ts +18 -0
- package/src/errors/invalid-request-error.ts +10 -8
- package/src/lib/csp/index.ts +98 -0
- package/src/lib/hcaptcha.ts +182 -0
- package/src/lib/html/build-document.ts +60 -16
- package/src/lib/http/middleware.ts +4 -3
- package/src/lib/http/request.ts +31 -1
- package/src/lib/http/response.ts +22 -9
- package/src/lib/locale.ts +21 -0
- package/src/lib/util/function.ts +0 -3
- package/src/lib/util/type.ts +130 -1
- package/src/metadata/build-metadata.ts +2 -1
- package/src/oauth-errors.ts +1 -0
- package/src/oauth-hooks.ts +69 -3
- package/src/oauth-provider.ts +403 -307
- package/src/oauth-verifier.ts +3 -1
- package/src/output/build-authorize-data.ts +1 -3
- package/src/output/build-customization-data.ts +228 -0
- package/src/output/output-manager.ts +111 -48
- package/src/output/send-authorize-redirect.ts +43 -36
- package/src/output/send-web-page.ts +40 -26
- package/src/request/request-manager.ts +4 -4
- package/src/request/request-store.ts +12 -16
- package/src/token/token-store.ts +14 -18
- package/tailwind.config.js +5 -0
- package/tsconfig.backend.tsbuildinfo +1 -1
- package/tsconfig.frontend.tsbuildinfo +1 -1
- package/tsconfig.tools.tsbuildinfo +1 -1
- package/vite.config.mjs +16 -0
- package/.postcssrc.yml +0 -3
- package/dist/assets/app/main.css +0 -3
- package/dist/assets/app/main.js +0 -20
- package/dist/assets/app/main.js.map +0 -1
- package/dist/output/customization.d.ts +0 -27
- package/dist/output/customization.d.ts.map +0 -1
- package/dist/output/customization.js +0 -88
- package/dist/output/customization.js.map +0 -1
- package/src/assets/app/components/accept-form.tsx +0 -137
- package/src/assets/app/components/account-identifier.tsx +0 -18
- package/src/assets/app/components/account-picker.tsx +0 -127
- package/src/assets/app/components/button.tsx +0 -34
- package/src/assets/app/components/client-name.tsx +0 -37
- package/src/assets/app/components/fieldset.tsx +0 -26
- package/src/assets/app/components/form-card.tsx +0 -47
- package/src/assets/app/components/help-card.tsx +0 -42
- package/src/assets/app/components/icons/alert-icon.tsx +0 -5
- package/src/assets/app/components/icons/at-symbol-icon.tsx +0 -5
- package/src/assets/app/components/icons/caret-right-icon.tsx +0 -5
- package/src/assets/app/components/icons/lock-icon.tsx +0 -5
- package/src/assets/app/components/icons/token-icon.tsx +0 -5
- package/src/assets/app/components/icons/util.tsx +0 -17
- package/src/assets/app/components/info-card.tsx +0 -45
- package/src/assets/app/components/input-checkbox.tsx +0 -47
- package/src/assets/app/components/input-container.tsx +0 -37
- package/src/assets/app/components/input-layout.tsx +0 -47
- package/src/assets/app/components/input-text.tsx +0 -69
- package/src/assets/app/components/layout-title-page.tsx +0 -60
- package/src/assets/app/components/layout-welcome.tsx +0 -74
- package/src/assets/app/components/sign-in-form.tsx +0 -337
- package/src/assets/app/components/sign-up-account-form.tsx +0 -194
- package/src/assets/app/components/sign-up-disclaimer.tsx +0 -44
- package/src/assets/app/views/accept-view.tsx +0 -55
- package/src/assets/app/views/authorize-view.tsx +0 -106
- package/src/assets/app/views/error-view.tsx +0 -36
- package/src/assets/app/views/sign-in-view.tsx +0 -111
- package/src/assets/app/views/sign-up-view.tsx +0 -86
- package/src/assets/app/views/welcome-view.tsx +0 -54
- package/src/output/customization.ts +0 -118
@@ -1,31 +1,164 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
OAuthIssuerIdentifier,
|
3
|
+
isOAuthClientIdLoopback,
|
4
|
+
} from '@atproto/oauth-types'
|
2
5
|
import { Client } from '../client/client.js'
|
3
6
|
import { DeviceId } from '../device/device-id.js'
|
7
|
+
import { InvalidRequestError } from '../errors/invalid-request-error.js'
|
8
|
+
import { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js'
|
9
|
+
import { callAsync } from '../lib/util/function.js'
|
4
10
|
import { constantTime } from '../lib/util/time.js'
|
5
|
-
import {
|
11
|
+
import { OAuthHooks, RequestMetadata } from '../oauth-hooks.js'
|
12
|
+
import { Customization } from '../oauth-provider.js'
|
6
13
|
import { Sub } from '../oidc/sub.js'
|
7
14
|
import { ClientAuth } from '../token/token-store.js'
|
8
15
|
import {
|
9
16
|
Account,
|
10
17
|
AccountInfo,
|
11
18
|
AccountStore,
|
12
|
-
|
19
|
+
ResetPasswordConfirmData,
|
20
|
+
ResetPasswordRequestData,
|
13
21
|
} from './account-store.js'
|
22
|
+
import { SignInData } from './sign-in-data.js'
|
23
|
+
import { SignUpData } from './sign-up-data.js'
|
14
24
|
|
15
25
|
const TIMING_ATTACK_MITIGATION_DELAY = 400
|
26
|
+
const BRUTE_FORCE_MITIGATION_DELAY = 300
|
16
27
|
|
17
28
|
export class AccountManager {
|
18
|
-
|
29
|
+
protected readonly inviteCodeRequired: boolean
|
30
|
+
protected readonly hcaptchaClient?: HCaptchaClient
|
31
|
+
|
32
|
+
constructor(
|
33
|
+
issuer: OAuthIssuerIdentifier,
|
34
|
+
protected readonly store: AccountStore,
|
35
|
+
protected readonly hooks: OAuthHooks,
|
36
|
+
customization: Customization,
|
37
|
+
) {
|
38
|
+
this.inviteCodeRequired = customization.inviteCodeRequired !== false
|
39
|
+
this.hcaptchaClient = customization.hcaptcha
|
40
|
+
? new HCaptchaClient(new URL(issuer).hostname, customization.hcaptcha)
|
41
|
+
: undefined
|
42
|
+
}
|
43
|
+
|
44
|
+
protected async verifySignupData(
|
45
|
+
data: SignUpData,
|
46
|
+
deviceId: DeviceId,
|
47
|
+
deviceMetadata: RequestMetadata,
|
48
|
+
): Promise<void> {
|
49
|
+
let hcaptchaResult: undefined | HcaptchaVerifyResult
|
50
|
+
|
51
|
+
if (this.inviteCodeRequired && !data.inviteCode) {
|
52
|
+
throw new InvalidRequestError('Invite code is required')
|
53
|
+
}
|
54
|
+
|
55
|
+
if (this.hcaptchaClient) {
|
56
|
+
if (!data.hcaptchaToken) {
|
57
|
+
throw new InvalidRequestError('hCaptcha token is required')
|
58
|
+
}
|
59
|
+
|
60
|
+
const { allowed, result } = await this.hcaptchaClient.verify(
|
61
|
+
'signup',
|
62
|
+
data.hcaptchaToken,
|
63
|
+
deviceMetadata.ipAddress,
|
64
|
+
data.handle,
|
65
|
+
deviceMetadata.userAgent,
|
66
|
+
)
|
67
|
+
|
68
|
+
await callAsync(this.hooks.onSignupHcaptchaResult, {
|
69
|
+
data,
|
70
|
+
allowed,
|
71
|
+
result,
|
72
|
+
deviceId,
|
73
|
+
deviceMetadata,
|
74
|
+
})
|
75
|
+
|
76
|
+
if (!allowed) {
|
77
|
+
throw new InvalidRequestError('hCaptcha verification failed')
|
78
|
+
}
|
79
|
+
|
80
|
+
hcaptchaResult = result
|
81
|
+
}
|
82
|
+
|
83
|
+
await callAsync(this.hooks.onSignupAttempt, {
|
84
|
+
data,
|
85
|
+
deviceId,
|
86
|
+
deviceMetadata,
|
87
|
+
hcaptchaResult,
|
88
|
+
})
|
89
|
+
}
|
90
|
+
|
91
|
+
public async signUp(
|
92
|
+
data: SignUpData,
|
93
|
+
deviceId: DeviceId,
|
94
|
+
deviceMetadata: RequestMetadata,
|
95
|
+
): Promise<AccountInfo> {
|
96
|
+
await this.verifySignupData(data, deviceId, deviceMetadata)
|
97
|
+
|
98
|
+
// Mitigation against brute forcing email of users.
|
99
|
+
// @TODO Add rate limit to all the OAuth routes.
|
100
|
+
return constantTime(BRUTE_FORCE_MITIGATION_DELAY, async () => {
|
101
|
+
let account: Account
|
102
|
+
try {
|
103
|
+
account = await this.store.createAccount(data)
|
104
|
+
} catch (err) {
|
105
|
+
throw InvalidRequestError.from(err, 'Account creation failed')
|
106
|
+
}
|
107
|
+
|
108
|
+
try {
|
109
|
+
const info = await this.store.addDeviceAccount(
|
110
|
+
deviceId,
|
111
|
+
account.sub,
|
112
|
+
false,
|
113
|
+
)
|
114
|
+
|
115
|
+
await callAsync(this.hooks.onSignedUp, {
|
116
|
+
data,
|
117
|
+
info,
|
118
|
+
account,
|
119
|
+
deviceId,
|
120
|
+
deviceMetadata,
|
121
|
+
})
|
122
|
+
|
123
|
+
return { account, info }
|
124
|
+
} catch (err) {
|
125
|
+
throw InvalidRequestError.from(
|
126
|
+
err,
|
127
|
+
'Something went wrong, try singing-in',
|
128
|
+
)
|
129
|
+
}
|
130
|
+
})
|
131
|
+
}
|
19
132
|
|
20
133
|
public async signIn(
|
21
|
-
|
134
|
+
data: SignInData,
|
22
135
|
deviceId: DeviceId,
|
136
|
+
deviceMetadata: RequestMetadata,
|
23
137
|
): Promise<AccountInfo> {
|
24
138
|
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
25
|
-
|
26
|
-
|
139
|
+
try {
|
140
|
+
const account = await this.store.authenticateAccount(data)
|
141
|
+
const info = await this.store.addDeviceAccount(
|
142
|
+
deviceId,
|
143
|
+
account.sub,
|
144
|
+
data.remember,
|
145
|
+
)
|
27
146
|
|
28
|
-
|
147
|
+
await callAsync(this.hooks.onSignedIn, {
|
148
|
+
data,
|
149
|
+
info,
|
150
|
+
account,
|
151
|
+
deviceId,
|
152
|
+
deviceMetadata,
|
153
|
+
})
|
154
|
+
|
155
|
+
return { account, info }
|
156
|
+
} catch (err) {
|
157
|
+
throw InvalidRequestError.from(
|
158
|
+
err,
|
159
|
+
'Unable to sign-in due to an unexpected server error',
|
160
|
+
)
|
161
|
+
}
|
29
162
|
})
|
30
163
|
}
|
31
164
|
|
@@ -52,4 +185,22 @@ export class AccountManager {
|
|
52
185
|
const results = await this.store.listDeviceAccounts(deviceId)
|
53
186
|
return results.filter((result) => result.info.remembered)
|
54
187
|
}
|
188
|
+
|
189
|
+
public async resetPasswordRequest(data: ResetPasswordRequestData) {
|
190
|
+
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
191
|
+
await this.store.resetPasswordRequest(data)
|
192
|
+
})
|
193
|
+
}
|
194
|
+
|
195
|
+
public async resetPasswordConfirm(data: ResetPasswordConfirmData) {
|
196
|
+
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
197
|
+
await this.store.resetPasswordConfirm(data)
|
198
|
+
})
|
199
|
+
}
|
200
|
+
|
201
|
+
public async verifyHandleAvailability(handle: string): Promise<void> {
|
202
|
+
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
203
|
+
return this.store.verifyHandleAvailability(handle)
|
204
|
+
})
|
205
|
+
}
|
55
206
|
}
|
@@ -1,25 +1,86 @@
|
|
1
|
+
import { isEmailValid } from '@hapi/address'
|
2
|
+
import { isDisposableEmail } from 'disposable-email-domains-js'
|
1
3
|
import { z } from 'zod'
|
2
4
|
import { ClientId } from '../client/client-id.js'
|
3
5
|
import { DeviceId } from '../device/device-id.js'
|
4
|
-
import {
|
6
|
+
import { localeSchema } from '../lib/locale.js'
|
7
|
+
import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'
|
8
|
+
import {
|
9
|
+
HandleUnavailableError,
|
10
|
+
InvalidRequestError,
|
11
|
+
SecondAuthenticationFactorRequiredError,
|
12
|
+
} from '../oauth-errors.js'
|
5
13
|
import { Sub } from '../oidc/sub.js'
|
6
14
|
import { Account } from './account.js'
|
7
15
|
|
8
|
-
|
9
|
-
|
10
|
-
|
16
|
+
// @NOTE Change the length here to force stronger passwords (through a reset)
|
17
|
+
export const oldPasswordSchema = z.string().min(1)
|
18
|
+
export const newPasswordSchema = z.string().min(8)
|
19
|
+
export const tokenSchema = z.string().regex(/^[A-Z2-7]{5}-[A-Z2-7]{5}$/)
|
20
|
+
export const handleSchema = z
|
21
|
+
.string()
|
22
|
+
.min(3)
|
23
|
+
.max(30)
|
24
|
+
.regex(/^[a-z0-9][a-z0-9-]+[a-z0-9](?:\.[a-z0-9][a-z0-9-]+[a-z0-9])+$/)
|
25
|
+
export const emailSchema = z
|
26
|
+
.string()
|
27
|
+
.email()
|
28
|
+
// @NOTE using @hapi/address here, in addition to the email() check to ensure
|
29
|
+
// compatibility with the current email validation in the PDS's account
|
30
|
+
// manager
|
31
|
+
.refine(isEmailValid, {
|
32
|
+
message: 'Invalid email address',
|
33
|
+
})
|
34
|
+
.refine((email) => !isDisposableEmail(email), {
|
35
|
+
message: 'Disposable email addresses are not allowed',
|
36
|
+
})
|
11
37
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
38
|
+
export const authenticateAccountDataSchema = z
|
39
|
+
.object({
|
40
|
+
locale: localeSchema,
|
41
|
+
username: z.string(),
|
42
|
+
password: oldPasswordSchema,
|
43
|
+
emailOtp: z.string().optional(),
|
44
|
+
})
|
45
|
+
.strict()
|
46
|
+
|
47
|
+
export type AuthenticateAccountData = z.TypeOf<
|
48
|
+
typeof authenticateAccountDataSchema
|
49
|
+
>
|
50
|
+
|
51
|
+
export const createAccountDataSchema = z
|
52
|
+
.object({
|
53
|
+
locale: localeSchema,
|
54
|
+
handle: handleSchema,
|
55
|
+
email: emailSchema,
|
56
|
+
password: z.intersection(oldPasswordSchema, newPasswordSchema),
|
57
|
+
inviteCode: tokenSchema.optional(),
|
58
|
+
})
|
59
|
+
.strict()
|
60
|
+
|
61
|
+
export type CreateAccountData = z.TypeOf<typeof createAccountDataSchema>
|
62
|
+
|
63
|
+
export const resetPasswordRequestDataSchema = z
|
64
|
+
.object({
|
65
|
+
locale: localeSchema,
|
66
|
+
email: emailSchema,
|
67
|
+
})
|
68
|
+
.strict()
|
69
|
+
|
70
|
+
export type ResetPasswordRequestData = z.TypeOf<
|
71
|
+
typeof resetPasswordRequestDataSchema
|
72
|
+
>
|
18
73
|
|
19
|
-
|
20
|
-
|
74
|
+
export const resetPasswordConfirmDataSchema = z
|
75
|
+
.object({
|
76
|
+
token: tokenSchema,
|
77
|
+
password: z.intersection(oldPasswordSchema, newPasswordSchema),
|
78
|
+
})
|
79
|
+
.strict()
|
21
80
|
|
22
|
-
export type
|
81
|
+
export type ResetPasswordConfirmData = z.TypeOf<
|
82
|
+
typeof resetPasswordConfirmDataSchema
|
83
|
+
>
|
23
84
|
|
24
85
|
export type DeviceAccountInfo = {
|
25
86
|
remembered: boolean
|
@@ -28,7 +89,14 @@ export type DeviceAccountInfo = {
|
|
28
89
|
}
|
29
90
|
|
30
91
|
// Export all types needed to implement the AccountStore interface
|
31
|
-
export
|
92
|
+
export {
|
93
|
+
type Account,
|
94
|
+
type DeviceId,
|
95
|
+
HandleUnavailableError,
|
96
|
+
InvalidRequestError,
|
97
|
+
SecondAuthenticationFactorRequiredError,
|
98
|
+
type Sub,
|
99
|
+
}
|
32
100
|
|
33
101
|
export type AccountInfo = {
|
34
102
|
account: Account
|
@@ -36,10 +104,17 @@ export type AccountInfo = {
|
|
36
104
|
}
|
37
105
|
|
38
106
|
export interface AccountStore {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
107
|
+
/**
|
108
|
+
* @throws {HandleUnavailableError} - To indicate that the handle is already taken
|
109
|
+
* @throws {InvalidRequestError} - To indicate that some data is invalid
|
110
|
+
*/
|
111
|
+
createAccount(data: CreateAccountData): Awaitable<Account>
|
112
|
+
|
113
|
+
/**
|
114
|
+
* @throws {InvalidRequestError} - When the credentials are not valid
|
115
|
+
* @throws {SecondAuthenticationFactorRequiredError} - To indicate that an {@link SecondAuthenticationFactorRequiredError.type} is required in the credentials
|
116
|
+
*/
|
117
|
+
authenticateAccount(data: AuthenticateAccountData): Awaitable<Account>
|
43
118
|
|
44
119
|
addAuthorizedClient(
|
45
120
|
deviceId: DeviceId,
|
@@ -47,6 +122,19 @@ export interface AccountStore {
|
|
47
122
|
clientId: ClientId,
|
48
123
|
): Awaitable<void>
|
49
124
|
|
125
|
+
/**
|
126
|
+
* @param remember If false, the account must not be returned from
|
127
|
+
* {@link AccountStore.listDeviceAccounts}.
|
128
|
+
*/
|
129
|
+
addDeviceAccount(
|
130
|
+
deviceId: DeviceId,
|
131
|
+
sub: Sub,
|
132
|
+
remember: boolean,
|
133
|
+
): Awaitable<DeviceAccountInfo>
|
134
|
+
|
135
|
+
/**
|
136
|
+
* @returns The account info, whether the account, even if remember was false.
|
137
|
+
*/
|
50
138
|
getDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<AccountInfo | null>
|
51
139
|
removeDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<void>
|
52
140
|
|
@@ -55,23 +143,30 @@ export interface AccountStore {
|
|
55
143
|
* be returned. The others will be ignored.
|
56
144
|
*/
|
57
145
|
listDeviceAccounts(deviceId: DeviceId): Awaitable<AccountInfo[]>
|
58
|
-
}
|
59
146
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
typeof implementation.listDeviceAccounts === 'function' &&
|
68
|
-
typeof implementation.removeDeviceAccount === 'function'
|
69
|
-
)
|
147
|
+
resetPasswordRequest(data: ResetPasswordRequestData): Awaitable<void>
|
148
|
+
resetPasswordConfirm(data: ResetPasswordConfirmData): Awaitable<void>
|
149
|
+
|
150
|
+
/**
|
151
|
+
* @throws {HandleUnavailableError} - To indicate that the handle is already taken
|
152
|
+
*/
|
153
|
+
verifyHandleAvailability(handle: string): Awaitable<void>
|
70
154
|
}
|
71
155
|
|
72
|
-
export
|
73
|
-
|
74
|
-
|
156
|
+
export const isAccountStore = buildInterfaceChecker<AccountStore>([
|
157
|
+
'createAccount',
|
158
|
+
'authenticateAccount',
|
159
|
+
'addAuthorizedClient',
|
160
|
+
'addDeviceAccount',
|
161
|
+
'getDeviceAccount',
|
162
|
+
'removeDeviceAccount',
|
163
|
+
'listDeviceAccounts',
|
164
|
+
'resetPasswordRequest',
|
165
|
+
'resetPasswordConfirm',
|
166
|
+
'verifyHandleAvailability',
|
167
|
+
])
|
168
|
+
|
169
|
+
export function asAccountStore<V>(implementation: V): V & AccountStore {
|
75
170
|
if (!implementation || !isAccountStore(implementation)) {
|
76
171
|
throw new Error('Invalid AccountStore implementation')
|
77
172
|
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { z } from 'zod'
|
2
|
+
import { authenticateAccountDataSchema } from './account-store.js'
|
3
|
+
|
4
|
+
export const signInDataSchema = authenticateAccountDataSchema
|
5
|
+
.extend({
|
6
|
+
/**
|
7
|
+
* If false, the account must not be returned from
|
8
|
+
* {@link AccountStore.listDeviceAccounts}. Note that this only makes sense when
|
9
|
+
* used with a device ID.
|
10
|
+
*/
|
11
|
+
remember: z.boolean().optional().default(false),
|
12
|
+
})
|
13
|
+
.strict()
|
14
|
+
|
15
|
+
export type SignInData = z.TypeOf<typeof signInDataSchema>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { z } from 'zod'
|
2
|
+
import { hcaptchaTokenSchema } from '../lib/hcaptcha.js'
|
3
|
+
import { createAccountDataSchema } from './account-store.js'
|
4
|
+
|
5
|
+
export const signUpDataSchema = createAccountDataSchema
|
6
|
+
.extend({
|
7
|
+
hcaptchaToken: hcaptchaTokenSchema.optional(),
|
8
|
+
})
|
9
|
+
.strict()
|
10
|
+
|
11
|
+
export type SignUpData = z.TypeOf<typeof signUpDataSchema>
|
package/src/assets/app/app.tsx
CHANGED
@@ -1,28 +1,43 @@
|
|
1
|
+
import { ErrorBoundary } from 'react-error-boundary'
|
1
2
|
import type {
|
2
3
|
AuthorizeData,
|
4
|
+
AvailableLocales,
|
3
5
|
CustomizationData,
|
4
6
|
ErrorData,
|
5
|
-
} from './backend-
|
6
|
-
import {
|
7
|
-
import {
|
7
|
+
} from './backend-types.ts'
|
8
|
+
import { LocaleProvider } from './locales/locale-provider.tsx'
|
9
|
+
import { AuthorizeView } from './views/authorize/authorize-view.tsx'
|
10
|
+
import { ErrorView } from './views/error/error-view.tsx'
|
8
11
|
|
9
12
|
export type AppProps = {
|
13
|
+
availableLocales?: AvailableLocales
|
10
14
|
authorizeData?: AuthorizeData
|
11
15
|
customizationData?: CustomizationData
|
12
16
|
errorData?: ErrorData
|
13
17
|
}
|
14
18
|
|
15
|
-
export function App({
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
19
|
+
export function App({
|
20
|
+
availableLocales,
|
21
|
+
authorizeData,
|
22
|
+
customizationData,
|
23
|
+
errorData,
|
24
|
+
}: AppProps) {
|
25
|
+
return (
|
26
|
+
<LocaleProvider availableLocales={availableLocales}>
|
27
|
+
<ErrorBoundary
|
28
|
+
fallbackRender={({ error }) => (
|
29
|
+
<ErrorView error={error} customizationData={customizationData} />
|
30
|
+
)}
|
31
|
+
>
|
32
|
+
{errorData || !authorizeData ? (
|
33
|
+
<ErrorView error={errorData} customizationData={customizationData} />
|
34
|
+
) : (
|
35
|
+
<AuthorizeView
|
36
|
+
customizationData={customizationData}
|
37
|
+
authorizeData={authorizeData}
|
38
|
+
/>
|
39
|
+
)}
|
40
|
+
</ErrorBoundary>
|
41
|
+
</LocaleProvider>
|
42
|
+
)
|
28
43
|
}
|
@@ -1,72 +1,27 @@
|
|
1
|
-
import {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
email?: string
|
10
|
-
name?: string
|
11
|
-
preferred_username?: string
|
12
|
-
picture?: string
|
13
|
-
}
|
14
|
-
|
15
|
-
export type Session = {
|
16
|
-
account: Account
|
17
|
-
info?: never // Prevent relying on this in the frontend
|
18
|
-
|
19
|
-
selected: boolean
|
20
|
-
loginRequired: boolean
|
21
|
-
consentRequired: boolean
|
22
|
-
}
|
23
|
-
|
24
|
-
export type LinkDefinition = {
|
25
|
-
title: string
|
26
|
-
href: string
|
27
|
-
rel?: string
|
28
|
-
}
|
29
|
-
|
30
|
-
export type CustomizationData = {
|
31
|
-
name?: string
|
32
|
-
logo?: string
|
33
|
-
links?: LinkDefinition[]
|
34
|
-
}
|
35
|
-
|
36
|
-
export type ErrorData = {
|
37
|
-
error: string
|
38
|
-
error_description: string
|
39
|
-
}
|
40
|
-
|
41
|
-
export type ScopeDetail = {
|
42
|
-
scope: string
|
43
|
-
description?: string
|
44
|
-
}
|
45
|
-
|
46
|
-
export type AuthorizeData = {
|
47
|
-
clientId: string
|
48
|
-
clientMetadata: OAuthClientMetadata
|
49
|
-
clientTrusted: boolean
|
50
|
-
requestUri: string
|
51
|
-
csrfCookie: string
|
52
|
-
loginHint?: string
|
53
|
-
scopeDetails?: ScopeDetail[]
|
54
|
-
newSessionsRequireConsent: boolean
|
55
|
-
sessions: Session[]
|
56
|
-
}
|
57
|
-
|
58
|
-
// see "declareBackendData()" in the backend
|
59
|
-
const readBackendData = <T>(key: string): T | undefined => {
|
1
|
+
import {
|
2
|
+
AuthorizeData,
|
3
|
+
AvailableLocales,
|
4
|
+
CustomizationData,
|
5
|
+
ErrorData,
|
6
|
+
} from './backend-types.ts'
|
7
|
+
|
8
|
+
function readBackendData<T>(key: string): T | undefined {
|
60
9
|
const value = window[key] as T | undefined
|
61
10
|
delete window[key] // Prevent accidental usage / potential leaks to dependencies
|
62
11
|
return value
|
63
12
|
}
|
64
13
|
|
65
14
|
// These values are injected by the backend when it builds the
|
66
|
-
// page HTML.
|
15
|
+
// page HTML. See "declareBackendData()" in the backend.
|
67
16
|
|
17
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
18
|
+
export const availableLocales =
|
19
|
+
readBackendData<AvailableLocales>('__availableLocales')
|
20
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
68
21
|
export const customizationData = readBackendData<CustomizationData>(
|
69
22
|
'__customizationData',
|
70
23
|
)
|
24
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
71
25
|
export const errorData = readBackendData<ErrorData>('__errorData')
|
26
|
+
/** @deprecated Do not import directly. Only import this from main.tsx */
|
72
27
|
export const authorizeData = readBackendData<AuthorizeData>('__authorizeData')
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import type { OAuthClientMetadata } from '@atproto/oauth-types'
|
2
|
+
|
3
|
+
// @TODO: Find a way to share these types with the backend code
|
4
|
+
|
5
|
+
export type Account = {
|
6
|
+
sub: string
|
7
|
+
aud: string | [string, ...string[]]
|
8
|
+
|
9
|
+
email?: string
|
10
|
+
email_verified?: boolean
|
11
|
+
name?: string
|
12
|
+
preferred_username?: string
|
13
|
+
picture?: string
|
14
|
+
}
|
15
|
+
|
16
|
+
export type Session = {
|
17
|
+
account: Account
|
18
|
+
info?: never // Prevent relying on this in the frontend
|
19
|
+
|
20
|
+
selected: boolean
|
21
|
+
loginRequired: boolean
|
22
|
+
consentRequired: boolean
|
23
|
+
}
|
24
|
+
|
25
|
+
export type LocalizedString = string | ({ en: string } & Record<string, string>)
|
26
|
+
|
27
|
+
export type AvailableLocales = readonly string[]
|
28
|
+
|
29
|
+
export type LinkDefinition = {
|
30
|
+
title: LocalizedString
|
31
|
+
href: string
|
32
|
+
rel?: string
|
33
|
+
}
|
34
|
+
|
35
|
+
export type CustomizationData = {
|
36
|
+
// Functional customization
|
37
|
+
hcaptchaSiteKey?: string
|
38
|
+
inviteCodeRequired?: boolean
|
39
|
+
availableUserDomains?: string[]
|
40
|
+
|
41
|
+
// Aesthetic customization
|
42
|
+
name?: string
|
43
|
+
logo?: string
|
44
|
+
links?: LinkDefinition[]
|
45
|
+
}
|
46
|
+
|
47
|
+
export type ErrorData = {
|
48
|
+
error: string
|
49
|
+
error_description: string
|
50
|
+
}
|
51
|
+
|
52
|
+
export type ScopeDetail = {
|
53
|
+
scope: string
|
54
|
+
description?: string
|
55
|
+
}
|
56
|
+
|
57
|
+
export type AuthorizeData = {
|
58
|
+
clientId: string
|
59
|
+
clientMetadata: OAuthClientMetadata
|
60
|
+
clientTrusted: boolean
|
61
|
+
requestUri: string
|
62
|
+
loginHint?: string
|
63
|
+
scopeDetails?: ScopeDetail[]
|
64
|
+
newSessionsRequireConsent: boolean
|
65
|
+
sessions: Session[]
|
66
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { useLingui } from '@lingui/react/macro'
|
2
|
+
import { Override } from '../../lib/util.ts'
|
3
|
+
import { EyeIcon, EyeSlashIcon } from '../utils/icons.tsx'
|
4
|
+
import { Button, ButtonProps } from './button.tsx'
|
5
|
+
|
6
|
+
export type ButtonToggleVisibilityProps = Override<
|
7
|
+
Omit<ButtonProps, 'aria-label' | 'square'>,
|
8
|
+
{
|
9
|
+
visible: boolean
|
10
|
+
toggleVisible: () => void
|
11
|
+
}
|
12
|
+
>
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Generic button to toggle visibility of an item (e.g. password).
|
16
|
+
*/
|
17
|
+
export function ButtonToggleVisibility({
|
18
|
+
visible,
|
19
|
+
toggleVisible,
|
20
|
+
|
21
|
+
// button
|
22
|
+
onClick,
|
23
|
+
...props
|
24
|
+
}: ButtonToggleVisibilityProps) {
|
25
|
+
const { t } = useLingui()
|
26
|
+
return (
|
27
|
+
<Button
|
28
|
+
{...props}
|
29
|
+
square
|
30
|
+
onClick={(event) => {
|
31
|
+
onClick?.(event)
|
32
|
+
if (!event.defaultPrevented) toggleVisible()
|
33
|
+
}}
|
34
|
+
aria-label={visible ? t`Hide` : t`Make visible`}
|
35
|
+
>
|
36
|
+
{visible ? (
|
37
|
+
<EyeIcon className="w-5" aria-hidden />
|
38
|
+
) : (
|
39
|
+
<EyeSlashIcon className="w-5" aria-hidden />
|
40
|
+
)}
|
41
|
+
</Button>
|
42
|
+
)
|
43
|
+
}
|