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