@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.
- package/.linguirc +57 -0
- package/CHANGELOG.md +21 -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 +232 -0
- package/dist/output/build-customization-data.d.ts.map +1 -0
- package/dist/output/build-customization-data.js +145 -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 +399 -307
- package/src/oauth-verifier.ts +3 -1
- package/src/output/build-authorize-data.ts +1 -3
- package/src/output/build-customization-data.ts +189 -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
@@ -0,0 +1,182 @@
|
|
1
|
+
import { createHash } from 'node:crypto'
|
2
|
+
import { z } from 'zod'
|
3
|
+
import {
|
4
|
+
Fetch,
|
5
|
+
FetchBound,
|
6
|
+
bindFetch,
|
7
|
+
fetchJsonProcessor,
|
8
|
+
fetchJsonZodProcessor,
|
9
|
+
fetchOkProcessor,
|
10
|
+
} from '@atproto-labs/fetch'
|
11
|
+
import { pipe } from '@atproto-labs/pipe'
|
12
|
+
|
13
|
+
export const hcaptchaTokenSchema = z.string().min(1)
|
14
|
+
export type HcaptchaToken = z.infer<typeof hcaptchaTokenSchema>
|
15
|
+
|
16
|
+
export const hcaptchaConfigSchema = z.object({
|
17
|
+
/**
|
18
|
+
* The hCaptcha site key to use for the sign-up form.
|
19
|
+
*/
|
20
|
+
siteKey: z.string().min(1),
|
21
|
+
/**
|
22
|
+
* The hCaptcha secret key to use for the sign-up form.
|
23
|
+
*/
|
24
|
+
secretKey: z.string().min(1),
|
25
|
+
/**
|
26
|
+
* A salt to use when hashing client tokens.
|
27
|
+
*/
|
28
|
+
tokenSalt: z.string().min(1),
|
29
|
+
/**
|
30
|
+
* The risk score over which the user is considered a threat and will be
|
31
|
+
* denied access. This will be ignored if the enterprise features are not
|
32
|
+
* available.
|
33
|
+
*/
|
34
|
+
scoreThreshold: z.number().optional(),
|
35
|
+
})
|
36
|
+
export type HcaptchaConfig = z.infer<typeof hcaptchaConfigSchema>
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @see {@link https://docs.hcaptcha.com/#verify-the-user-response-server-side hCaptcha API}
|
40
|
+
*/
|
41
|
+
export const hcaptchaVerifyResultSchema = z.object({
|
42
|
+
/**
|
43
|
+
* is the passcode valid, and does it meet security criteria you specified, e.g. sitekey?
|
44
|
+
*/
|
45
|
+
success: z.boolean(),
|
46
|
+
/**
|
47
|
+
* timestamp of the challenge (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
|
48
|
+
*/
|
49
|
+
challenge_ts: z.string(),
|
50
|
+
/**
|
51
|
+
* the hostname of the site where the challenge was passed
|
52
|
+
*/
|
53
|
+
hostname: z.string(),
|
54
|
+
/**
|
55
|
+
* optional: any error codes
|
56
|
+
*/
|
57
|
+
'error-codes': z.array(z.string()),
|
58
|
+
/**
|
59
|
+
* ENTERPRISE feature: a score denoting malicious activity. Value ranges from
|
60
|
+
* 0.0 (no risk) to 1.0 (confirmed threat).
|
61
|
+
*/
|
62
|
+
score: z.number().optional(),
|
63
|
+
/**
|
64
|
+
* ENTERPRISE feature: reason(s) for score.
|
65
|
+
*/
|
66
|
+
score_reason: z.array(z.string()).optional(),
|
67
|
+
/**
|
68
|
+
* sitekey of the request
|
69
|
+
*/
|
70
|
+
sitekey: z.string().optional(),
|
71
|
+
/**
|
72
|
+
* obj of form: {'ip_device': 1, .. etc}
|
73
|
+
*/
|
74
|
+
behavior_counts: z.record(z.unknown()).optional(),
|
75
|
+
/**
|
76
|
+
* how similar is this? (0.0 - 1.0, -1 on err)
|
77
|
+
*/
|
78
|
+
similarity: z.number().optional(),
|
79
|
+
/**
|
80
|
+
* count of similar_tokens not processed
|
81
|
+
*/
|
82
|
+
similarity_failures: z.number().optional(),
|
83
|
+
/**
|
84
|
+
* array of strings for any similarity errors
|
85
|
+
*/
|
86
|
+
similarity_error_details: z.array(z.string()).optional(),
|
87
|
+
/**
|
88
|
+
* encoded clientID
|
89
|
+
*/
|
90
|
+
scoped_uid_0: z.string().optional(),
|
91
|
+
/**
|
92
|
+
* encoded IP
|
93
|
+
*/
|
94
|
+
scoped_uid_1: z.string().optional(),
|
95
|
+
/**
|
96
|
+
* encoded IP (APT)
|
97
|
+
*/
|
98
|
+
scoped_uid_2: z.string().optional(),
|
99
|
+
/**
|
100
|
+
* Risk Insights (APT + RI)
|
101
|
+
*/
|
102
|
+
risk_insights: z.record(z.unknown()).optional(),
|
103
|
+
/**
|
104
|
+
* Advanced Threat Signatures (APT)
|
105
|
+
*/
|
106
|
+
sigs: z.record(z.unknown()).optional(),
|
107
|
+
/**
|
108
|
+
* tags added via Rules
|
109
|
+
*/
|
110
|
+
tags: z.array(z.string()).optional(),
|
111
|
+
})
|
112
|
+
|
113
|
+
export type HcaptchaVerifyResult = z.infer<typeof hcaptchaVerifyResultSchema>
|
114
|
+
|
115
|
+
const fetchSuccessHandler = pipe(
|
116
|
+
fetchOkProcessor(),
|
117
|
+
fetchJsonProcessor(),
|
118
|
+
fetchJsonZodProcessor(hcaptchaVerifyResultSchema),
|
119
|
+
)
|
120
|
+
|
121
|
+
export class HCaptchaClient {
|
122
|
+
protected readonly fetch: FetchBound
|
123
|
+
constructor(
|
124
|
+
private readonly hostname: string,
|
125
|
+
private readonly config: HcaptchaConfig,
|
126
|
+
fetch: Fetch = globalThis.fetch,
|
127
|
+
) {
|
128
|
+
this.fetch = bindFetch(fetch)
|
129
|
+
}
|
130
|
+
|
131
|
+
async verify(
|
132
|
+
behaviorType: 'login' | 'signup',
|
133
|
+
response: string,
|
134
|
+
remoteip: string,
|
135
|
+
handle: string,
|
136
|
+
userAgent?: string,
|
137
|
+
) {
|
138
|
+
const result = await this.fetch('https://api.hcaptcha.com/siteverify', {
|
139
|
+
method: 'POST',
|
140
|
+
headers: {
|
141
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
142
|
+
},
|
143
|
+
body: new URLSearchParams({
|
144
|
+
secret: this.config.secretKey,
|
145
|
+
sitekey: this.config.siteKey,
|
146
|
+
behavior_type: behaviorType,
|
147
|
+
response,
|
148
|
+
remoteip,
|
149
|
+
client_tokens: JSON.stringify({
|
150
|
+
hashedIp: this.hashToken(remoteip),
|
151
|
+
hashedHandle: this.hashToken(handle),
|
152
|
+
hashedUserAgent: userAgent ? this.hashToken(userAgent) : undefined,
|
153
|
+
}),
|
154
|
+
}).toString(),
|
155
|
+
}).then(fetchSuccessHandler)
|
156
|
+
|
157
|
+
return {
|
158
|
+
allowed: this.isAllowed(result),
|
159
|
+
result,
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
isAllowed({ success, hostname, score }: HcaptchaVerifyResult) {
|
164
|
+
return (
|
165
|
+
success &&
|
166
|
+
// Fool-proofing: If this is false, the user is trying to use a token
|
167
|
+
// generated for the same siteKey, but on another domain.
|
168
|
+
hostname === this.hostname &&
|
169
|
+
// Ignore if enterprise feature is not enabled
|
170
|
+
score != null &&
|
171
|
+
this.config.scoreThreshold != null &&
|
172
|
+
score < this.config.scoreThreshold
|
173
|
+
)
|
174
|
+
}
|
175
|
+
|
176
|
+
hashToken(value: string) {
|
177
|
+
const hash = createHash('sha256')
|
178
|
+
hash.update(this.config.tokenSalt)
|
179
|
+
hash.update(value)
|
180
|
+
return hash.digest().toString('base64')
|
181
|
+
}
|
182
|
+
}
|
@@ -8,7 +8,43 @@ export type AssetRef = {
|
|
8
8
|
}
|
9
9
|
|
10
10
|
export type Attrs = Record<string, boolean | string | undefined>
|
11
|
-
|
11
|
+
|
12
|
+
/**
|
13
|
+
* @see {@link https://developer.mozilla.org/fr/docs/Web/HTML/Attributes/rel}
|
14
|
+
*/
|
15
|
+
const ALLOWED_LINK_REL_VALUES = Object.freeze([
|
16
|
+
'alternate',
|
17
|
+
'author',
|
18
|
+
'canonical',
|
19
|
+
'dns-prefetch',
|
20
|
+
'external',
|
21
|
+
'expect',
|
22
|
+
'help',
|
23
|
+
'icon',
|
24
|
+
'license',
|
25
|
+
'manifest',
|
26
|
+
'me',
|
27
|
+
'modulepreload',
|
28
|
+
'next',
|
29
|
+
'pingback',
|
30
|
+
'preconnect',
|
31
|
+
'prefetch',
|
32
|
+
'preload',
|
33
|
+
'prerender',
|
34
|
+
'prev',
|
35
|
+
'privacy-policy',
|
36
|
+
'search',
|
37
|
+
'stylesheet',
|
38
|
+
'terms-of-service',
|
39
|
+
] as const)
|
40
|
+
export type LinkRel = (typeof ALLOWED_LINK_REL_VALUES)[number]
|
41
|
+
export const isLinkRel = (rel: unknown): rel is LinkRel =>
|
42
|
+
(ALLOWED_LINK_REL_VALUES as readonly unknown[]).includes(rel)
|
43
|
+
|
44
|
+
export type LinkAttrs = Attrs & {
|
45
|
+
href: string
|
46
|
+
rel: LinkRel
|
47
|
+
}
|
12
48
|
export type MetaAttrs =
|
13
49
|
| { name: string; content: string }
|
14
50
|
| { 'http-equiv': string; content: string }
|
@@ -27,7 +63,7 @@ export type BuildDocumentOptions = {
|
|
27
63
|
title?: HtmlValue
|
28
64
|
scripts?: readonly (Html | AssetRef)[]
|
29
65
|
styles?: readonly (Html | AssetRef)[]
|
30
|
-
body
|
66
|
+
body?: HtmlValue
|
31
67
|
bodyAttrs?: Attrs
|
32
68
|
}
|
33
69
|
|
@@ -50,12 +86,13 @@ export const buildDocument = ({
|
|
50
86
|
${base && html`<base href="${base.href}" />`}
|
51
87
|
${meta?.some(isViewportMeta) ? null : defaultViewport}
|
52
88
|
${meta?.map(metaToHtml)}
|
89
|
+
${styles?.map(linkPreload('style'))}
|
90
|
+
${scripts?.map(linkPreload('script'))}
|
53
91
|
${links?.map(linkToHtml)}
|
54
|
-
${head}
|
92
|
+
${head}
|
93
|
+
${styles?.map(styleToHtml)}
|
55
94
|
</head>
|
56
|
-
<body${attrsToHtml(bodyAttrs)}>
|
57
|
-
${body} ${scripts?.map(scriptToHtml)}
|
58
|
-
</body>
|
95
|
+
<body${attrsToHtml(bodyAttrs)}>${body}${scripts?.map(scriptToHtml)}</body>
|
59
96
|
</html>`
|
60
97
|
|
61
98
|
function isViewportMeta<T extends MetaAttrs>(
|
@@ -64,12 +101,12 @@ function isViewportMeta<T extends MetaAttrs>(
|
|
64
101
|
return 'name' in attrs && attrs.name === 'viewport'
|
65
102
|
}
|
66
103
|
|
67
|
-
function
|
68
|
-
|
104
|
+
function linkToHtml(attrs: LinkAttrs) {
|
105
|
+
return html`<link${attrsToHtml(attrs)} />`
|
69
106
|
}
|
70
107
|
|
71
|
-
function
|
72
|
-
|
108
|
+
function metaToHtml(attrs: MetaAttrs) {
|
109
|
+
return html`<meta${attrsToHtml(attrs)} />`
|
73
110
|
}
|
74
111
|
|
75
112
|
function* attrsToHtml(attrs?: Attrs) {
|
@@ -83,16 +120,23 @@ function* attrsToHtml(attrs?: Attrs) {
|
|
83
120
|
}
|
84
121
|
}
|
85
122
|
|
86
|
-
function
|
87
|
-
|
123
|
+
function linkPreload(as: 'script' | 'style') {
|
124
|
+
return (style: Html | AssetRef) =>
|
125
|
+
style instanceof Html
|
126
|
+
? undefined
|
127
|
+
: html`<link rel="preload" href="${style.url}" as="${as}" />`
|
128
|
+
}
|
129
|
+
|
130
|
+
function scriptToHtml(script: Html | AssetRef) {
|
131
|
+
return script instanceof Html
|
88
132
|
? // prettier-ignore
|
89
133
|
html`<script>${script}</script>` // hash validity requires no space around the content
|
90
|
-
: html`<script type="module" src="${script.url}
|
134
|
+
: html`<script type="module" src="${script.url}"></script>`
|
91
135
|
}
|
92
136
|
|
93
|
-
function
|
94
|
-
|
137
|
+
function styleToHtml(style: Html | AssetRef) {
|
138
|
+
return style instanceof Html
|
95
139
|
? // prettier-ignore
|
96
140
|
html`<style>${style}</style>` // hash validity requires no space around the content
|
97
|
-
: html`<link rel="stylesheet" href="${style.url}
|
141
|
+
: html`<link rel="stylesheet" href="${style.url}" />`
|
98
142
|
}
|
@@ -2,6 +2,8 @@ import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
2
2
|
import { writeJson } from './response.js'
|
3
3
|
import { Handler, Middleware, NextFunction } from './types.js'
|
4
4
|
|
5
|
+
const isNonNullable = <X>(x: X): x is NonNullable<X> => x != null
|
6
|
+
|
5
7
|
export function combineMiddlewares<M extends Middleware<any, any, any>>(
|
6
8
|
middlewares: Iterable<null | undefined | M>,
|
7
9
|
options?: { skipKeyword?: string },
|
@@ -15,12 +17,11 @@ export function combineMiddlewares(
|
|
15
17
|
middlewares: Iterable<null | undefined | Middleware<unknown>>,
|
16
18
|
{ skipKeyword }: { skipKeyword?: string } = {},
|
17
19
|
): Middleware<unknown> {
|
18
|
-
const middlewaresArray = Array.from(middlewares).filter(
|
19
|
-
(x): x is NonNullable<typeof x> => x != null,
|
20
|
-
)
|
20
|
+
const middlewaresArray = Array.from(middlewares).filter(isNonNullable)
|
21
21
|
|
22
22
|
// Optimization: if there are no middlewares, return a noop middleware.
|
23
23
|
if (middlewaresArray.length === 0) return (req, res, next) => void next()
|
24
|
+
if (middlewaresArray.length === 1) return middlewaresArray[0]
|
24
25
|
|
25
26
|
return function (req, res, next) {
|
26
27
|
let i = 0
|
package/src/lib/http/request.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { randomBytes } from 'node:crypto'
|
2
2
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
3
|
+
import { languages, mediaType } from '@hapi/accept'
|
3
4
|
import { parse as parseCookie, serialize as serializeCookie } from 'cookie'
|
4
5
|
import forwarded from 'forwarded'
|
5
6
|
import createHttpError from 'http-errors'
|
@@ -79,6 +80,18 @@ export function validateFetchSite(
|
|
79
80
|
validateHeaderValue(req, 'sec-fetch-site', expectedSite)
|
80
81
|
}
|
81
82
|
|
83
|
+
export function validateReferer(
|
84
|
+
req: IncomingMessage,
|
85
|
+
res: ServerResponse,
|
86
|
+
reference: UrlReference,
|
87
|
+
allowNull: true,
|
88
|
+
): URL | null
|
89
|
+
export function validateReferer(
|
90
|
+
req: IncomingMessage,
|
91
|
+
res: ServerResponse,
|
92
|
+
reference: UrlReference,
|
93
|
+
allowNull?: false,
|
94
|
+
): URL
|
82
95
|
export function validateReferer(
|
83
96
|
req: IncomingMessage,
|
84
97
|
res: ServerResponse,
|
@@ -90,6 +103,7 @@ export function validateReferer(
|
|
90
103
|
if (refererUrl ? !urlMatch(refererUrl, reference) : !allowNull) {
|
91
104
|
throw createHttpError(400, `Invalid referer ${referer}`)
|
92
105
|
}
|
106
|
+
return refererUrl
|
93
107
|
}
|
94
108
|
|
95
109
|
export async function setupCsrfToken(
|
@@ -126,12 +140,13 @@ export function validateSameOrigin(
|
|
126
140
|
export function validateCsrfToken(
|
127
141
|
req: IncomingMessage,
|
128
142
|
res: ServerResponse,
|
129
|
-
csrfToken:
|
143
|
+
csrfToken: unknown,
|
130
144
|
cookieName = 'csrf_token',
|
131
145
|
clearCookie = false,
|
132
146
|
) {
|
133
147
|
const cookies = parseHttpCookies(req)
|
134
148
|
if (
|
149
|
+
typeof csrfToken !== 'string' ||
|
135
150
|
!csrfToken ||
|
136
151
|
!cookies ||
|
137
152
|
!cookieName ||
|
@@ -248,3 +263,18 @@ function extractPort(req: IncomingMessage, ip: string): number {
|
|
248
263
|
|
249
264
|
throw new Error('Could not determine port')
|
250
265
|
}
|
266
|
+
|
267
|
+
export function extractLocales(req: IncomingMessage) {
|
268
|
+
const acceptLanguage = req.headers['accept-language']
|
269
|
+
return acceptLanguage ? languages(acceptLanguage) : []
|
270
|
+
}
|
271
|
+
|
272
|
+
export function negotiateResponseContent<T extends string>(
|
273
|
+
req: IncomingMessage,
|
274
|
+
types: readonly T[],
|
275
|
+
): T | undefined {
|
276
|
+
const type = mediaType(req.headers['accept'], types)
|
277
|
+
if (type) return type as T
|
278
|
+
|
279
|
+
return undefined
|
280
|
+
}
|
package/src/lib/http/response.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import type { ServerResponse } from 'node:http'
|
2
2
|
import { type Readable, pipeline } from 'node:stream'
|
3
|
-
import { Handler } from './types.js'
|
3
|
+
import type { Handler, Middleware } from './types.js'
|
4
4
|
|
5
5
|
export function appendHeader(
|
6
6
|
res: ServerResponse,
|
@@ -53,22 +53,27 @@ export function writeStream(
|
|
53
53
|
export function writeBuffer(
|
54
54
|
res: ServerResponse,
|
55
55
|
chunk: string | Buffer,
|
56
|
-
|
57
|
-
status = 200,
|
58
|
-
contentType = 'application/octet-stream',
|
59
|
-
}: WriteResponseOptions = {},
|
56
|
+
opts: WriteResponseOptions,
|
60
57
|
): void {
|
61
|
-
res.statusCode = status
|
62
|
-
res.setHeader('content-type', contentType)
|
58
|
+
if (opts?.status != null) res.statusCode = opts.status
|
59
|
+
res.setHeader('content-type', opts?.contentType || 'application/octet-stream')
|
63
60
|
res.end(chunk)
|
64
61
|
}
|
65
62
|
|
63
|
+
export function toJsonBuffer(value: unknown): Buffer {
|
64
|
+
try {
|
65
|
+
return Buffer.from(JSON.stringify(value))
|
66
|
+
} catch (cause) {
|
67
|
+
throw new Error(`Failed to serialize as JSON`, { cause })
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
66
71
|
export function writeJson(
|
67
72
|
res: ServerResponse,
|
68
73
|
payload: unknown,
|
69
74
|
{ contentType = 'application/json', ...options }: WriteResponseOptions = {},
|
70
75
|
): void {
|
71
|
-
const buffer =
|
76
|
+
const buffer = toJsonBuffer(payload)
|
72
77
|
writeBuffer(res, buffer, { ...options, contentType })
|
73
78
|
}
|
74
79
|
|
@@ -76,7 +81,7 @@ export function staticJsonMiddleware(
|
|
76
81
|
value: unknown,
|
77
82
|
{ contentType = 'application/json', ...options }: WriteResponseOptions = {},
|
78
83
|
): Handler<unknown> {
|
79
|
-
const buffer =
|
84
|
+
const buffer = toJsonBuffer(value)
|
80
85
|
const staticOptions: WriteResponseOptions = { ...options, contentType }
|
81
86
|
return function (req, res) {
|
82
87
|
writeBuffer(res, buffer, staticOptions)
|
@@ -90,3 +95,11 @@ export function writeHtml(
|
|
90
95
|
): void {
|
91
96
|
writeBuffer(res, html, { ...options, contentType })
|
92
97
|
}
|
98
|
+
|
99
|
+
export function cacheControlMiddleware(maxAge: number): Middleware<void> {
|
100
|
+
const header = `max-age=${maxAge}`
|
101
|
+
return function (req, res, next) {
|
102
|
+
res.setHeader('Cache-Control', header)
|
103
|
+
next()
|
104
|
+
}
|
105
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { z } from 'zod'
|
2
|
+
|
3
|
+
export const localeSchema = z
|
4
|
+
.string()
|
5
|
+
.regex(/^[a-z]{2,3}(-[A-Z]{2})?$/, 'Invalid locale')
|
6
|
+
export type Locale = z.infer<typeof localeSchema>
|
7
|
+
|
8
|
+
export const multiLangStringSchema = z.intersection(
|
9
|
+
z.object({ en: z.string() }), // en is required
|
10
|
+
z.record(localeSchema, z.union([z.string(), z.undefined()])),
|
11
|
+
)
|
12
|
+
export type MultiLangString = z.infer<typeof multiLangStringSchema>
|
13
|
+
|
14
|
+
export const AVAILABLE_LOCALES = [
|
15
|
+
// TODO: Add more in this list as translations are added in the PO files
|
16
|
+
'en',
|
17
|
+
'fr',
|
18
|
+
] as const satisfies readonly Locale[]
|
19
|
+
export type AvailableLocale = (typeof AVAILABLE_LOCALES)[number]
|
20
|
+
export const isAvailableLocale = (v: unknown): v is AvailableLocale =>
|
21
|
+
(AVAILABLE_LOCALES as readonly unknown[]).includes(v)
|
package/src/lib/util/function.ts
CHANGED
@@ -6,17 +6,14 @@
|
|
6
6
|
* particularly useful when the function is a member of a "private" object.
|
7
7
|
*/
|
8
8
|
export async function callAsync<F extends (...args: any[]) => unknown>(
|
9
|
-
this: ThisParameterType<F>,
|
10
9
|
fn: F,
|
11
10
|
...args: Parameters<F>
|
12
11
|
): Promise<Awaited<ReturnType<F>>>
|
13
12
|
export async function callAsync<F extends (...args: any[]) => unknown>(
|
14
|
-
this: ThisParameterType<F>,
|
15
13
|
fn?: F,
|
16
14
|
...args: Parameters<F>
|
17
15
|
): Promise<Awaited<ReturnType<F>> | undefined>
|
18
16
|
export async function callAsync<F extends (...args: any[]) => unknown>(
|
19
|
-
this: ThisParameterType<F>,
|
20
17
|
fn?: F,
|
21
18
|
...args: Parameters<F>
|
22
19
|
): Promise<Awaited<ReturnType<F>> | undefined> {
|
package/src/lib/util/type.ts
CHANGED
@@ -1,4 +1,133 @@
|
|
1
1
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
2
2
|
export type Simplify<T> = { [K in keyof T]: T[K] } & {}
|
3
|
-
export type Override<T, V> = Simplify<
|
3
|
+
export type Override<T, V> = Simplify<{
|
4
|
+
[K in keyof (V & T)]: K extends keyof V
|
5
|
+
? V[K]
|
6
|
+
: K extends keyof T
|
7
|
+
? T[K]
|
8
|
+
: never
|
9
|
+
}>
|
4
10
|
export type Awaitable<T> = T | Promise<T>
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Similar to {@link Required} but also ensures that all values are defined.
|
14
|
+
*/
|
15
|
+
export type RequiredDefined<T> = { [K in keyof T]-?: Exclude<T[K], undefined> }
|
16
|
+
|
17
|
+
// <hardcore-mode> (don't touch this)
|
18
|
+
|
19
|
+
/**
|
20
|
+
* @example
|
21
|
+
* ```ts
|
22
|
+
* type F = UnionToFnUnion<'a' | 'b'> // (() => 'a') | (() => 'b')
|
23
|
+
* ```
|
24
|
+
*/
|
25
|
+
type UnionToFnUnion<T> = T extends any ? () => T : never
|
26
|
+
|
27
|
+
/**
|
28
|
+
* @example
|
29
|
+
* ```ts
|
30
|
+
* type A = UnionToIntersection<(() => 'a') | (() => 'b')> // (() => 'a') & (() => 'b')
|
31
|
+
*
|
32
|
+
* UnionToIntersection<{ foo: string | number } | { foo: number; bar: 4 }> // { foo: number; bar: 4 }
|
33
|
+
* ```
|
34
|
+
*/
|
35
|
+
type UnionToIntersection<T> = (T extends any ? (x: T) => void : never) extends (
|
36
|
+
x: infer U,
|
37
|
+
) => void
|
38
|
+
? U
|
39
|
+
: never
|
40
|
+
|
41
|
+
/**
|
42
|
+
* @example
|
43
|
+
* ```ts
|
44
|
+
* type B = ExtractUnionItem<'a' | 'b'> // 'b'
|
45
|
+
* ```
|
46
|
+
*/
|
47
|
+
type ExtractUnionItem<T> =
|
48
|
+
// There exists a quirk in the way TypeScript works when inferring return
|
49
|
+
// types of an (disjoined) intersection of functions:
|
50
|
+
//
|
51
|
+
// type AnB = (() => 'a') & (() => 'b')
|
52
|
+
// type B = AnB extends () => infer R ? R : never // 'b'
|
53
|
+
//
|
54
|
+
// By turning the input union T (e.g. 'a' | 'b') into a union of function
|
55
|
+
// (() => 'a') | (() => 'b') and then into an intersection of those functions
|
56
|
+
// (() => 'a') & (() => 'b'), we can exploit the special TypeScript behavior
|
57
|
+
// to infer only the last return type from the functions, which is effectively
|
58
|
+
// equal to the last item of the input union T.
|
59
|
+
UnionToIntersection<UnionToFnUnion<T>> extends () => infer R ? R : never
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Utility that turn a union of types (`'a' | 'b'`) into a tuple with matching
|
63
|
+
* types (`['a', 'b']`).
|
64
|
+
*
|
65
|
+
* @note this only work with unions of "const" types. Using this with globals
|
66
|
+
* types (`string`, etc.) will yield unexpected results.
|
67
|
+
*
|
68
|
+
* @example
|
69
|
+
* ```ts
|
70
|
+
* type T = UnionToTuple<'a' | 'b'> // ['a', 'b']
|
71
|
+
* type T = UnionToTuple<'a' | 'b' | 'c'> // ['a', 'b', 'c']
|
72
|
+
* ```
|
73
|
+
*/
|
74
|
+
type UnionToTuple<T> = UnionToTupleInternal<T>
|
75
|
+
|
76
|
+
type UnionToTupleInternal<
|
77
|
+
T,
|
78
|
+
// Accumulator for terminal recursivity (initialized to empty tuple)
|
79
|
+
Acc extends readonly any[] = [],
|
80
|
+
// Get the next item from the union (if any)
|
81
|
+
Next = ExtractUnionItem<T>,
|
82
|
+
> =
|
83
|
+
// If there were no more items to extract from the union T, then we are done
|
84
|
+
[Next] extends [never]
|
85
|
+
? // Return result of previous recursive calls
|
86
|
+
Acc
|
87
|
+
: // Recursively call UnionToTupleInternal by Exclude'ing the Next item from
|
88
|
+
// the union (T) and adding it to the accumulator.
|
89
|
+
UnionToTupleInternal<Exclude<T, Next>, readonly [Next, ...Acc]>
|
90
|
+
|
91
|
+
/**
|
92
|
+
* This utility allows to create an assertion function that checks if a
|
93
|
+
* particular interface is fully implemented by some value.
|
94
|
+
*
|
95
|
+
* The use of the (rather complex) {@link UnionToTuple} allows to ensure that,
|
96
|
+
* at runtime, all the required interface keys are indeed checked, and that
|
97
|
+
* future additions to the interface do not result in a false sense of type
|
98
|
+
* safety.
|
99
|
+
*
|
100
|
+
* @note This function should not be made public, as it relies on a quirk of
|
101
|
+
* TypeScript to work properly.
|
102
|
+
*
|
103
|
+
* @example Valid use
|
104
|
+
*
|
105
|
+
* ```ts
|
106
|
+
* const isFoo = buildInterfaceChecker<{ foo: string }>(['foo'])
|
107
|
+
* const isFooBar = buildInterfaceChecker<{ foo: string; bar: boolean }>([
|
108
|
+
* 'foo',
|
109
|
+
* 'bar',
|
110
|
+
* ])
|
111
|
+
*
|
112
|
+
* declare const val: { foo?: string }
|
113
|
+
*
|
114
|
+
* if (isFoo(val)) {
|
115
|
+
* val // { foo: string }
|
116
|
+
* }
|
117
|
+
* ```
|
118
|
+
*
|
119
|
+
* @example Use cases where the runtime keys do not match the interface keys
|
120
|
+
*
|
121
|
+
* ```ts
|
122
|
+
* buildInterfaceChecker<{ foo: string }>([])
|
123
|
+
* buildInterfaceChecker<{ foo: string }>(['fee'])
|
124
|
+
* buildInterfaceChecker<{ foo: string; bar: string }>(['foo'])
|
125
|
+
* buildInterfaceChecker<{ foo: string; bar: string }>(['foo', 'baz'])
|
126
|
+
* ```
|
127
|
+
*/
|
128
|
+
export const buildInterfaceChecker =
|
129
|
+
<I extends object>(keys: readonly string[] & UnionToTuple<keyof I>) =>
|
130
|
+
<V extends Partial<I>>(value: V): value is V & RequiredDefined<I> =>
|
131
|
+
keys.every((name) => value[name] !== undefined)
|
132
|
+
|
133
|
+
// </hardcore-mode>
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { Keyset } from '@atproto/jwk'
|
2
2
|
import {
|
3
3
|
OAuthAuthorizationServerMetadata,
|
4
|
+
OAuthIssuerIdentifier,
|
4
5
|
oauthAuthorizationServerMetadataSchema,
|
5
6
|
} from '@atproto/oauth-types'
|
6
7
|
import { Client } from '../client/client.js'
|
@@ -17,7 +18,7 @@ export type CustomMetadata = {
|
|
17
18
|
* @see {@link https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata}
|
18
19
|
*/
|
19
20
|
export function buildMetadata(
|
20
|
-
issuer:
|
21
|
+
issuer: OAuthIssuerIdentifier,
|
21
22
|
keyset: Keyset,
|
22
23
|
customMetadata?: CustomMetadata,
|
23
24
|
): OAuthAuthorizationServerMetadata {
|
package/src/oauth-errors.ts
CHANGED
@@ -4,6 +4,7 @@ export { OAuthError } from './errors/oauth-error.js'
|
|
4
4
|
export { AccessDeniedError } from './errors/access-denied-error.js'
|
5
5
|
export { AccountSelectionRequiredError } from './errors/account-selection-required-error.js'
|
6
6
|
export { ConsentRequiredError } from './errors/consent-required-error.js'
|
7
|
+
export { HandleUnavailableError } from './errors/handle-unavailable-error.js'
|
7
8
|
export { InvalidAuthorizationDetailsError } from './errors/invalid-authorization-details-error.js'
|
8
9
|
export { InvalidClientError } from './errors/invalid-client-error.js'
|
9
10
|
export { InvalidClientIdError } from './errors/invalid-client-id-error.js'
|