@atproto/oauth-provider 0.6.6 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/dist/access-token/access-token-mode.d.ts +5 -0
- package/dist/access-token/access-token-mode.d.ts.map +1 -0
- package/dist/access-token/access-token-mode.js +9 -0
- package/dist/access-token/access-token-mode.js.map +1 -0
- package/dist/account/account-manager.d.ts +13 -7
- package/dist/account/account-manager.d.ts.map +1 -1
- package/dist/account/account-manager.js +69 -52
- package/dist/account/account-manager.js.map +1 -1
- package/dist/account/account-store.d.ts +88 -77
- package/dist/account/account-store.d.ts.map +1 -1
- package/dist/account/account-store.js +24 -73
- package/dist/account/account-store.js.map +1 -1
- package/dist/account/sign-in-data.d.ts +4 -13
- package/dist/account/sign-in-data.d.ts.map +1 -1
- package/dist/account/sign-in-data.js +9 -9
- package/dist/account/sign-in-data.js.map +1 -1
- package/dist/account/sign-up-input.d.ts +4 -5
- package/dist/account/sign-up-input.d.ts.map +1 -1
- package/dist/account/sign-up-input.js +13 -3
- package/dist/account/sign-up-input.js.map +1 -1
- package/dist/client/client-manager.d.ts +4 -1
- package/dist/client/client-manager.d.ts.map +1 -1
- package/dist/client/client-manager.js +13 -1
- package/dist/client/client-manager.js.map +1 -1
- package/dist/client/client-store.d.ts +1 -1
- package/dist/client/client-store.d.ts.map +1 -1
- package/dist/constants.d.ts +5 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +6 -2
- package/dist/constants.js.map +1 -1
- package/dist/customization/branding.d.ts +54 -0
- package/dist/customization/branding.d.ts.map +1 -0
- package/dist/customization/branding.js +13 -0
- package/dist/customization/branding.js.map +1 -0
- package/dist/customization/build-customization-css.d.ts +3 -0
- package/dist/customization/build-customization-css.d.ts.map +1 -0
- package/dist/customization/build-customization-css.js +27 -0
- package/dist/customization/build-customization-css.js.map +1 -0
- package/dist/customization/build-customization-data.d.ts +4 -0
- package/dist/customization/build-customization-data.d.ts.map +1 -0
- package/dist/customization/build-customization-data.js +18 -0
- package/dist/customization/build-customization-data.js.map +1 -0
- package/dist/customization/colors.d.ts +7 -0
- package/dist/customization/colors.d.ts.map +1 -0
- package/dist/customization/colors.js +27 -0
- package/dist/customization/colors.js.map +1 -0
- package/dist/customization/customization.d.ts +129 -0
- package/dist/customization/customization.d.ts.map +1 -0
- package/dist/customization/customization.js +26 -0
- package/dist/customization/customization.js.map +1 -0
- package/dist/customization/links.d.ts +26 -0
- package/dist/customization/links.d.ts.map +1 -0
- package/dist/customization/links.js +12 -0
- package/dist/customization/links.js.map +1 -0
- package/dist/device/device-id.d.ts +1 -0
- package/dist/device/device-id.d.ts.map +1 -1
- package/dist/device/device-id.js +4 -0
- package/dist/device/device-id.js.map +1 -1
- package/dist/device/device-manager.d.ts +6 -36
- package/dist/device/device-manager.d.ts.map +1 -1
- package/dist/device/device-manager.js +49 -43
- package/dist/device/device-manager.js.map +1 -1
- package/dist/device/device-store.d.ts +1 -0
- package/dist/device/device-store.d.ts.map +1 -1
- package/dist/device/device-store.js.map +1 -1
- package/dist/dpop/dpop-manager.d.ts +3 -3
- package/dist/dpop/dpop-nonce.d.ts +3 -3
- package/dist/dpop/dpop-nonce.d.ts.map +1 -1
- package/dist/errors/access-denied-error.d.ts +4 -3
- package/dist/errors/access-denied-error.d.ts.map +1 -1
- package/dist/errors/access-denied-error.js +5 -6
- package/dist/errors/access-denied-error.js.map +1 -1
- package/dist/{output/build-error-payload.d.ts → errors/error-parser.d.ts} +1 -1
- package/dist/errors/error-parser.d.ts.map +1 -0
- package/dist/{output/build-error-payload.js → errors/error-parser.js} +2 -2
- package/dist/errors/error-parser.js.map +1 -0
- package/dist/errors/invalid-grant-error.d.ts +1 -0
- package/dist/errors/invalid-grant-error.d.ts.map +1 -1
- package/dist/errors/invalid-grant-error.js +5 -0
- package/dist/errors/invalid-grant-error.js.map +1 -1
- package/dist/errors/login-required-error.d.ts +1 -0
- package/dist/errors/login-required-error.d.ts.map +1 -1
- package/dist/errors/login-required-error.js +5 -0
- package/dist/errors/login-required-error.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/html/build-document.d.ts +2 -2
- package/dist/lib/html/build-document.d.ts.map +1 -1
- package/dist/lib/html/build-document.js +4 -0
- package/dist/lib/html/build-document.js.map +1 -1
- package/dist/lib/html/hydration-data.d.ts +4 -0
- package/dist/lib/html/hydration-data.d.ts.map +1 -0
- package/dist/{output/backend-data.js → lib/html/hydration-data.js} +8 -8
- package/dist/lib/html/hydration-data.js.map +1 -0
- package/dist/lib/html/tags.d.ts +1 -1
- package/dist/lib/html/tags.d.ts.map +1 -1
- package/dist/lib/html/tags.js +1 -1
- package/dist/lib/html/tags.js.map +1 -1
- package/dist/lib/http/accept.d.ts +2 -2
- package/dist/lib/http/accept.d.ts.map +1 -1
- package/dist/lib/http/accept.js +1 -1
- package/dist/lib/http/accept.js.map +1 -1
- package/dist/lib/http/context.d.ts +2 -4
- package/dist/lib/http/context.d.ts.map +1 -1
- package/dist/lib/http/context.js +29 -4
- package/dist/lib/http/context.js.map +1 -1
- package/dist/lib/http/headers.d.ts +3 -0
- package/dist/lib/http/headers.d.ts.map +1 -0
- package/dist/lib/http/headers.js +14 -0
- package/dist/lib/http/headers.js.map +1 -0
- package/dist/lib/http/index.d.ts +1 -0
- package/dist/lib/http/index.d.ts.map +1 -1
- package/dist/lib/http/index.js +1 -0
- package/dist/lib/http/index.js.map +1 -1
- package/dist/lib/http/middleware.d.ts +1 -1
- package/dist/lib/http/middleware.d.ts.map +1 -1
- package/dist/lib/http/middleware.js +8 -24
- package/dist/lib/http/middleware.js.map +1 -1
- package/dist/lib/http/parser.d.ts +3 -3
- package/dist/lib/http/parser.d.ts.map +1 -1
- package/dist/lib/http/request.d.ts +13 -9
- package/dist/lib/http/request.d.ts.map +1 -1
- package/dist/lib/http/request.js +27 -49
- package/dist/lib/http/request.js.map +1 -1
- package/dist/lib/http/response.d.ts +6 -2
- package/dist/lib/http/response.d.ts.map +1 -1
- package/dist/lib/http/response.js +31 -11
- package/dist/lib/http/response.js.map +1 -1
- package/dist/lib/http/route.d.ts +3 -3
- package/dist/lib/http/route.d.ts.map +1 -1
- package/dist/lib/http/route.js +1 -1
- package/dist/lib/http/route.js.map +1 -1
- package/dist/lib/http/router.d.ts +12 -11
- package/dist/lib/http/router.d.ts.map +1 -1
- package/dist/lib/http/router.js +26 -34
- package/dist/lib/http/router.js.map +1 -1
- package/dist/lib/http/security-headers.js +1 -1
- package/dist/lib/http/security-headers.js.map +1 -1
- package/dist/lib/http/stream.d.ts +3 -3
- package/dist/lib/http/stream.d.ts.map +1 -1
- package/dist/lib/http/types.d.ts +1 -1
- package/dist/lib/http/types.d.ts.map +1 -1
- package/dist/lib/send-web-page.d.ts +8 -0
- package/dist/lib/send-web-page.d.ts.map +1 -0
- package/dist/{output → lib}/send-web-page.js +9 -7
- package/dist/lib/send-web-page.js.map +1 -0
- package/dist/lib/util/authorization-header.d.ts.map +1 -1
- package/dist/lib/util/color.d.ts +32 -0
- package/dist/lib/util/color.d.ts.map +1 -0
- package/dist/lib/util/color.js +116 -0
- package/dist/lib/util/color.js.map +1 -0
- package/dist/lib/util/crypto.d.ts +1 -0
- package/dist/lib/util/crypto.d.ts.map +1 -1
- package/dist/lib/util/crypto.js +8 -3
- package/dist/lib/util/crypto.js.map +1 -1
- package/dist/lib/util/function.d.ts +1 -0
- package/dist/lib/util/function.d.ts.map +1 -1
- package/dist/lib/util/function.js +12 -0
- package/dist/lib/util/function.js.map +1 -1
- package/dist/lib/util/locale.d.ts +20 -0
- package/dist/lib/util/locale.d.ts.map +1 -0
- package/dist/lib/util/locale.js +14 -0
- package/dist/lib/util/locale.js.map +1 -0
- package/dist/lib/util/time.d.ts +1 -1
- package/dist/lib/util/time.d.ts.map +1 -1
- package/dist/lib/util/time.js +1 -1
- package/dist/lib/util/time.js.map +1 -1
- package/dist/lib/util/type.d.ts +22 -0
- package/dist/lib/util/type.d.ts.map +1 -1
- package/dist/lib/util/type.js.map +1 -1
- package/dist/lib/util/ui8.d.ts +4 -0
- package/dist/lib/util/ui8.d.ts.map +1 -0
- package/dist/lib/util/ui8.js +17 -0
- package/dist/lib/util/ui8.js.map +1 -0
- package/dist/lib/util/zod-error.d.ts +2 -0
- package/dist/lib/util/zod-error.d.ts.map +1 -0
- package/dist/lib/util/zod-error.js +16 -0
- package/dist/lib/util/zod-error.js.map +1 -0
- package/dist/oauth-errors.d.ts +22 -22
- package/dist/oauth-errors.d.ts.map +1 -1
- package/dist/oauth-errors.js +37 -45
- package/dist/oauth-errors.js.map +1 -1
- package/dist/oauth-hooks.d.ts +11 -23
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-hooks.js.map +1 -1
- package/dist/oauth-middleware.d.ts +12 -0
- package/dist/oauth-middleware.d.ts.map +1 -0
- package/dist/oauth-middleware.js +32 -0
- package/dist/oauth-middleware.js.map +1 -0
- package/dist/oauth-provider.d.ts +109 -113
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +124 -542
- package/dist/oauth-provider.js.map +1 -1
- package/dist/oauth-verifier.d.ts +7 -26
- package/dist/oauth-verifier.d.ts.map +1 -1
- package/dist/oauth-verifier.js +6 -16
- package/dist/oauth-verifier.js.map +1 -1
- package/dist/request/code.d.ts.map +1 -1
- package/dist/request/request-data.d.ts +2 -4
- package/dist/request/request-data.d.ts.map +1 -1
- package/dist/request/request-data.js.map +1 -1
- package/dist/request/request-manager.d.ts +4 -2
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +9 -8
- package/dist/request/request-manager.js.map +1 -1
- package/dist/request/request-store.d.ts +6 -0
- package/dist/request/request-store.d.ts.map +1 -1
- package/dist/request/request-store.js +3 -1
- package/dist/request/request-store.js.map +1 -1
- package/dist/result/authorization-redirect-parameters.d.ts +18 -0
- package/dist/result/authorization-redirect-parameters.d.ts.map +1 -0
- package/dist/result/authorization-redirect-parameters.js +3 -0
- package/dist/result/authorization-redirect-parameters.js.map +1 -0
- package/dist/result/authorization-result-authorize-page.d.ts +13 -0
- package/dist/result/authorization-result-authorize-page.d.ts.map +1 -0
- package/dist/result/authorization-result-authorize-page.js +3 -0
- package/dist/result/authorization-result-authorize-page.js.map +1 -0
- package/dist/result/authorization-result-redirect.d.ts +8 -0
- package/dist/result/authorization-result-redirect.d.ts.map +1 -0
- package/dist/result/authorization-result-redirect.js +3 -0
- package/dist/result/authorization-result-redirect.js.map +1 -0
- package/dist/router/assets/assets-manifest.d.ts +10 -0
- package/dist/router/assets/assets-manifest.d.ts.map +1 -0
- package/dist/router/assets/assets-manifest.js +77 -0
- package/dist/router/assets/assets-manifest.js.map +1 -0
- package/dist/router/assets/assets.d.ts +16 -0
- package/dist/router/assets/assets.d.ts.map +1 -0
- package/dist/router/assets/assets.js +43 -0
- package/dist/router/assets/assets.js.map +1 -0
- package/dist/router/assets/csrf.d.ts +4 -0
- package/dist/router/assets/csrf.d.ts.map +1 -0
- package/dist/router/assets/csrf.js +51 -0
- package/dist/router/assets/csrf.js.map +1 -0
- package/dist/router/assets/send-account-page.d.ts +7 -0
- package/dist/router/assets/send-account-page.d.ts.map +1 -0
- package/dist/router/assets/send-account-page.js +34 -0
- package/dist/router/assets/send-account-page.js.map +1 -0
- package/dist/router/assets/send-authorization-page.d.ts +5 -0
- package/dist/router/assets/send-authorization-page.d.ts.map +1 -0
- package/dist/router/assets/send-authorization-page.js +49 -0
- package/dist/router/assets/send-authorization-page.js.map +1 -0
- package/dist/router/assets/send-error-page.d.ts +4 -0
- package/dist/router/assets/send-error-page.d.ts.map +1 -0
- package/dist/router/assets/send-error-page.js +34 -0
- package/dist/router/assets/send-error-page.js.map +1 -0
- package/dist/router/create-account-page-middleware.d.ts +6 -0
- package/dist/router/create-account-page-middleware.d.ts.map +1 -0
- package/dist/router/create-account-page-middleware.js +39 -0
- package/dist/router/create-account-page-middleware.js.map +1 -0
- package/dist/router/create-api-middleware.d.ts +8 -0
- package/dist/router/create-api-middleware.d.ts.map +1 -0
- package/dist/router/create-api-middleware.js +501 -0
- package/dist/router/create-api-middleware.js.map +1 -0
- package/dist/router/create-authorization-page-middleware.d.ts +6 -0
- package/dist/router/create-authorization-page-middleware.d.ts.map +1 -0
- package/dist/router/create-authorization-page-middleware.js +104 -0
- package/dist/router/create-authorization-page-middleware.js.map +1 -0
- package/dist/router/create-oauth-middleware.d.ts +6 -0
- package/dist/router/create-oauth-middleware.d.ts.map +1 -0
- package/dist/router/create-oauth-middleware.js +142 -0
- package/dist/router/create-oauth-middleware.js.map +1 -0
- package/dist/router/error-handler.d.ts +3 -0
- package/dist/router/error-handler.d.ts.map +1 -0
- package/dist/{account/account.js → router/error-handler.js} +1 -1
- package/dist/router/error-handler.js.map +1 -0
- package/dist/router/middleware-options.d.ts +6 -0
- package/dist/router/middleware-options.d.ts.map +1 -0
- package/dist/router/middleware-options.js +3 -0
- package/dist/router/middleware-options.js.map +1 -0
- package/dist/router/send-redirect.d.ts +16 -0
- package/dist/router/send-redirect.d.ts.map +1 -0
- package/dist/{output/send-authorize-redirect.js → router/send-redirect.js} +40 -24
- package/dist/router/send-redirect.js.map +1 -0
- package/dist/{token/token-claims.d.ts → signer/api-token-payload.d.ts} +237 -232
- package/dist/signer/api-token-payload.d.ts.map +1 -0
- package/dist/signer/api-token-payload.js +17 -0
- package/dist/signer/api-token-payload.js.map +1 -0
- package/dist/signer/signed-token-payload.d.ts +164 -159
- package/dist/signer/signed-token-payload.d.ts.map +1 -1
- package/dist/signer/signed-token-payload.js +10 -16
- package/dist/signer/signed-token-payload.js.map +1 -1
- package/dist/signer/signer.d.ts +42 -11246
- package/dist/signer/signer.d.ts.map +1 -1
- package/dist/signer/signer.js +30 -15
- package/dist/signer/signer.js.map +1 -1
- package/dist/token/refresh-token.d.ts.map +1 -1
- package/dist/token/token-data.d.ts +1 -1
- package/dist/token/token-data.d.ts.map +1 -1
- package/dist/token/token-id.d.ts.map +1 -1
- package/dist/token/token-manager.d.ts +28 -26
- package/dist/token/token-manager.d.ts.map +1 -1
- package/dist/token/token-manager.js +138 -196
- package/dist/token/token-manager.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 +1 -0
- package/dist/token/token-store.js.map +1 -1
- package/dist/token/verify-token-claims.d.ts +3 -3
- package/dist/token/verify-token-claims.d.ts.map +1 -1
- package/dist/token/verify-token-claims.js +1 -1
- package/dist/token/verify-token-claims.js.map +1 -1
- package/dist/types/email-otp.d.ts +3 -0
- package/dist/types/email-otp.d.ts.map +1 -0
- package/dist/types/email-otp.js +6 -0
- package/dist/types/email-otp.js.map +1 -0
- package/dist/types/email.d.ts +3 -0
- package/dist/types/email.d.ts.map +1 -0
- package/dist/types/email.js +29 -0
- package/dist/types/email.js.map +1 -0
- package/dist/types/handle.d.ts +3 -0
- package/dist/types/handle.d.ts.map +1 -0
- package/dist/types/handle.js +22 -0
- package/dist/types/handle.js.map +1 -0
- package/dist/types/invite-code.d.ts +4 -0
- package/dist/types/invite-code.d.ts.map +1 -0
- package/dist/types/invite-code.js +6 -0
- package/dist/types/invite-code.js.map +1 -0
- package/dist/types/password.d.ts +4 -0
- package/dist/types/password.d.ts.map +1 -0
- package/dist/types/password.js +7 -0
- package/dist/types/password.js.map +1 -0
- package/package.json +11 -14
- package/src/access-token/access-token-mode.ts +4 -0
- package/src/account/account-manager.ts +105 -75
- package/src/account/account-store.ts +118 -114
- package/src/account/sign-in-data.ts +10 -10
- package/src/account/sign-up-input.ts +13 -4
- package/src/client/client-manager.ts +34 -2
- package/src/client/client-store.ts +1 -1
- package/src/constants.ts +6 -1
- package/src/customization/branding.ts +12 -0
- package/src/customization/build-customization-css.ts +30 -0
- package/src/customization/build-customization-data.ts +22 -0
- package/src/customization/colors.ts +30 -0
- package/src/customization/customization.ts +25 -0
- package/src/customization/links.ts +10 -0
- package/src/device/device-id.ts +5 -0
- package/src/device/device-manager.ts +76 -66
- package/src/device/device-store.ts +2 -0
- package/src/errors/access-denied-error.ts +24 -17
- package/src/{output/build-error-payload.ts → errors/error-parser.ts} +1 -1
- package/src/errors/invalid-grant-error.ts +5 -0
- package/src/errors/login-required-error.ts +10 -0
- package/src/index.ts +1 -0
- package/src/lib/html/build-document.ts +6 -4
- package/src/{output/backend-data.ts → lib/html/hydration-data.ts} +7 -5
- package/src/lib/html/tags.ts +2 -2
- package/src/lib/http/accept.ts +3 -3
- package/src/lib/http/context.ts +41 -10
- package/src/lib/http/headers.ts +15 -0
- package/src/lib/http/index.ts +1 -0
- package/src/lib/http/middleware.ts +8 -23
- package/src/lib/http/request.ts +40 -75
- package/src/lib/http/response.ts +39 -15
- package/src/lib/http/route.ts +8 -5
- package/src/lib/http/router.ts +40 -46
- package/src/lib/http/security-headers.ts +1 -1
- package/src/lib/http/types.ts +1 -6
- package/src/{output → lib}/send-web-page.ts +10 -9
- package/src/lib/util/color.ts +132 -0
- package/src/lib/util/crypto.ts +9 -4
- package/src/lib/util/function.ts +14 -0
- package/src/lib/util/locale.ts +18 -0
- package/src/lib/util/time.ts +3 -4
- package/src/lib/util/type.ts +24 -0
- package/src/lib/util/ui8.ts +14 -0
- package/src/lib/util/zod-error.ts +14 -0
- package/src/oauth-errors.ts +22 -22
- package/src/oauth-hooks.ts +11 -24
- package/src/oauth-middleware.ts +53 -0
- package/src/oauth-provider.ts +290 -1061
- package/src/oauth-verifier.ts +9 -55
- package/src/request/request-data.ts +5 -4
- package/src/request/request-manager.ts +11 -11
- package/src/request/request-store.ts +7 -0
- package/src/result/authorization-redirect-parameters.ts +24 -0
- package/src/result/authorization-result-authorize-page.ts +14 -0
- package/src/result/authorization-result-redirect.ts +8 -0
- package/src/router/assets/assets-manifest.ts +115 -0
- package/src/router/assets/assets.ts +54 -0
- package/src/router/assets/csrf.ts +63 -0
- package/src/router/assets/send-account-page.ts +43 -0
- package/src/router/assets/send-authorization-page.ts +62 -0
- package/src/router/assets/send-error-page.ts +42 -0
- package/src/router/create-account-page-middleware.ts +69 -0
- package/src/router/create-api-middleware.ts +814 -0
- package/src/router/create-authorization-page-middleware.ts +173 -0
- package/src/router/create-oauth-middleware.ts +247 -0
- package/src/router/error-handler.ts +6 -0
- package/src/router/middleware-options.ts +9 -0
- package/src/router/send-redirect.ts +142 -0
- package/src/signer/api-token-payload.ts +18 -0
- package/src/signer/signed-token-payload.ts +18 -28
- package/src/signer/signer.ts +49 -34
- package/src/token/token-data.ts +1 -1
- package/src/token/token-manager.ts +190 -239
- package/src/token/token-store.ts +6 -4
- package/src/token/verify-token-claims.ts +4 -4
- package/src/types/email-otp.ts +3 -0
- package/src/types/email.ts +26 -0
- package/src/types/handle.ts +18 -0
- package/src/types/invite-code.ts +4 -0
- package/src/types/password.ts +4 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +1 -1
- package/dist/access-token/access-token-type.d.ts +0 -6
- package/dist/access-token/access-token-type.d.ts.map +0 -1
- package/dist/access-token/access-token-type.js +0 -10
- package/dist/access-token/access-token-type.js.map +0 -1
- package/dist/account/account.d.ts +0 -2
- package/dist/account/account.d.ts.map +0 -1
- package/dist/account/account.js.map +0 -1
- package/dist/assets/assets-middleware.d.ts +0 -5
- package/dist/assets/assets-middleware.d.ts.map +0 -1
- package/dist/assets/assets-middleware.js +0 -41
- package/dist/assets/assets-middleware.js.map +0 -1
- package/dist/lib/locale.d.ts +0 -15
- package/dist/lib/locale.d.ts.map +0 -1
- package/dist/lib/locale.js +0 -17
- package/dist/lib/locale.js.map +0 -1
- package/dist/output/backend-data.d.ts +0 -4
- package/dist/output/backend-data.d.ts.map +0 -1
- package/dist/output/backend-data.js.map +0 -1
- package/dist/output/build-authorize-data.d.ts +0 -29
- package/dist/output/build-authorize-data.d.ts.map +0 -1
- package/dist/output/build-authorize-data.js +0 -21
- package/dist/output/build-authorize-data.js.map +0 -1
- package/dist/output/build-customization-data.d.ts +0 -234
- package/dist/output/build-customization-data.d.ts.map +0 -1
- package/dist/output/build-customization-data.js +0 -174
- package/dist/output/build-customization-data.js.map +0 -1
- package/dist/output/build-error-data.d.ts +0 -3
- package/dist/output/build-error-data.d.ts.map +0 -1
- package/dist/output/build-error-data.js +0 -10
- package/dist/output/build-error-data.js.map +0 -1
- package/dist/output/build-error-payload.d.ts.map +0 -1
- package/dist/output/build-error-payload.js.map +0 -1
- package/dist/output/output-manager.d.ts +0 -28
- package/dist/output/output-manager.d.ts.map +0 -1
- package/dist/output/output-manager.js +0 -134
- package/dist/output/output-manager.js.map +0 -1
- package/dist/output/send-authorize-redirect.d.ts +0 -25
- package/dist/output/send-authorize-redirect.d.ts.map +0 -1
- package/dist/output/send-authorize-redirect.js.map +0 -1
- package/dist/output/send-web-page.d.ts +0 -8
- package/dist/output/send-web-page.d.ts.map +0 -1
- package/dist/output/send-web-page.js.map +0 -1
- package/dist/token/token-claims.d.ts.map +0 -1
- package/dist/token/token-claims.js +0 -27
- package/dist/token/token-claims.js.map +0 -1
- package/src/access-token/access-token-type.ts +0 -5
- package/src/account/account.ts +0 -1
- package/src/assets/assets-middleware.ts +0 -44
- package/src/lib/locale.ts +0 -21
- package/src/output/build-authorize-data.ts +0 -53
- package/src/output/build-customization-data.ts +0 -217
- package/src/output/build-error-data.ts +0 -8
- package/src/output/output-manager.ts +0 -188
- package/src/output/send-authorize-redirect.ts +0 -137
- package/src/token/token-claims.ts +0 -30
- package/tsconfig.backend.tsbuildinfo +0 -1
- /package/{tsconfig.backend.json → tsconfig.build.json} +0 -0
package/src/oauth-verifier.ts
CHANGED
@@ -6,7 +6,6 @@ import {
|
|
6
6
|
OAuthTokenType,
|
7
7
|
oauthIssuerIdentifierSchema,
|
8
8
|
} from '@atproto/oauth-types'
|
9
|
-
import { AccessTokenType } from './access-token/access-token-type.js'
|
10
9
|
import { DpopManager, DpopManagerOptions } from './dpop/dpop-manager.js'
|
11
10
|
import { DpopNonce } from './dpop/dpop-nonce.js'
|
12
11
|
import { InvalidDpopProofError } from './errors/invalid-dpop-proof-error.js'
|
@@ -40,24 +39,6 @@ export type OAuthVerifierOptions = Override<
|
|
40
39
|
*/
|
41
40
|
keyset: Keyset | Iterable<Key | undefined | null | false>
|
42
41
|
|
43
|
-
/**
|
44
|
-
* If set to {@link AccessTokenType.jwt}, the provider will use JWTs for
|
45
|
-
* access tokens. If set to {@link AccessTokenType.id}, the provider will
|
46
|
-
* use tokenId as access tokens. If set to {@link AccessTokenType.auto},
|
47
|
-
* JWTs will only be used if the audience is different from the issuer.
|
48
|
-
* Defaults to {@link AccessTokenType.jwt}.
|
49
|
-
*
|
50
|
-
* Here is a comparison of the two types:
|
51
|
-
*
|
52
|
-
* - pro id: less CPU intensive (no crypto operations)
|
53
|
-
* - pro id: less bandwidth (shorter tokens than jwt)
|
54
|
-
* - pro id: token data is in sync with database (e.g. revocation)
|
55
|
-
* - pro jwt: stateless: no I/O needed (no db lookups through token store)
|
56
|
-
* - pro jwt: stateless: allows Resource Server to be on a different
|
57
|
-
* host/server
|
58
|
-
*/
|
59
|
-
accessTokenType?: AccessTokenType
|
60
|
-
|
61
42
|
/**
|
62
43
|
* A redis instance to use for replay protection. If not provided, replay
|
63
44
|
* protection will use memory storage.
|
@@ -68,22 +49,16 @@ export type OAuthVerifierOptions = Override<
|
|
68
49
|
}
|
69
50
|
>
|
70
51
|
|
71
|
-
export {
|
72
|
-
|
73
|
-
DpopNonce,
|
74
|
-
Keyset,
|
75
|
-
type ReplayStore,
|
76
|
-
type VerifyTokenClaimsOptions,
|
77
|
-
}
|
52
|
+
export { DpopNonce, Key, Keyset }
|
53
|
+
export type { RedisOptions, ReplayStore, VerifyTokenClaimsOptions }
|
78
54
|
|
79
55
|
export class OAuthVerifier {
|
80
56
|
public readonly issuer: OAuthIssuerIdentifier
|
81
57
|
public readonly keyset: Keyset
|
82
58
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
protected readonly signer: Signer
|
59
|
+
public readonly dpopManager: DpopManager
|
60
|
+
public readonly replayManager: ReplayManager
|
61
|
+
public readonly signer: Signer
|
87
62
|
|
88
63
|
constructor({
|
89
64
|
redis,
|
@@ -92,7 +67,6 @@ export class OAuthVerifier {
|
|
92
67
|
replayStore = redis != null
|
93
68
|
? new ReplayStoreRedis({ redis })
|
94
69
|
: new ReplayStoreMemory(),
|
95
|
-
accessTokenType = AccessTokenType.jwt,
|
96
70
|
|
97
71
|
...rest
|
98
72
|
}: OAuthVerifierOptions) {
|
@@ -101,7 +75,7 @@ export class OAuthVerifier {
|
|
101
75
|
const issuerParsed = oauthIssuerIdentifierSchema.parse(issuer)
|
102
76
|
const issuerUrl = new URL(issuerParsed)
|
103
77
|
|
104
|
-
// TODO (?) support issuer with path
|
78
|
+
// @TODO (?) support issuer with path
|
105
79
|
if (issuerUrl.pathname !== '/') {
|
106
80
|
throw new TypeError(
|
107
81
|
`"issuer" must be an URL with no path, search or hash (${issuerUrl})`,
|
@@ -111,7 +85,6 @@ export class OAuthVerifier {
|
|
111
85
|
this.issuer = issuerParsed
|
112
86
|
this.keyset = keyset instanceof Keyset ? keyset : new Keyset(keyset)
|
113
87
|
|
114
|
-
this.accessTokenType = accessTokenType
|
115
88
|
this.dpopManager = new DpopManager(dpopMgrOptions)
|
116
89
|
this.replayManager = new ReplayManager(replayStore)
|
117
90
|
this.signer = new Signer(this.issuer, this.keyset)
|
@@ -142,19 +115,7 @@ export class OAuthVerifier {
|
|
142
115
|
return jkt
|
143
116
|
}
|
144
117
|
|
145
|
-
protected
|
146
|
-
tokenType: OAuthTokenType,
|
147
|
-
accessTokenType: AccessTokenType,
|
148
|
-
) {
|
149
|
-
if (
|
150
|
-
this.accessTokenType !== AccessTokenType.auto &&
|
151
|
-
this.accessTokenType !== accessTokenType
|
152
|
-
) {
|
153
|
-
throw new InvalidTokenError(tokenType, `Invalid token type`)
|
154
|
-
}
|
155
|
-
}
|
156
|
-
|
157
|
-
protected async authenticateToken(
|
118
|
+
protected async verifyToken(
|
158
119
|
tokenType: OAuthTokenType,
|
159
120
|
token: OAuthAccessToken,
|
160
121
|
dpopJkt: string | null,
|
@@ -164,8 +125,6 @@ export class OAuthVerifier {
|
|
164
125
|
throw new InvalidTokenError(tokenType, `Malformed token`)
|
165
126
|
}
|
166
127
|
|
167
|
-
this.assertTokenTypeAllowed(tokenType, AccessTokenType.jwt)
|
168
|
-
|
169
128
|
const { payload } = await this.signer
|
170
129
|
.verifyAccessToken(token)
|
171
130
|
.catch((err) => {
|
@@ -190,7 +149,7 @@ export class OAuthVerifier {
|
|
190
149
|
dpop?: unknown
|
191
150
|
},
|
192
151
|
verifyOptions?: VerifyTokenClaimsOptions,
|
193
|
-
)
|
152
|
+
) {
|
194
153
|
const [tokenType, token] = parseAuthorizationHeader(headers.authorization)
|
195
154
|
try {
|
196
155
|
const dpopJkt = await this.checkDpopProof(
|
@@ -204,12 +163,7 @@ export class OAuthVerifier {
|
|
204
163
|
throw new InvalidDpopProofError(`DPoP proof required`)
|
205
164
|
}
|
206
165
|
|
207
|
-
return await this.
|
208
|
-
tokenType,
|
209
|
-
token,
|
210
|
-
dpopJkt,
|
211
|
-
verifyOptions,
|
212
|
-
)
|
166
|
+
return await this.verifyToken(tokenType, token, dpopJkt, verifyOptions)
|
213
167
|
} catch (err) {
|
214
168
|
if (err instanceof UseDpopNonceError) throw err.toWwwAuthenticateError()
|
215
169
|
if (err instanceof WWWAuthenticateError) throw err
|
@@ -2,6 +2,7 @@ import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
|
2
2
|
import { ClientAuth } from '../client/client-auth.js'
|
3
3
|
import { ClientId } from '../client/client-id.js'
|
4
4
|
import { DeviceId } from '../device/device-id.js'
|
5
|
+
import { NonNullableKeys } from '../lib/util/type.js'
|
5
6
|
import { Sub } from '../oidc/sub.js'
|
6
7
|
import { Code } from './code.js'
|
7
8
|
|
@@ -15,10 +16,10 @@ export type RequestData = {
|
|
15
16
|
code: Code | null
|
16
17
|
}
|
17
18
|
|
18
|
-
export type RequestDataAuthorized =
|
19
|
-
|
20
|
-
deviceId
|
21
|
-
|
19
|
+
export type RequestDataAuthorized = NonNullableKeys<
|
20
|
+
RequestData,
|
21
|
+
'sub' | 'deviceId'
|
22
|
+
>
|
22
23
|
|
23
24
|
export const isRequestDataAuthorized = (
|
24
25
|
data: RequestData,
|
@@ -1,9 +1,9 @@
|
|
1
|
+
import type { Account } from '@atproto/oauth-provider-api'
|
1
2
|
import {
|
2
3
|
CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
3
4
|
OAuthAuthorizationRequestParameters,
|
4
5
|
OAuthAuthorizationServerMetadata,
|
5
6
|
} from '@atproto/oauth-types'
|
6
|
-
import { Account } from '../account/account.js'
|
7
7
|
import { ClientAuth } from '../client/client-auth.js'
|
8
8
|
import { ClientId } from '../client/client-id.js'
|
9
9
|
import { Client } from '../client/client.js'
|
@@ -132,7 +132,7 @@ export class RequestManager {
|
|
132
132
|
throw new AccessDeniedError(
|
133
133
|
parameters,
|
134
134
|
`Unsupported grant_type "authorization_code"`,
|
135
|
-
'
|
135
|
+
'invalid_request',
|
136
136
|
)
|
137
137
|
}
|
138
138
|
|
@@ -377,9 +377,9 @@ export class RequestManager {
|
|
377
377
|
deviceId: DeviceId,
|
378
378
|
deviceMetadata: RequestMetadata,
|
379
379
|
): Promise<Code> {
|
380
|
-
const
|
380
|
+
const requestId = decodeRequestUri(uri)
|
381
381
|
|
382
|
-
const data = await this.store.readRequest(
|
382
|
+
const data = await this.store.readRequest(requestId)
|
383
383
|
if (!data) throw new InvalidRequestError('Unknown request_uri')
|
384
384
|
|
385
385
|
try {
|
@@ -409,7 +409,7 @@ export class RequestManager {
|
|
409
409
|
const code = await generateCode()
|
410
410
|
|
411
411
|
// Bind the request to the account, preventing it from being used again.
|
412
|
-
await this.store.updateRequest(
|
412
|
+
await this.store.updateRequest(requestId, {
|
413
413
|
sub: account.sub,
|
414
414
|
code,
|
415
415
|
// Allow the client to exchange the code for a token within the next 60 seconds.
|
@@ -422,11 +422,12 @@ export class RequestManager {
|
|
422
422
|
parameters: data.parameters,
|
423
423
|
deviceId,
|
424
424
|
deviceMetadata,
|
425
|
+
requestId,
|
425
426
|
})
|
426
427
|
|
427
428
|
return code
|
428
429
|
} catch (err) {
|
429
|
-
await this.store.deleteRequest(
|
430
|
+
await this.store.deleteRequest(requestId)
|
430
431
|
throw err
|
431
432
|
}
|
432
433
|
}
|
@@ -439,13 +440,12 @@ export class RequestManager {
|
|
439
440
|
client: Client,
|
440
441
|
clientAuth: ClientAuth,
|
441
442
|
code: Code,
|
442
|
-
): Promise<RequestDataAuthorized> {
|
443
|
+
): Promise<RequestDataAuthorized & { requestUri: RequestUri }> {
|
443
444
|
const result = await this.store.findRequestByCode(code)
|
444
445
|
if (!result) throw new InvalidGrantError('Invalid code')
|
445
446
|
|
447
|
+
const { id, data } = result
|
446
448
|
try {
|
447
|
-
const { data } = result
|
448
|
-
|
449
449
|
if (!isRequestDataAuthorized(data)) {
|
450
450
|
// Should never happen: maybe the store implementation is faulty ?
|
451
451
|
throw new Error('Unexpected request state')
|
@@ -478,10 +478,10 @@ export class RequestManager {
|
|
478
478
|
}
|
479
479
|
}
|
480
480
|
|
481
|
-
return data
|
481
|
+
return { ...data, requestUri: encodeRequestUri(id) }
|
482
482
|
} finally {
|
483
483
|
// A "code" can only be used once
|
484
|
-
await this.store.deleteRequest(
|
484
|
+
await this.store.deleteRequest(id)
|
485
485
|
}
|
486
486
|
}
|
487
487
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { InvalidGrantError } from '../errors/invalid-grant-error.js'
|
1
2
|
import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'
|
2
3
|
import { Code } from './code.js'
|
3
4
|
import { RequestData } from './request-data.js'
|
@@ -19,6 +20,8 @@ export type FoundRequestResult = {
|
|
19
20
|
data: RequestData
|
20
21
|
}
|
21
22
|
|
23
|
+
export { InvalidGrantError }
|
24
|
+
|
22
25
|
export interface RequestStore {
|
23
26
|
createRequest(id: RequestId, data: RequestData): Awaitable<void>
|
24
27
|
/**
|
@@ -28,6 +31,10 @@ export interface RequestStore {
|
|
28
31
|
readRequest(id: RequestId): Awaitable<RequestData | null>
|
29
32
|
updateRequest(id: RequestId, data: UpdateRequestData): Awaitable<void>
|
30
33
|
deleteRequest(id: RequestId): void | Awaitable<void>
|
34
|
+
/**
|
35
|
+
* @throws {InvalidGrantError} - When the request is not found or has expired
|
36
|
+
* (allows to provide an error message instead of returning `null`).
|
37
|
+
*/
|
31
38
|
findRequestByCode(code: Code): Awaitable<FoundRequestResult | null>
|
32
39
|
}
|
33
40
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { OAuthTokenType } from '@atproto/oauth-types'
|
2
|
+
import { Code } from '../request/code.js'
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @note `iss` and `state` will be added from the
|
6
|
+
* {@link AuthorizationResultRedirect} object.
|
7
|
+
*/
|
8
|
+
export type AuthorizationRedirectParameters =
|
9
|
+
| {
|
10
|
+
// iss: string
|
11
|
+
// state?: string
|
12
|
+
code: Code
|
13
|
+
id_token?: string
|
14
|
+
access_token?: string
|
15
|
+
token_type?: OAuthTokenType
|
16
|
+
expires_in?: string
|
17
|
+
}
|
18
|
+
| {
|
19
|
+
// iss: string
|
20
|
+
// state?: string
|
21
|
+
error: string
|
22
|
+
error_description?: string
|
23
|
+
error_uri?: string
|
24
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import type { ScopeDetail, Session } from '@atproto/oauth-provider-api'
|
2
|
+
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
3
|
+
import { Client } from '../client/client.js'
|
4
|
+
import { RequestUri } from '../request/request-uri.js'
|
5
|
+
|
6
|
+
export type AuthorizationResultAuthorizePage = {
|
7
|
+
issuer: string
|
8
|
+
client: Client
|
9
|
+
parameters: OAuthAuthorizationRequestParameters
|
10
|
+
|
11
|
+
uri: RequestUri
|
12
|
+
scopeDetails?: ScopeDetail[]
|
13
|
+
sessions: readonly Session[]
|
14
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
2
|
+
import { AuthorizationRedirectParameters } from './authorization-redirect-parameters.js'
|
3
|
+
|
4
|
+
export type AuthorizationResultRedirect = {
|
5
|
+
issuer: string
|
6
|
+
parameters: OAuthAuthorizationRequestParameters
|
7
|
+
redirect: AuthorizationRedirectParameters
|
8
|
+
}
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import { createReadStream } from 'node:fs'
|
2
|
+
import { join } from 'node:path'
|
3
|
+
import { Readable } from 'node:stream'
|
4
|
+
import type { Manifest } from '@atproto-labs/rollup-plugin-bundle-manifest'
|
5
|
+
import { AssetRef } from '../../lib/html/build-document.js'
|
6
|
+
import {
|
7
|
+
Middleware,
|
8
|
+
validateFetchDest,
|
9
|
+
validateFetchSite,
|
10
|
+
writeStream,
|
11
|
+
} from '../../lib/http/index.js'
|
12
|
+
|
13
|
+
type Asset =
|
14
|
+
| {
|
15
|
+
type: 'asset'
|
16
|
+
mime?: string
|
17
|
+
sha256: string
|
18
|
+
stream: () => Readable
|
19
|
+
}
|
20
|
+
| {
|
21
|
+
type: 'chunk'
|
22
|
+
mime: string
|
23
|
+
sha256: string
|
24
|
+
dynamicImports: string[]
|
25
|
+
isDynamicEntry: boolean
|
26
|
+
isEntry: boolean
|
27
|
+
isImplicitEntry: boolean
|
28
|
+
name: string
|
29
|
+
stream: () => Readable
|
30
|
+
}
|
31
|
+
|
32
|
+
const ASSETS_URL_PREFIX = '/@atproto/oauth-provider/~assets/'
|
33
|
+
|
34
|
+
export function parseAssetsManifest(manifestPath: string) {
|
35
|
+
// Using `require` instead of `JSON.parse(readFileSync())` so that node's
|
36
|
+
// watch mode can pick up changes to the manifest file.
|
37
|
+
|
38
|
+
// eslint-disable-next-line
|
39
|
+
const manifest = require(manifestPath) as Manifest
|
40
|
+
|
41
|
+
const assets = new Map<string, Asset>(
|
42
|
+
Object.entries(manifest).map(([filename, { data, ...item }]) => {
|
43
|
+
const buffer = data ? Buffer.from(data, 'base64') : null
|
44
|
+
const filepath = join(manifestPath, '..', filename)
|
45
|
+
const stream = buffer
|
46
|
+
? () => Readable.from(buffer)
|
47
|
+
: () => createReadStream(filepath)
|
48
|
+
return [filename, { ...item, stream }]
|
49
|
+
}),
|
50
|
+
)
|
51
|
+
|
52
|
+
const assetsMiddleware: Middleware = (req, res, next) => {
|
53
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') return next()
|
54
|
+
if (!req.url?.startsWith(ASSETS_URL_PREFIX)) return next()
|
55
|
+
|
56
|
+
const filename = decodeURIComponent(req.url.slice(ASSETS_URL_PREFIX.length))
|
57
|
+
if (!filename) return next()
|
58
|
+
|
59
|
+
const asset = assets.get(filename)
|
60
|
+
if (!asset) return next()
|
61
|
+
|
62
|
+
try {
|
63
|
+
// Allow "null" (ie. no header) to allow loading assets outside of a
|
64
|
+
// fetch context (not from a web page).
|
65
|
+
validateFetchSite(req, [null, 'none', 'cross-site', 'same-origin'])
|
66
|
+
validateFetchDest(req, [null, 'document', 'style', 'script'])
|
67
|
+
} catch (err) {
|
68
|
+
return next(err)
|
69
|
+
}
|
70
|
+
|
71
|
+
if (req.headers['if-none-match'] === asset.sha256) {
|
72
|
+
return void res.writeHead(304).end()
|
73
|
+
}
|
74
|
+
|
75
|
+
res.setHeader('ETag', asset.sha256)
|
76
|
+
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
|
77
|
+
|
78
|
+
writeStream(res, asset.stream(), { contentType: asset.mime })
|
79
|
+
}
|
80
|
+
|
81
|
+
return {
|
82
|
+
getAssets,
|
83
|
+
assetsMiddleware,
|
84
|
+
}
|
85
|
+
|
86
|
+
function getAssets(entryName: string) {
|
87
|
+
const scripts = getScripts(entryName)
|
88
|
+
if (!scripts.length) return null
|
89
|
+
const styles = getStyles(entryName)
|
90
|
+
return { scripts, styles }
|
91
|
+
}
|
92
|
+
|
93
|
+
function getScripts(entryName: string) {
|
94
|
+
return Array.from(assets)
|
95
|
+
.filter(
|
96
|
+
([, asset]) =>
|
97
|
+
asset.type === 'chunk' && asset.isEntry && asset.name === entryName,
|
98
|
+
)
|
99
|
+
.map(assetEntryUrl)
|
100
|
+
}
|
101
|
+
|
102
|
+
function getStyles(_entryName: string) {
|
103
|
+
return Array.from(assets)
|
104
|
+
.filter(([, asset]) => asset.mime === 'text/css')
|
105
|
+
.map(assetEntryUrl)
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
function assetEntryUrl([filename]: [string, Asset]): AssetRef {
|
110
|
+
return { url: assetUrl(filename) }
|
111
|
+
}
|
112
|
+
|
113
|
+
function assetUrl(filename: string) {
|
114
|
+
return `${ASSETS_URL_PREFIX}${encodeURIComponent(filename)}`
|
115
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import type { HydrationData as FeHydrationData } from '@atproto/oauth-provider-frontend/hydration-data'
|
2
|
+
import type { HydrationData as UiHydrationData } from '@atproto/oauth-provider-ui/hydration-data'
|
3
|
+
import { CspConfig } from '../../lib/csp/index.js'
|
4
|
+
import { combineMiddlewares } from '../../lib/http/middleware.js'
|
5
|
+
import { Simplify } from '../../lib/util/type.js'
|
6
|
+
import { parseAssetsManifest } from './assets-manifest.js'
|
7
|
+
|
8
|
+
// If the "ui" and "frontend" packages are ever unified, this can be replaced
|
9
|
+
// with a single expression:
|
10
|
+
//
|
11
|
+
// const { getAssets, assetsMiddleware } = parseAssetsManifest(
|
12
|
+
// require.resolve('@atproto/oauth-provider-ui/bundle-manifest.json'),
|
13
|
+
// )
|
14
|
+
|
15
|
+
const ui = parseAssetsManifest(
|
16
|
+
require.resolve('@atproto/oauth-provider-ui/bundle-manifest.json'),
|
17
|
+
)
|
18
|
+
const fe = parseAssetsManifest(
|
19
|
+
require.resolve('@atproto/oauth-provider-frontend/bundle-manifest.json'),
|
20
|
+
)
|
21
|
+
|
22
|
+
export type HydrationData = Simplify<UiHydrationData & FeHydrationData>
|
23
|
+
|
24
|
+
export function getAssets(entryName: keyof HydrationData) {
|
25
|
+
const assetRef = ui.getAssets(entryName) || fe.getAssets(entryName)
|
26
|
+
if (assetRef) return assetRef
|
27
|
+
|
28
|
+
// Fool-proof. Should never happen.
|
29
|
+
throw new Error(`Entry "${entryName}" not found in assets`)
|
30
|
+
}
|
31
|
+
|
32
|
+
export const assetsMiddleware = combineMiddlewares([
|
33
|
+
ui.assetsMiddleware,
|
34
|
+
fe.assetsMiddleware,
|
35
|
+
])
|
36
|
+
|
37
|
+
export const SPA_CSP: CspConfig = {
|
38
|
+
// API calls are made to the same origin
|
39
|
+
'connect-src': ["'self'"],
|
40
|
+
// Allow loading of PDS logo & User avatars
|
41
|
+
'img-src': ['data:', 'https:'],
|
42
|
+
// Prevent embedding in iframes
|
43
|
+
'frame-ancestors': ["'none'"],
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* @see {@link https://docs.hcaptcha.com/#content-security-policy-settings}
|
48
|
+
*/
|
49
|
+
export const HCAPTCHA_CSP: CspConfig = {
|
50
|
+
'script-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
|
51
|
+
'frame-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
|
52
|
+
'style-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
|
53
|
+
'connect-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'],
|
54
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http'
|
2
|
+
import createHttpError from 'http-errors'
|
3
|
+
import { CSRF_COOKIE_NAME, CSRF_HEADER_NAME } from '@atproto/oauth-provider-api'
|
4
|
+
import {
|
5
|
+
CookieSerializeOptions,
|
6
|
+
parseHttpCookies,
|
7
|
+
setCookie,
|
8
|
+
} from '../../lib/http/index.js'
|
9
|
+
import { randomHexId } from '../../lib/util/crypto.js'
|
10
|
+
|
11
|
+
const TOKEN_BYTE_LENGTH = 12
|
12
|
+
const TOKEN_LENGTH = TOKEN_BYTE_LENGTH * 2 // 2 hex chars per byte
|
13
|
+
|
14
|
+
// @NOTE Cookie based CSRF protection is redundant with session cookies using
|
15
|
+
// `SameSite` and could probably be removed in the future.
|
16
|
+
const CSRF_COOKIE_OPTIONS: Readonly<CookieSerializeOptions> = {
|
17
|
+
expires: undefined, // "session" cookie
|
18
|
+
secure: true,
|
19
|
+
httpOnly: false, // Need to be accessible from JavaScript
|
20
|
+
sameSite: 'lax',
|
21
|
+
path: `/`,
|
22
|
+
}
|
23
|
+
|
24
|
+
export async function setupCsrfToken(
|
25
|
+
req: IncomingMessage,
|
26
|
+
res: ServerResponse,
|
27
|
+
): Promise<string> {
|
28
|
+
const token = getCookieCsrf(req) || (await randomHexId(TOKEN_BYTE_LENGTH))
|
29
|
+
|
30
|
+
// Refresh cookie (See Chrome's "Lax+POST" behavior)
|
31
|
+
setCookie(res, CSRF_COOKIE_NAME, token, CSRF_COOKIE_OPTIONS)
|
32
|
+
|
33
|
+
return token
|
34
|
+
}
|
35
|
+
|
36
|
+
export async function validateCsrfToken(
|
37
|
+
req: IncomingMessage,
|
38
|
+
res: ServerResponse,
|
39
|
+
) {
|
40
|
+
const cookieValue = await setupCsrfToken(req, res)
|
41
|
+
const headerValue = getHeadersCsrf(req)
|
42
|
+
|
43
|
+
if (cookieValue !== headerValue) {
|
44
|
+
throw createHttpError(400, `CSRF mismatch`)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
function getCookieCsrf(req: IncomingMessage) {
|
49
|
+
const cookies = parseHttpCookies(req)
|
50
|
+
const cookieValue = cookies[CSRF_COOKIE_NAME]
|
51
|
+
if (cookieValue?.length === TOKEN_LENGTH) {
|
52
|
+
return cookieValue
|
53
|
+
}
|
54
|
+
return undefined
|
55
|
+
}
|
56
|
+
|
57
|
+
function getHeadersCsrf(req: IncomingMessage) {
|
58
|
+
const headerValue = req.headers[CSRF_HEADER_NAME]
|
59
|
+
if (typeof headerValue === 'string' && headerValue.length === TOKEN_LENGTH) {
|
60
|
+
return headerValue
|
61
|
+
}
|
62
|
+
return undefined
|
63
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http'
|
2
|
+
import type { ActiveDeviceSession } from '@atproto/oauth-provider-api'
|
3
|
+
import { buildCustomizationCss } from '../../customization/build-customization-css.js'
|
4
|
+
import { buildCustomizationData } from '../../customization/build-customization-data.js'
|
5
|
+
import { Customization } from '../../customization/customization.js'
|
6
|
+
import { declareHydrationData } from '../../lib/html/hydration-data.js'
|
7
|
+
import { cssCode } from '../../lib/html/index.js'
|
8
|
+
import { html } from '../../lib/html/tags.js'
|
9
|
+
import { CrossOriginEmbedderPolicy } from '../../lib/http/security-headers.js'
|
10
|
+
import { sendWebPage } from '../../lib/send-web-page.js'
|
11
|
+
import { HydrationData, SPA_CSP, getAssets } from './assets.js'
|
12
|
+
import { setupCsrfToken } from './csrf.js'
|
13
|
+
|
14
|
+
export function sendAccountPageFactory(customization: Customization) {
|
15
|
+
// Pre-computed options:
|
16
|
+
const customizationData = buildCustomizationData(customization)
|
17
|
+
const customizationCss = cssCode(buildCustomizationCss(customization))
|
18
|
+
const { scripts, styles } = getAssets('account-page')
|
19
|
+
|
20
|
+
return async function sendAccountPage(
|
21
|
+
req: IncomingMessage,
|
22
|
+
res: ServerResponse,
|
23
|
+
data: {
|
24
|
+
deviceSessions: readonly ActiveDeviceSession[]
|
25
|
+
},
|
26
|
+
): Promise<void> {
|
27
|
+
await setupCsrfToken(req, res)
|
28
|
+
|
29
|
+
const script = declareHydrationData<HydrationData['account-page']>({
|
30
|
+
__customizationData: customizationData,
|
31
|
+
__deviceSessions: data.deviceSessions,
|
32
|
+
})
|
33
|
+
|
34
|
+
return sendWebPage(res, {
|
35
|
+
meta: [{ name: 'robots', content: 'noindex' }],
|
36
|
+
body: html`<div id="root"></div>`,
|
37
|
+
csp: SPA_CSP,
|
38
|
+
coep: CrossOriginEmbedderPolicy.credentialless,
|
39
|
+
scripts: [script, ...scripts],
|
40
|
+
styles: [...styles, customizationCss],
|
41
|
+
})
|
42
|
+
}
|
43
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http'
|
2
|
+
import { buildCustomizationCss } from '../../customization/build-customization-css.js'
|
3
|
+
import { buildCustomizationData } from '../../customization/build-customization-data.js'
|
4
|
+
import { Customization } from '../../customization/customization.js'
|
5
|
+
import { mergeCsp } from '../../lib/csp/index.js'
|
6
|
+
import { declareHydrationData } from '../../lib/html/hydration-data.js'
|
7
|
+
import { cssCode, html } from '../../lib/html/index.js'
|
8
|
+
import { CrossOriginEmbedderPolicy } from '../../lib/http/security-headers.js'
|
9
|
+
import { sendWebPage } from '../../lib/send-web-page.js'
|
10
|
+
import { AuthorizationResultAuthorizePage } from '../../result/authorization-result-authorize-page.js'
|
11
|
+
import { HCAPTCHA_CSP, HydrationData, SPA_CSP, getAssets } from './assets.js'
|
12
|
+
import { setupCsrfToken } from './csrf.js'
|
13
|
+
|
14
|
+
export function sendAuthorizePageFactory(customization: Customization) {
|
15
|
+
// Pre-computed options:
|
16
|
+
const customizationData = buildCustomizationData(customization)
|
17
|
+
const customizationCss = cssCode(buildCustomizationCss(customization))
|
18
|
+
const { scripts, styles } = getAssets('authorization-page')
|
19
|
+
const csp = mergeCsp(
|
20
|
+
SPA_CSP,
|
21
|
+
customization?.hcaptcha ? HCAPTCHA_CSP : undefined,
|
22
|
+
)
|
23
|
+
const coep = customization?.hcaptcha
|
24
|
+
? // https://github.com/hCaptcha/react-hcaptcha/issues/259
|
25
|
+
// @TODO Remove the use of `unsafeNone` once the issue above is resolved
|
26
|
+
CrossOriginEmbedderPolicy.unsafeNone
|
27
|
+
: CrossOriginEmbedderPolicy.credentialless
|
28
|
+
|
29
|
+
return async function sendAuthorizePage(
|
30
|
+
req: IncomingMessage,
|
31
|
+
res: ServerResponse,
|
32
|
+
data: AuthorizationResultAuthorizePage,
|
33
|
+
): Promise<void> {
|
34
|
+
await setupCsrfToken(req, res)
|
35
|
+
|
36
|
+
const script = declareHydrationData<HydrationData['authorization-page']>({
|
37
|
+
__customizationData: customizationData,
|
38
|
+
__authorizeData: {
|
39
|
+
requestUri: data.uri,
|
40
|
+
|
41
|
+
clientId: data.client.id,
|
42
|
+
clientMetadata: data.client.metadata,
|
43
|
+
clientTrusted: data.client.info.isTrusted,
|
44
|
+
|
45
|
+
scopeDetails: data.scopeDetails,
|
46
|
+
|
47
|
+
uiLocales: data.parameters.ui_locales,
|
48
|
+
loginHint: data.parameters.login_hint,
|
49
|
+
},
|
50
|
+
__sessions: data.sessions,
|
51
|
+
})
|
52
|
+
|
53
|
+
return sendWebPage(res, {
|
54
|
+
meta: [{ name: 'robots', content: 'noindex' }],
|
55
|
+
body: html`<div id="root"></div>`,
|
56
|
+
csp,
|
57
|
+
coep,
|
58
|
+
scripts: [script, ...scripts],
|
59
|
+
styles: [...styles, customizationCss],
|
60
|
+
})
|
61
|
+
}
|
62
|
+
}
|