@atproto/oauth-provider 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.linguirc +57 -0
- package/CHANGELOG.md +29 -0
- package/LICENSE.txt +1 -1
- 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 +12 -13
- package/dist/device/device-manager.d.ts.map +1 -1
- package/dist/device/device-manager.js +5 -3
- 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 +18 -3
- package/dist/lib/http/request.d.ts.map +1 -1
- package/dist/lib/http/request.js +56 -23
- 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 +28 -22
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +212 -211
- 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 +46 -21
- 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 +14 -15
- 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 +81 -28
- 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 +410 -315
- 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
package/dist/oauth-provider.js
CHANGED
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
};
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
6
6
|
exports.OAuthProvider = exports.Keyset = void 0;
|
7
|
-
const accept_1 = require("@hapi/accept");
|
8
7
|
const http_errors_1 = __importDefault(require("http-errors"));
|
9
8
|
const zod_1 = require("zod");
|
10
9
|
const jwk_1 = require("@atproto/jwk");
|
@@ -15,9 +14,10 @@ const simple_store_memory_1 = require("@atproto-labs/simple-store-memory");
|
|
15
14
|
const access_token_type_js_1 = require("./access-token/access-token-type.js");
|
16
15
|
const account_manager_js_1 = require("./account/account-manager.js");
|
17
16
|
const account_store_js_1 = require("./account/account-store.js");
|
17
|
+
const sign_in_data_js_1 = require("./account/sign-in-data.js");
|
18
|
+
const sign_up_data_js_1 = require("./account/sign-up-data.js");
|
18
19
|
const assets_middleware_js_1 = require("./assets/assets-middleware.js");
|
19
20
|
const client_auth_js_1 = require("./client/client-auth.js");
|
20
|
-
const client_id_js_1 = require("./client/client-id.js");
|
21
21
|
const client_manager_js_1 = require("./client/client-manager.js");
|
22
22
|
const client_store_js_1 = require("./client/client-store.js");
|
23
23
|
const constants_js_1 = require("./constants.js");
|
@@ -31,13 +31,14 @@ const invalid_grant_error_js_1 = require("./errors/invalid-grant-error.js");
|
|
31
31
|
const invalid_parameters_error_js_1 = require("./errors/invalid-parameters-error.js");
|
32
32
|
const invalid_request_error_js_1 = require("./errors/invalid-request-error.js");
|
33
33
|
const login_required_error_js_1 = require("./errors/login-required-error.js");
|
34
|
-
const oauth_error_js_1 = require("./errors/oauth-error.js");
|
35
34
|
const unauthorized_client_error_js_1 = require("./errors/unauthorized-client-error.js");
|
36
35
|
const www_authenticate_error_js_1 = require("./errors/www-authenticate-error.js");
|
37
36
|
const index_js_1 = require("./lib/http/index.js");
|
37
|
+
const request_js_1 = require("./lib/http/request.js");
|
38
38
|
const date_js_1 = require("./lib/util/date.js");
|
39
39
|
const build_metadata_js_1 = require("./metadata/build-metadata.js");
|
40
40
|
const oauth_verifier_js_1 = require("./oauth-verifier.js");
|
41
|
+
const build_customization_data_js_1 = require("./output/build-customization-data.js");
|
41
42
|
const build_error_payload_js_1 = require("./output/build-error-payload.js");
|
42
43
|
const output_manager_js_1 = require("./output/output-manager.js");
|
43
44
|
const send_authorize_redirect_js_1 = require("./output/send-authorize-redirect.js");
|
@@ -53,14 +54,14 @@ const token_manager_js_1 = require("./token/token-manager.js");
|
|
53
54
|
const token_store_js_1 = require("./token/token-store.js");
|
54
55
|
class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
55
56
|
metadata;
|
56
|
-
customization;
|
57
57
|
authenticationMaxAge;
|
58
58
|
accountManager;
|
59
|
-
|
59
|
+
deviceManager;
|
60
60
|
clientManager;
|
61
61
|
requestManager;
|
62
62
|
tokenManager;
|
63
|
-
|
63
|
+
outputManager;
|
64
|
+
constructor({ metadata, authenticationMaxAge = constants_js_1.AUTHENTICATION_MAX_AGE, tokenMaxAge = constants_js_1.TOKEN_MAX_AGE, safeFetch = (0, fetch_node_1.safeFetchWrap)(), redis, store, // compound store implementation
|
64
65
|
// Requires stores
|
65
66
|
accountStore = (0, account_store_js_1.asAccountStore)(store), deviceStore = (0, device_store_js_1.asDeviceStore)(store), tokenStore = (0, token_store_js_1.asTokenStore)(store),
|
66
67
|
// These are optional
|
@@ -71,20 +72,36 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
71
72
|
maxSize: 50_000_000,
|
72
73
|
ttl: 600e3,
|
73
74
|
}), loopbackMetadata = oauth_types_1.atprotoLoopbackClientMetadata,
|
74
|
-
// OAuthHooks &
|
75
|
+
// OAuthHooks &
|
76
|
+
// OAuthVerifierOptions &
|
77
|
+
// DeviceManagerOptions &
|
78
|
+
// Customization
|
75
79
|
...rest }) {
|
76
|
-
|
80
|
+
const customization = build_customization_data_js_1.customizationSchema.parse(rest);
|
81
|
+
const deviceManagerOptions = device_manager_js_1.deviceManagerOptionsSchema.parse(rest);
|
82
|
+
// @NOTE: hooks don't really need a type parser, as all zod can actually
|
83
|
+
// check at runtime is the fact that the values are functions. The only way
|
84
|
+
// we would benefit from zod here would be to wrap the functions with a
|
85
|
+
// validator for the provided function's return types, which we do not add
|
86
|
+
// because it would impact runtime performance and we trust the users of
|
87
|
+
// this lib (basically ourselves) to rely on the typing system to ensure the
|
88
|
+
// correct types are returned.
|
89
|
+
const hooks = rest;
|
90
|
+
// @NOTE: validation of super params (if we wanted to implement it) should
|
91
|
+
// be the responsibility of the super class.
|
92
|
+
const superOptions = rest;
|
93
|
+
super({ replayStore, redis, ...superOptions });
|
77
94
|
requestStore ??= redis
|
78
95
|
? new request_store_redis_js_1.RequestStoreRedis({ redis })
|
79
96
|
: new request_store_memory_js_1.RequestStoreMemory();
|
80
97
|
this.authenticationMaxAge = authenticationMaxAge;
|
81
98
|
this.metadata = (0, build_metadata_js_1.buildMetadata)(this.issuer, this.keyset, metadata);
|
82
|
-
this.
|
83
|
-
this.
|
84
|
-
this.accountManager = new account_manager_js_1.AccountManager(accountStore);
|
85
|
-
this.clientManager = new client_manager_js_1.ClientManager(this.metadata, this.keyset,
|
86
|
-
this.requestManager = new request_manager_js_1.RequestManager(requestStore, this.signer, this.metadata,
|
87
|
-
this.tokenManager = new token_manager_js_1.TokenManager(tokenStore, this.signer,
|
99
|
+
this.deviceManager = new device_manager_js_1.DeviceManager(deviceStore, deviceManagerOptions);
|
100
|
+
this.outputManager = new output_manager_js_1.OutputManager(customization);
|
101
|
+
this.accountManager = new account_manager_js_1.AccountManager(this.issuer, accountStore, hooks, customization);
|
102
|
+
this.clientManager = new client_manager_js_1.ClientManager(this.metadata, this.keyset, hooks, clientStore || null, loopbackMetadata || null, safeFetch, clientJwksCache, clientMetadataCache);
|
103
|
+
this.requestManager = new request_manager_js_1.RequestManager(requestStore, this.signer, this.metadata, hooks);
|
104
|
+
this.tokenManager = new token_manager_js_1.TokenManager(tokenStore, this.signer, hooks, this.accessTokenType, tokenMaxAge);
|
88
105
|
}
|
89
106
|
get jwks() {
|
90
107
|
return this.keyset.publicJwks;
|
@@ -170,8 +187,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
170
187
|
// https://datatracker.ietf.org/doc/html/rfc9126#section-2.3-1
|
171
188
|
// > Since initial processing of the pushed authorization request does not
|
172
189
|
// > involve resource owner interaction, error codes related to user
|
173
|
-
// > interaction, such as
|
174
|
-
// > returned.
|
190
|
+
// > interaction, such as "access_denied", are never returned.
|
175
191
|
if (err instanceof access_denied_error_js_1.AccessDeniedError) {
|
176
192
|
throw new invalid_request_error_js_1.InvalidRequestError(err.error_description, err);
|
177
193
|
}
|
@@ -183,7 +199,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
183
199
|
const requestUri = await request_uri_js_1.requestUriSchema
|
184
200
|
.parseAsync(query.request_uri, { path: ['query', 'request_uri'] })
|
185
201
|
.catch(throwInvalidRequest);
|
186
|
-
return this.requestManager.get(requestUri, client.id
|
202
|
+
return this.requestManager.get(requestUri, deviceId, client.id);
|
187
203
|
}
|
188
204
|
if ('request' in query) {
|
189
205
|
const requestObject = await this.decodeJAR(client, query);
|
@@ -202,9 +218,9 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
202
218
|
}
|
203
219
|
return this.requestManager.createAuthorizationRequest(client, { method: 'none' }, query, deviceId, null);
|
204
220
|
}
|
205
|
-
async deleteRequest(
|
221
|
+
async deleteRequest(requestUri, parameters) {
|
206
222
|
try {
|
207
|
-
await this.requestManager.delete(
|
223
|
+
await this.requestManager.delete(requestUri);
|
208
224
|
}
|
209
225
|
catch (err) {
|
210
226
|
throw access_denied_error_js_1.AccessDeniedError.from(parameters, err);
|
@@ -310,12 +326,21 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
310
326
|
matchesHint: hint == null || matchesHint(account),
|
311
327
|
}));
|
312
328
|
}
|
313
|
-
async
|
329
|
+
async signUp({ requestUri, deviceId, deviceMetadata }, data) {
|
330
|
+
const { clientId } = await this.requestManager.get(requestUri, deviceId);
|
314
331
|
const client = await this.clientManager.getClient(clientId);
|
332
|
+
const { account } = await this.accountManager.signUp(data, deviceId, deviceMetadata);
|
333
|
+
return {
|
334
|
+
account,
|
335
|
+
consentRequired: !client.info.isFirstParty,
|
336
|
+
};
|
337
|
+
}
|
338
|
+
async signIn({ requestUri, deviceId, deviceMetadata }, data) {
|
315
339
|
// Ensure the request is still valid (and update the request expiration)
|
316
340
|
// @TODO use the returned scopes to determine if consent is required
|
317
|
-
await this.requestManager.get(
|
318
|
-
const
|
341
|
+
const { clientId } = await this.requestManager.get(requestUri, deviceId);
|
342
|
+
const client = await this.clientManager.getClient(clientId);
|
343
|
+
const { account, info } = await this.accountManager.signIn(data, deviceId, deviceMetadata);
|
319
344
|
return {
|
320
345
|
account,
|
321
346
|
consentRequired: client.info.isFirstParty
|
@@ -326,28 +351,30 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
326
351
|
!info.authorizedClients.includes(client.id),
|
327
352
|
};
|
328
353
|
}
|
329
|
-
async acceptRequest(
|
354
|
+
async acceptRequest({ requestUri, deviceId, deviceMetadata }, sub) {
|
330
355
|
const { issuer } = this;
|
356
|
+
const { parameters, clientId, clientAuth } = await this.requestManager.get(requestUri, deviceId);
|
331
357
|
const client = await this.clientManager.getClient(clientId);
|
332
|
-
const { parameters, clientAuth } = await this.requestManager.get(uri, clientId, deviceId);
|
333
358
|
try {
|
359
|
+
// @TODO Currently, a user can "accept" a request for any did that sing-in
|
360
|
+
// on the device, even if "remember" was set to false.
|
334
361
|
const { account, info } = await this.accountManager.get(deviceId, sub);
|
335
362
|
// The user is trying to authorize without a fresh login
|
336
363
|
if (this.loginRequired(client, parameters, info)) {
|
337
364
|
throw new login_required_error_js_1.LoginRequiredError(parameters, 'Account authentication required.');
|
338
365
|
}
|
339
|
-
const code = await this.requestManager.setAuthorized(
|
366
|
+
const code = await this.requestManager.setAuthorized(requestUri, client, account, deviceId, deviceMetadata);
|
340
367
|
await this.accountManager.addAuthorizedClient(deviceId, account, client, clientAuth);
|
341
368
|
return { issuer, parameters, redirect: { code } };
|
342
369
|
}
|
343
370
|
catch (err) {
|
344
|
-
await this.deleteRequest(
|
371
|
+
await this.deleteRequest(requestUri, parameters);
|
345
372
|
throw access_denied_error_js_1.AccessDeniedError.from(parameters, err);
|
346
373
|
}
|
347
374
|
}
|
348
|
-
async rejectRequest(
|
349
|
-
const { parameters } = await this.requestManager.get(
|
350
|
-
await this.deleteRequest(
|
375
|
+
async rejectRequest({ requestUri, deviceId, }) {
|
376
|
+
const { parameters } = await this.requestManager.get(requestUri, deviceId);
|
377
|
+
await this.deleteRequest(requestUri, parameters);
|
351
378
|
return {
|
352
379
|
issuer: this.issuer,
|
353
380
|
parameters: parameters,
|
@@ -478,52 +505,60 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
478
505
|
return router.buildHandler();
|
479
506
|
}
|
480
507
|
buildRouter(options) {
|
481
|
-
const deviceManager = new device_manager_js_1.DeviceManager(this.deviceStore, options);
|
482
|
-
const outputManager = new output_manager_js_1.OutputManager(this.customization);
|
483
508
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
484
509
|
const server = this;
|
485
510
|
const issuerUrl = new URL(server.issuer);
|
486
511
|
const issuerOrigin = issuerUrl.origin;
|
487
512
|
const router = new index_js_1.Router(issuerUrl);
|
488
513
|
// Utils
|
489
|
-
const csrfCookie = (
|
514
|
+
const csrfCookie = (requestUri) => `csrf-${requestUri}`;
|
490
515
|
const onError = options?.onError ??
|
491
516
|
(process.env['NODE_ENV'] === 'development'
|
492
|
-
? (req, res, err, msg) =>
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
const
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
517
|
+
? (req, res, err, msg) => {
|
518
|
+
console.error(`OAuthProvider error (${msg}):`, err);
|
519
|
+
}
|
520
|
+
: null);
|
521
|
+
// CORS preflight
|
522
|
+
const corsHeaders = function (req, res, next) {
|
523
|
+
res.setHeader('Access-Control-Max-Age', '86400'); // 1 day
|
524
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
525
|
+
//
|
526
|
+
// > For requests without credentials, the literal value "*" can be
|
527
|
+
// > specified as a wildcard; the value tells browsers to allow
|
528
|
+
// > requesting code from any origin to access the resource.
|
529
|
+
// > Attempting to use the wildcard with credentials results in an
|
530
|
+
// > error.
|
531
|
+
//
|
532
|
+
// A "*" is safer to use than reflecting the request origin.
|
533
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
534
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
|
535
|
+
// > The value "*" only counts as a special wildcard value for
|
536
|
+
// > requests without credentials (requests without HTTP cookies or
|
537
|
+
// > HTTP authentication information). In requests with credentials,
|
538
|
+
// > it is treated as the literal method name "*" without special
|
539
|
+
// > semantics.
|
540
|
+
res.setHeader('Access-Control-Allow-Methods', '*');
|
541
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,DPoP');
|
542
|
+
next();
|
543
|
+
};
|
544
|
+
const corsPreflight = (0, index_js_1.combineMiddlewares)([
|
545
|
+
corsHeaders,
|
546
|
+
(req, res) => {
|
547
|
+
res.writeHead(200).end();
|
503
548
|
},
|
504
|
-
(0, index_js_1.staticJsonMiddleware)(json),
|
505
549
|
]);
|
506
550
|
/**
|
507
551
|
* Wrap an OAuth endpoint in a middleware that will set the appropriate
|
508
552
|
* response headers and format the response as JSON.
|
509
553
|
*/
|
510
554
|
const jsonHandler = (buildJson, status) => async function (req, res) {
|
511
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
512
|
-
res.setHeader('Access-Control-Allow-Headers', '*');
|
513
|
-
// https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
|
514
|
-
res.setHeader('Cache-Control', 'no-store');
|
515
|
-
res.setHeader('Pragma', 'no-cache');
|
516
|
-
// https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
|
517
|
-
const dpopNonce = server.nextDpopNonce();
|
518
|
-
if (dpopNonce) {
|
519
|
-
const name = 'DPoP-Nonce';
|
520
|
-
res.setHeader(name, dpopNonce);
|
521
|
-
res.appendHeader('Access-Control-Expose-Headers', name);
|
522
|
-
}
|
523
555
|
try {
|
556
|
+
// https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
|
557
|
+
res.setHeader('Cache-Control', 'no-store');
|
558
|
+
res.setHeader('Pragma', 'no-cache');
|
524
559
|
// Ensure we can agree on a content encoding & type before starting to
|
525
560
|
// build the JSON response.
|
526
|
-
if (!(0,
|
561
|
+
if (!(0, request_js_1.negotiateResponseContent)(req, ['application/json'])) {
|
527
562
|
throw (0, http_errors_1.default)(406, 'Unsupported media type');
|
528
563
|
}
|
529
564
|
const result = await buildJson.call(this, req, res);
|
@@ -535,12 +570,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
535
570
|
}
|
536
571
|
}
|
537
572
|
catch (err) {
|
573
|
+
onError?.(req, res, err, 'OAuth request error');
|
538
574
|
if (!res.headersSent) {
|
539
|
-
if (err instanceof www_authenticate_error_js_1.WWWAuthenticateError) {
|
540
|
-
const name = 'WWW-Authenticate';
|
541
|
-
res.setHeader(name, err.wwwAuthenticateHeader);
|
542
|
-
res.appendHeader('Access-Control-Expose-Headers', name);
|
543
|
-
}
|
544
575
|
const payload = (0, build_error_payload_js_1.buildErrorPayload)(err);
|
545
576
|
const status = (0, build_error_payload_js_1.buildErrorStatus)(err);
|
546
577
|
(0, index_js_1.writeJson)(res, payload, { status });
|
@@ -548,19 +579,52 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
548
579
|
else {
|
549
580
|
res.destroy();
|
550
581
|
}
|
551
|
-
// OAuthError are used to build expected responses, so we don't log
|
552
|
-
// them as errors.
|
553
|
-
if (!(err instanceof oauth_error_js_1.OAuthError) || err.statusCode >= 500) {
|
554
|
-
onError?.(req, res, err, 'Unexpected error');
|
555
|
-
}
|
556
582
|
}
|
557
583
|
};
|
584
|
+
const oauthHandler = (buildOAuthResponse, status) => (0, index_js_1.combineMiddlewares)([
|
585
|
+
corsHeaders,
|
586
|
+
jsonHandler(async function (req, res) {
|
587
|
+
try {
|
588
|
+
// https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
|
589
|
+
const dpopNonce = server.nextDpopNonce();
|
590
|
+
if (dpopNonce) {
|
591
|
+
const name = 'DPoP-Nonce';
|
592
|
+
res.setHeader(name, dpopNonce);
|
593
|
+
res.appendHeader('Access-Control-Expose-Headers', name);
|
594
|
+
}
|
595
|
+
return await buildOAuthResponse.call(this, req, res);
|
596
|
+
}
|
597
|
+
catch (err) {
|
598
|
+
if (!res.headersSent && err instanceof www_authenticate_error_js_1.WWWAuthenticateError) {
|
599
|
+
const name = 'WWW-Authenticate';
|
600
|
+
res.setHeader(name, err.wwwAuthenticateHeader);
|
601
|
+
res.appendHeader('Access-Control-Expose-Headers', name);
|
602
|
+
}
|
603
|
+
throw err;
|
604
|
+
}
|
605
|
+
}, status),
|
606
|
+
]);
|
607
|
+
const apiHandler = (inputSchema, buildJson, status) => jsonHandler(async function (req, res) {
|
608
|
+
(0, index_js_1.validateFetchMode)(req, res, ['same-origin']);
|
609
|
+
(0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
|
610
|
+
(0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
|
611
|
+
const referer = (0, index_js_1.validateReferer)(req, res, {
|
612
|
+
origin: issuerOrigin,
|
613
|
+
pathname: '/oauth/authorize',
|
614
|
+
});
|
615
|
+
const requestUri = await request_uri_js_1.requestUriSchema.parseAsync(referer.searchParams.get('request_uri'), { path: ['query', 'request_uri'] });
|
616
|
+
(0, index_js_1.validateCsrfToken)(req, res, req.headers['x-csrf-token'], csrfCookie(requestUri));
|
617
|
+
const { deviceId, deviceMetadata } = await server.deviceManager.load(req, res);
|
618
|
+
const payload = await (0, index_js_1.parseHttpRequest)(req, ['json']);
|
619
|
+
const input = await inputSchema.parseAsync(payload, { path: ['body'] });
|
620
|
+
const context = { requestUri, deviceId, deviceMetadata };
|
621
|
+
return buildJson.call(this, req, res, input, context);
|
622
|
+
}, status);
|
558
623
|
const navigationHandler = (handler) => async function (req, res) {
|
559
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
560
|
-
res.setHeader('Access-Control-Allow-Headers', '*');
|
561
|
-
res.setHeader('Cache-Control', 'no-store');
|
562
|
-
res.setHeader('Pragma', 'no-cache');
|
563
624
|
try {
|
625
|
+
res.setHeader('Cache-Control', 'no-store');
|
626
|
+
res.setHeader('Pragma', 'no-cache');
|
627
|
+
res.setHeader('Referrer-Policy', 'same-origin');
|
564
628
|
(0, index_js_1.validateFetchMode)(req, res, ['navigate']);
|
565
629
|
(0, index_js_1.validateFetchDest)(req, res, ['document']);
|
566
630
|
(0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
|
@@ -569,10 +633,42 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
569
633
|
catch (err) {
|
570
634
|
onError?.(req, res, err, `Failed to handle navigation request to "${req.url}"`);
|
571
635
|
if (!res.headersSent) {
|
572
|
-
await outputManager.sendErrorPage(res, err
|
636
|
+
await server.outputManager.sendErrorPage(res, err, {
|
637
|
+
preferredLocales: (0, request_js_1.extractLocales)(req),
|
638
|
+
});
|
573
639
|
}
|
574
640
|
}
|
575
641
|
};
|
642
|
+
// Simple GET requests fall under the category of "no-cors" request, meaning
|
643
|
+
// that the browser will allow any cross-origin request, with credentials,
|
644
|
+
// to be sent to the oauth server. The OAuth Server will, however:
|
645
|
+
// 1) validate the request origin (see navigationHandler),
|
646
|
+
// 2) validate the CSRF token,
|
647
|
+
// 3) validate the referer,
|
648
|
+
// 4) validate the sec-fetch-site header,
|
649
|
+
// 4) validate the sec-fetch-mode header (see navigationHandler),
|
650
|
+
// 5) validate the sec-fetch-dest header (see navigationHandler).
|
651
|
+
// And will error (refuse to serve the request) if any of these checks fail.
|
652
|
+
const sameOriginNavigationHandler = (handler) => navigationHandler(async function (req, res) {
|
653
|
+
(0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
|
654
|
+
const deviceInfo = await server.deviceManager.load(req, res);
|
655
|
+
return handler.call(this, req, res, deviceInfo);
|
656
|
+
});
|
657
|
+
const authorizeRedirectNavigationHandler = (handler) => sameOriginNavigationHandler(async function (req, res, deviceInfo) {
|
658
|
+
const referer = (0, index_js_1.validateReferer)(req, res, {
|
659
|
+
origin: issuerOrigin,
|
660
|
+
pathname: '/oauth/authorize',
|
661
|
+
});
|
662
|
+
const requestUri = await request_uri_js_1.requestUriSchema.parseAsync(referer.searchParams.get('request_uri'));
|
663
|
+
const csrfToken = this.url.searchParams.get('csrf_token');
|
664
|
+
const csrfCookieName = csrfCookie(requestUri);
|
665
|
+
// Next line will "clear" the CSRF token cookie, preventing replay of
|
666
|
+
// this request (navigating "back" will result in an error).
|
667
|
+
(0, index_js_1.validateCsrfToken)(req, res, csrfToken, csrfCookieName, true);
|
668
|
+
const context = { ...deviceInfo, requestUri };
|
669
|
+
const redirect = await handler.call(this, req, res, context);
|
670
|
+
return (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, redirect);
|
671
|
+
});
|
576
672
|
/**
|
577
673
|
* Provides a better UX when a request is denied by redirecting to the
|
578
674
|
* client with the error details. This will also log any error that caused
|
@@ -592,36 +688,12 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
592
688
|
throw err;
|
593
689
|
};
|
594
690
|
//- Public OAuth endpoints
|
595
|
-
router.
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
.writeHead(204, {
|
600
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
601
|
-
//
|
602
|
-
// > For requests without credentials, the literal value "*" can be
|
603
|
-
// > specified as a wildcard; the value tells browsers to allow
|
604
|
-
// > requesting code from any origin to access the resource.
|
605
|
-
// > Attempting to use the wildcard with credentials results in an
|
606
|
-
// > error.
|
607
|
-
//
|
608
|
-
// A "*" is safer to use than reflecting the request origin.
|
609
|
-
'Access-Control-Allow-Origin': '*',
|
610
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
|
611
|
-
// > The value "*" only counts as a special wildcard value for
|
612
|
-
// > requests without credentials (requests without HTTP cookies or
|
613
|
-
// > HTTP authentication information). In requests with credentials,
|
614
|
-
// > it is treated as the literal method name "*" without special
|
615
|
-
// > semantics.
|
616
|
-
'Access-Control-Allow-Methods': '*',
|
617
|
-
'Access-Control-Allow-Headers': 'Content-Type,Authorization,DPoP',
|
618
|
-
'Access-Control-Max-Age': '86400', // 1 day
|
619
|
-
})
|
620
|
-
.end();
|
621
|
-
};
|
622
|
-
router.get('/oauth/jwks', staticJson(server.jwks));
|
691
|
+
router.options('/.well-known/oauth-authorization-server', corsPreflight);
|
692
|
+
router.get('/.well-known/oauth-authorization-server', corsHeaders, (0, index_js_1.cacheControlMiddleware)(300), (0, index_js_1.staticJsonMiddleware)(server.metadata));
|
693
|
+
router.options('/oauth/jwks', corsPreflight);
|
694
|
+
router.get('/oauth/jwks', corsHeaders, (0, index_js_1.cacheControlMiddleware)(300), (0, index_js_1.staticJsonMiddleware)(server.jwks));
|
623
695
|
router.options('/oauth/par', corsPreflight);
|
624
|
-
router.post('/oauth/par',
|
696
|
+
router.post('/oauth/par', oauthHandler(async function (req, _res) {
|
625
697
|
const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
|
626
698
|
const credentials = await oauth_types_1.oauthClientCredentialsSchema
|
627
699
|
.parseAsync(payload, { path: ['body'] })
|
@@ -635,14 +707,13 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
635
707
|
// https://datatracker.ietf.org/doc/html/rfc9126#section-2.3
|
636
708
|
// > If the request did not use the POST method, the authorization server
|
637
709
|
// > responds with an HTTP 405 (Method Not Allowed) status code.
|
638
|
-
router.options('/oauth/par', corsPreflight);
|
639
710
|
router.all('/oauth/par', (req, res) => {
|
640
711
|
res.writeHead(405).end();
|
641
712
|
});
|
642
713
|
router.options('/oauth/token', corsPreflight);
|
643
|
-
router.post('/oauth/token',
|
714
|
+
router.post('/oauth/token', oauthHandler(async function (req, _res) {
|
644
715
|
const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
|
645
|
-
const clientMetadata = await deviceManager.getRequestMetadata(req);
|
716
|
+
const clientMetadata = await server.deviceManager.getRequestMetadata(req);
|
646
717
|
const clientCredentials = await oauth_types_1.oauthClientCredentialsSchema
|
647
718
|
.parseAsync(payload, { path: ['body'] })
|
648
719
|
.catch(throwInvalidClient);
|
@@ -653,7 +724,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
653
724
|
return server.token(clientCredentials, clientMetadata, tokenRequest, dpopJkt);
|
654
725
|
}));
|
655
726
|
router.options('/oauth/revoke', corsPreflight);
|
656
|
-
router.post('/oauth/revoke',
|
727
|
+
router.post('/oauth/revoke', oauthHandler(async function (req, res) {
|
657
728
|
const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
|
658
729
|
const tokenIdentification = await oauth_types_1.oauthTokenIdentificationSchema
|
659
730
|
.parseAsync(payload, { path: ['body'] })
|
@@ -665,7 +736,6 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
665
736
|
onError?.(req, res, err, 'Failed to revoke token');
|
666
737
|
}
|
667
738
|
}));
|
668
|
-
router.options('/oauth/revoke', corsPreflight);
|
669
739
|
router.get('/oauth/revoke', navigationHandler(async function (req, res) {
|
670
740
|
const query = Object.fromEntries(this.url.searchParams);
|
671
741
|
const tokenIdentification = await oauth_types_1.oauthTokenIdentificationSchema
|
@@ -681,7 +751,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
681
751
|
// todo: generate JSONP response (if "callback" is provided)
|
682
752
|
throw new Error('You are successfully logged out. Redirect not implemented');
|
683
753
|
}));
|
684
|
-
router.
|
754
|
+
router.options('/oauth/introspect', corsPreflight);
|
755
|
+
router.post('/oauth/introspect', oauthHandler(async function (req, _res) {
|
685
756
|
const payload = await (0, index_js_1.parseHttpRequest)(req, ['json', 'urlencoded']);
|
686
757
|
const credentials = await oauth_types_1.oauthClientCredentialsSchema
|
687
758
|
.parseAsync(payload, { path: ['body'] })
|
@@ -697,7 +768,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
697
768
|
(0, index_js_1.validateFetchSite)(req, res, ['cross-site', 'none']);
|
698
769
|
const query = Object.fromEntries(this.url.searchParams);
|
699
770
|
const clientCredentials = await oauth_types_1.oauthClientCredentialsSchema
|
700
|
-
.parseAsync(query, { path: ['
|
771
|
+
.parseAsync(query, { path: ['query'] })
|
701
772
|
.catch(throwInvalidRequest);
|
702
773
|
if ('client_secret' in clientCredentials) {
|
703
774
|
throw new invalid_request_error_js_1.InvalidRequestError('Client secret must not be provided');
|
@@ -705,117 +776,47 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
705
776
|
const authorizationRequest = await oauth_types_1.oauthAuthorizationRequestQuerySchema
|
706
777
|
.parseAsync(query, { path: ['query'] })
|
707
778
|
.catch(throwInvalidRequest);
|
708
|
-
const { deviceId, deviceMetadata } = await deviceManager.load(req, res);
|
709
|
-
const
|
779
|
+
const { deviceId, deviceMetadata } = await server.deviceManager.load(req, res);
|
780
|
+
const result = await server
|
710
781
|
.authorize(clientCredentials, authorizationRequest, deviceId, deviceMetadata)
|
711
782
|
.catch((err) => accessDeniedToRedirectCatcher(req, res, err));
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
}
|
720
|
-
default: {
|
721
|
-
// Should never happen
|
722
|
-
throw new Error('Unexpected authorization result');
|
723
|
-
}
|
783
|
+
if ('redirect' in result) {
|
784
|
+
return (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, result);
|
785
|
+
}
|
786
|
+
else {
|
787
|
+
await (0, index_js_1.setupCsrfToken)(req, res, csrfCookie(result.authorize.uri));
|
788
|
+
return server.outputManager.sendAuthorizePage(res, result, {
|
789
|
+
preferredLocales: (0, request_js_1.extractLocales)(req),
|
790
|
+
});
|
724
791
|
}
|
725
792
|
}));
|
726
|
-
|
727
|
-
|
728
|
-
request_uri: request_uri_js_1.requestUriSchema,
|
729
|
-
client_id: client_id_js_1.clientIdSchema,
|
730
|
-
credentials: account_store_js_1.signInCredentialsSchema,
|
731
|
-
});
|
732
|
-
router.options('/oauth/authorize/sign-in', corsPreflight);
|
733
|
-
router.post('/oauth/authorize/sign-in', jsonHandler(async function (req, res) {
|
734
|
-
(0, index_js_1.validateFetchMode)(req, res, ['same-origin']);
|
735
|
-
(0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
|
736
|
-
(0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
|
737
|
-
const payload = await (0, index_js_1.parseHttpRequest)(req, ['json']);
|
738
|
-
const input = await signInPayloadSchema.parseAsync(payload, {
|
739
|
-
path: ['body'],
|
740
|
-
});
|
741
|
-
(0, index_js_1.validateReferer)(req, res, {
|
742
|
-
origin: issuerOrigin,
|
743
|
-
pathname: '/oauth/authorize',
|
744
|
-
});
|
745
|
-
(0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri));
|
746
|
-
const { deviceId } = await deviceManager.load(req, res, true);
|
747
|
-
return server.signIn(deviceId, input.request_uri, input.client_id, input.credentials);
|
793
|
+
router.post('/oauth/authorize/verify-handle-availability', apiHandler(zod_1.z.object({ handle: account_store_js_1.handleSchema }).strict(), async function (req, res, data) {
|
794
|
+
return server.accountManager.verifyHandleAvailability(data.handle);
|
748
795
|
}));
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
});
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
const input = await acceptQuerySchema.parseAsync(query, {
|
768
|
-
path: ['query'],
|
769
|
-
});
|
770
|
-
(0, index_js_1.validateReferer)(req, res, {
|
771
|
-
origin: issuerOrigin,
|
772
|
-
pathname: '/oauth/authorize',
|
773
|
-
searchParams: [
|
774
|
-
['request_uri', input.request_uri],
|
775
|
-
['client_id', input.client_id],
|
776
|
-
],
|
777
|
-
});
|
778
|
-
(0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri), true);
|
779
|
-
const { deviceId, deviceMetadata } = await deviceManager.load(req, res);
|
780
|
-
const data = await server
|
781
|
-
.acceptRequest(input.request_uri, input.client_id, input.account_sub, deviceId, deviceMetadata)
|
796
|
+
router.post('/oauth/authorize/sign-up', apiHandler(sign_up_data_js_1.signUpDataSchema, async function (req, res, data, ctx) {
|
797
|
+
return server.signUp(ctx, data);
|
798
|
+
}));
|
799
|
+
router.post('/oauth/authorize/sign-in', apiHandler(sign_in_data_js_1.signInDataSchema, async function (req, res, data, ctx) {
|
800
|
+
return server.signIn(ctx, data);
|
801
|
+
}));
|
802
|
+
router.post('/oauth/authorize/reset-password-request', apiHandler(account_store_js_1.resetPasswordRequestDataSchema, async function (req, res, data) {
|
803
|
+
await server.accountManager.resetPasswordRequest(data);
|
804
|
+
}));
|
805
|
+
router.post('/oauth/authorize/reset-password-confirm', apiHandler(account_store_js_1.resetPasswordConfirmDataSchema, async function (req, res, data) {
|
806
|
+
await server.accountManager.resetPasswordConfirm(data);
|
807
|
+
}));
|
808
|
+
router.get('/oauth/authorize/accept', authorizeRedirectNavigationHandler(async function (req, res, ctx) {
|
809
|
+
const sub = this.url.searchParams.get('account_sub');
|
810
|
+
if (!sub)
|
811
|
+
throw new invalid_request_error_js_1.InvalidRequestError('Account sub not provided');
|
812
|
+
return server
|
813
|
+
.acceptRequest(ctx, sub)
|
782
814
|
.catch((err) => accessDeniedToRedirectCatcher(req, res, err));
|
783
|
-
return await (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
|
784
815
|
}));
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
client_id: client_id_js_1.clientIdSchema,
|
789
|
-
});
|
790
|
-
// Though this is a "no-cors" request, meaning that the browser will allow
|
791
|
-
// any cross-origin request, with credentials, to be sent, the handler will
|
792
|
-
// 1) validate the request origin,
|
793
|
-
// 2) validate the CSRF token,
|
794
|
-
// 3) validate the referer,
|
795
|
-
// 4) validate the sec-fetch-site header,
|
796
|
-
// 4) validate the sec-fetch-mode header,
|
797
|
-
// 5) validate the sec-fetch-dest header (see navigationHandler).
|
798
|
-
// And will error if any of these checks fail.
|
799
|
-
router.get('/oauth/authorize/reject', navigationHandler(async function (req, res) {
|
800
|
-
(0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
|
801
|
-
const query = Object.fromEntries(this.url.searchParams);
|
802
|
-
const input = await rejectQuerySchema.parseAsync(query, {
|
803
|
-
path: ['query'],
|
804
|
-
});
|
805
|
-
(0, index_js_1.validateReferer)(req, res, {
|
806
|
-
origin: issuerOrigin,
|
807
|
-
pathname: '/oauth/authorize',
|
808
|
-
searchParams: [
|
809
|
-
['request_uri', input.request_uri],
|
810
|
-
['client_id', input.client_id],
|
811
|
-
],
|
812
|
-
});
|
813
|
-
(0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri), true);
|
814
|
-
const { deviceId } = await deviceManager.load(req, res);
|
815
|
-
const data = await server
|
816
|
-
.rejectRequest(deviceId, input.request_uri, input.client_id)
|
816
|
+
router.get('/oauth/authorize/reject', authorizeRedirectNavigationHandler(async function (req, res, ctx) {
|
817
|
+
return server
|
818
|
+
.rejectRequest(ctx)
|
817
819
|
.catch((err) => accessDeniedToRedirectCatcher(req, res, err));
|
818
|
-
return await (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
|
819
820
|
}));
|
820
821
|
return router;
|
821
822
|
}
|