@atproto/oauth-provider 0.1.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/.postcssrc.yml +3 -0
- package/CHANGELOG.md +19 -0
- package/LICENSE.txt +7 -0
- package/dist/access-token/access-token-type.d.ts +6 -0
- package/dist/access-token/access-token-type.d.ts.map +1 -0
- package/dist/access-token/access-token-type.js +10 -0
- package/dist/access-token/access-token-type.js.map +1 -0
- package/dist/account/account-manager.d.ts +14 -0
- package/dist/account/account-manager.d.ts.map +1 -0
- package/dist/account/account-manager.js +39 -0
- package/dist/account/account-manager.js.map +1 -0
- package/dist/account/account-store.d.ts +39 -0
- package/dist/account/account-store.d.ts.map +1 -0
- package/dist/account/account-store.js +19 -0
- package/dist/account/account-store.js.map +1 -0
- package/dist/account/account.d.ts +8 -0
- package/dist/account/account.d.ts.map +1 -0
- package/dist/account/account.js +3 -0
- package/dist/account/account.js.map +1 -0
- package/dist/assets/app/bundle-manifest.json +22 -0
- package/dist/assets/app/main.css +3 -0
- package/dist/assets/app/main.js +20 -0
- package/dist/assets/app/main.js.map +1 -0
- package/dist/assets/asset.d.ts +9 -0
- package/dist/assets/asset.d.ts.map +1 -0
- package/dist/assets/asset.js +3 -0
- package/dist/assets/asset.js.map +1 -0
- package/dist/assets/assets-middleware.d.ts +2 -0
- package/dist/assets/assets-middleware.d.ts.map +1 -0
- package/dist/assets/assets-middleware.js +30 -0
- package/dist/assets/assets-middleware.js.map +1 -0
- package/dist/assets/index.d.ts +4 -0
- package/dist/assets/index.d.ts.map +1 -0
- package/dist/assets/index.js +65 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/client/client-auth.d.ts +13 -0
- package/dist/client/client-auth.d.ts.map +1 -0
- package/dist/client/client-auth.js +35 -0
- package/dist/client/client-auth.js.map +1 -0
- package/dist/client/client-data.d.ts +8 -0
- package/dist/client/client-data.d.ts.map +1 -0
- package/dist/client/client-data.js +3 -0
- package/dist/client/client-data.js.map +1 -0
- package/dist/client/client-id.d.ts +4 -0
- package/dist/client/client-id.d.ts.map +1 -0
- package/dist/client/client-id.js +6 -0
- package/dist/client/client-id.js.map +1 -0
- package/dist/client/client-info.d.ts +13 -0
- package/dist/client/client-info.d.ts.map +1 -0
- package/dist/client/client-info.js +3 -0
- package/dist/client/client-info.js.map +1 -0
- package/dist/client/client-manager.d.ts +38 -0
- package/dist/client/client-manager.d.ts.map +1 -0
- package/dist/client/client-manager.js +534 -0
- package/dist/client/client-manager.js.map +1 -0
- package/dist/client/client-store.d.ts +13 -0
- package/dist/client/client-store.d.ts.map +1 -0
- package/dist/client/client-store.js +39 -0
- package/dist/client/client-store.js.map +1 -0
- package/dist/client/client-utils.d.ts +6 -0
- package/dist/client/client-utils.d.ts.map +1 -0
- package/dist/client/client-utils.js +40 -0
- package/dist/client/client-utils.js.map +1 -0
- package/dist/client/client.d.ts +41 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +163 -0
- package/dist/client/client.js.map +1 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +53 -0
- package/dist/constants.js.map +1 -0
- package/dist/device/device-data.d.ts +20 -0
- package/dist/device/device-data.d.ts.map +1 -0
- package/dist/device/device-data.js +11 -0
- package/dist/device/device-data.js.map +1 -0
- package/dist/device/device-details.d.ts +17 -0
- package/dist/device/device-details.d.ts.map +1 -0
- package/dist/device/device-details.js +34 -0
- package/dist/device/device-details.js.map +1 -0
- package/dist/device/device-id.d.ts +6 -0
- package/dist/device/device-id.d.ts.map +1 -0
- package/dist/device/device-id.js +18 -0
- package/dist/device/device-id.js.map +1 -0
- package/dist/device/device-manager.d.ts +88 -0
- package/dist/device/device-manager.d.ts.map +1 -0
- package/dist/device/device-manager.js +206 -0
- package/dist/device/device-manager.js.map +1 -0
- package/dist/device/device-store.d.ts +15 -0
- package/dist/device/device-store.d.ts.map +1 -0
- package/dist/device/device-store.js +36 -0
- package/dist/device/device-store.js.map +1 -0
- package/dist/device/session-id.d.ts +6 -0
- package/dist/device/session-id.d.ts.map +1 -0
- package/dist/device/session-id.js +18 -0
- package/dist/device/session-id.js.map +1 -0
- package/dist/dpop/dpop-manager.d.ts +33 -0
- package/dist/dpop/dpop-manager.d.ts.map +1 -0
- package/dist/dpop/dpop-manager.js +115 -0
- package/dist/dpop/dpop-manager.js.map +1 -0
- package/dist/dpop/dpop-nonce.d.ts +13 -0
- package/dist/dpop/dpop-nonce.d.ts.map +1 -0
- package/dist/dpop/dpop-nonce.js +94 -0
- package/dist/dpop/dpop-nonce.js.map +1 -0
- package/dist/errors/access-denied-error.d.ts +8 -0
- package/dist/errors/access-denied-error.d.ts.map +1 -0
- package/dist/errors/access-denied-error.js +21 -0
- package/dist/errors/access-denied-error.js.map +1 -0
- package/dist/errors/account-selection-required-error.d.ts +6 -0
- package/dist/errors/account-selection-required-error.d.ts.map +1 -0
- package/dist/errors/account-selection-required-error.js +11 -0
- package/dist/errors/account-selection-required-error.js.map +1 -0
- package/dist/errors/consent-required-error.d.ts +6 -0
- package/dist/errors/consent-required-error.d.ts.map +1 -0
- package/dist/errors/consent-required-error.js +11 -0
- package/dist/errors/consent-required-error.js.map +1 -0
- package/dist/errors/invalid-authorization-details-error.d.ts +20 -0
- package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -0
- package/dist/errors/invalid-authorization-details-error.js +26 -0
- package/dist/errors/invalid-authorization-details-error.js.map +1 -0
- package/dist/errors/invalid-client-error.d.ts +18 -0
- package/dist/errors/invalid-client-error.d.ts.map +1 -0
- package/dist/errors/invalid-client-error.js +24 -0
- package/dist/errors/invalid-client-error.js.map +1 -0
- package/dist/errors/invalid-client-id-error.d.ts +13 -0
- package/dist/errors/invalid-client-id-error.d.ts.map +1 -0
- package/dist/errors/invalid-client-id-error.js +25 -0
- package/dist/errors/invalid-client-id-error.js.map +1 -0
- package/dist/errors/invalid-client-metadata-error.d.ts +13 -0
- package/dist/errors/invalid-client-metadata-error.d.ts.map +1 -0
- package/dist/errors/invalid-client-metadata-error.js +23 -0
- package/dist/errors/invalid-client-metadata-error.js.map +1 -0
- package/dist/errors/invalid-dpop-key-binding-error.d.ts +12 -0
- package/dist/errors/invalid-dpop-key-binding-error.d.ts.map +1 -0
- package/dist/errors/invalid-dpop-key-binding-error.js +20 -0
- package/dist/errors/invalid-dpop-key-binding-error.js.map +1 -0
- package/dist/errors/invalid-dpop-proof-error.d.ts +5 -0
- package/dist/errors/invalid-dpop-proof-error.d.ts.map +1 -0
- package/dist/errors/invalid-dpop-proof-error.js +12 -0
- package/dist/errors/invalid-dpop-proof-error.js.map +1 -0
- package/dist/errors/invalid-grant-error.d.ts +14 -0
- package/dist/errors/invalid-grant-error.d.ts.map +1 -0
- package/dist/errors/invalid-grant-error.js +20 -0
- package/dist/errors/invalid-grant-error.js.map +1 -0
- package/dist/errors/invalid-parameters-error.d.ts +6 -0
- package/dist/errors/invalid-parameters-error.d.ts.map +1 -0
- package/dist/errors/invalid-parameters-error.js +11 -0
- package/dist/errors/invalid-parameters-error.js.map +1 -0
- package/dist/errors/invalid-redirect-uri-error.d.ts +11 -0
- package/dist/errors/invalid-redirect-uri-error.d.ts.map +1 -0
- package/dist/errors/invalid-redirect-uri-error.js +21 -0
- package/dist/errors/invalid-redirect-uri-error.js.map +1 -0
- package/dist/errors/invalid-request-error.d.ts +28 -0
- package/dist/errors/invalid-request-error.d.ts.map +1 -0
- package/dist/errors/invalid-request-error.js +34 -0
- package/dist/errors/invalid-request-error.js.map +1 -0
- package/dist/errors/invalid-token-error.d.ts +16 -0
- package/dist/errors/invalid-token-error.d.ts.map +1 -0
- package/dist/errors/invalid-token-error.js +45 -0
- package/dist/errors/invalid-token-error.js.map +1 -0
- package/dist/errors/login-required-error.d.ts +6 -0
- package/dist/errors/login-required-error.d.ts.map +1 -0
- package/dist/errors/login-required-error.js +11 -0
- package/dist/errors/login-required-error.js.map +1 -0
- package/dist/errors/oauth-error.d.ts +13 -0
- package/dist/errors/oauth-error.d.ts.map +1 -0
- package/dist/errors/oauth-error.js +29 -0
- package/dist/errors/oauth-error.js.map +1 -0
- package/dist/errors/unauthorized-client-error.d.ts +18 -0
- package/dist/errors/unauthorized-client-error.d.ts.map +1 -0
- package/dist/errors/unauthorized-client-error.js +24 -0
- package/dist/errors/unauthorized-client-error.js.map +1 -0
- package/dist/errors/use-dpop-nonce-error.d.ts +18 -0
- package/dist/errors/use-dpop-nonce-error.d.ts.map +1 -0
- package/dist/errors/use-dpop-nonce-error.js +27 -0
- package/dist/errors/use-dpop-nonce-error.js.map +1 -0
- package/dist/errors/www-authenticate-error.d.ts +9 -0
- package/dist/errors/www-authenticate-error.d.ts.map +1 -0
- package/dist/errors/www-authenticate-error.js +46 -0
- package/dist/errors/www-authenticate-error.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/html/build-document.d.ts +32 -0
- package/dist/lib/html/build-document.d.ts.map +1 -0
- package/dist/lib/html/build-document.js +61 -0
- package/dist/lib/html/build-document.js.map +1 -0
- package/dist/lib/html/escapers.d.ts +9 -0
- package/dist/lib/html/escapers.d.ts.map +1 -0
- package/dist/lib/html/escapers.js +66 -0
- package/dist/lib/html/escapers.js.map +1 -0
- package/dist/lib/html/html.d.ts +13 -0
- package/dist/lib/html/html.d.ts.map +1 -0
- package/dist/lib/html/html.js +53 -0
- package/dist/lib/html/html.js.map +1 -0
- package/dist/lib/html/index.d.ts +4 -0
- package/dist/lib/html/index.d.ts.map +1 -0
- package/dist/lib/html/index.js +21 -0
- package/dist/lib/html/index.js.map +1 -0
- package/dist/lib/html/tags.d.ts +34 -0
- package/dist/lib/html/tags.d.ts.map +1 -0
- package/dist/lib/html/tags.js +47 -0
- package/dist/lib/html/tags.js.map +1 -0
- package/dist/lib/html/util.d.ts +4 -0
- package/dist/lib/html/util.d.ts.map +1 -0
- package/dist/lib/html/util.js +20 -0
- package/dist/lib/html/util.js.map +1 -0
- package/dist/lib/http/accept.d.ts +29 -0
- package/dist/lib/http/accept.d.ts.map +1 -0
- package/dist/lib/http/accept.js +67 -0
- package/dist/lib/http/accept.js.map +1 -0
- package/dist/lib/http/context.d.ts +5 -0
- package/dist/lib/http/context.d.ts.map +1 -0
- package/dist/lib/http/context.js +10 -0
- package/dist/lib/http/context.js.map +1 -0
- package/dist/lib/http/index.d.ts +10 -0
- package/dist/lib/http/index.d.ts.map +1 -0
- package/dist/lib/http/index.js +26 -0
- package/dist/lib/http/index.js.map +1 -0
- package/dist/lib/http/method.d.ts +6 -0
- package/dist/lib/http/method.d.ts.map +1 -0
- package/dist/lib/http/method.js +19 -0
- package/dist/lib/http/method.js.map +1 -0
- package/dist/lib/http/middleware.d.ts +18 -0
- package/dist/lib/http/middleware.d.ts.map +1 -0
- package/dist/lib/http/middleware.js +118 -0
- package/dist/lib/http/middleware.js.map +1 -0
- package/dist/lib/http/parser.d.ts +33 -0
- package/dist/lib/http/parser.d.ts.map +1 -0
- package/dist/lib/http/parser.js +48 -0
- package/dist/lib/http/parser.js.map +1 -0
- package/dist/lib/http/path.d.ts +9 -0
- package/dist/lib/http/path.d.ts.map +1 -0
- package/dist/lib/http/path.js +54 -0
- package/dist/lib/http/path.js.map +1 -0
- package/dist/lib/http/request.d.ts +33 -0
- package/dist/lib/http/request.d.ts.map +1 -0
- package/dist/lib/http/request.js +86 -0
- package/dist/lib/http/request.js.map +1 -0
- package/dist/lib/http/response.d.ts +13 -0
- package/dist/lib/http/response.d.ts.map +1 -0
- package/dist/lib/http/response.js +98 -0
- package/dist/lib/http/response.js.map +1 -0
- package/dist/lib/http/route.d.ts +25 -0
- package/dist/lib/http/route.d.ts.map +1 -0
- package/dist/lib/http/route.js +39 -0
- package/dist/lib/http/route.js.map +1 -0
- package/dist/lib/http/router.d.ts +32 -0
- package/dist/lib/http/router.d.ts.map +1 -0
- package/dist/lib/http/router.js +74 -0
- package/dist/lib/http/router.js.map +1 -0
- package/dist/lib/http/stream.d.ts +13 -0
- package/dist/lib/http/stream.d.ts.map +1 -0
- package/dist/lib/http/stream.js +46 -0
- package/dist/lib/http/stream.js.map +1 -0
- package/dist/lib/http/types.d.ts +7 -0
- package/dist/lib/http/types.d.ts.map +1 -0
- package/dist/lib/http/types.js +3 -0
- package/dist/lib/http/types.js.map +1 -0
- package/dist/lib/http/url.d.ts +8 -0
- package/dist/lib/http/url.d.ts.map +1 -0
- package/dist/lib/http/url.js +22 -0
- package/dist/lib/http/url.js.map +1 -0
- package/dist/lib/redis.d.ts +5 -0
- package/dist/lib/redis.d.ts.map +1 -0
- package/dist/lib/redis.js +22 -0
- package/dist/lib/redis.js.map +1 -0
- package/dist/lib/util/authorization-header.d.ts +4 -0
- package/dist/lib/util/authorization-header.d.ts.map +1 -0
- package/dist/lib/util/authorization-header.js +23 -0
- package/dist/lib/util/authorization-header.js.map +1 -0
- package/dist/lib/util/cast.d.ts +2 -0
- package/dist/lib/util/cast.d.ts.map +1 -0
- package/dist/lib/util/cast.js +10 -0
- package/dist/lib/util/cast.js.map +1 -0
- package/dist/lib/util/crypto.d.ts +3 -0
- package/dist/lib/util/crypto.d.ts.map +1 -0
- package/dist/lib/util/crypto.js +29 -0
- package/dist/lib/util/crypto.js.map +1 -0
- package/dist/lib/util/date.d.ts +3 -0
- package/dist/lib/util/date.d.ts.map +1 -0
- package/dist/lib/util/date.js +12 -0
- package/dist/lib/util/date.js.map +1 -0
- package/dist/lib/util/hostname.d.ts +6 -0
- package/dist/lib/util/hostname.d.ts.map +1 -0
- package/dist/lib/util/hostname.js +24 -0
- package/dist/lib/util/hostname.js.map +1 -0
- package/dist/lib/util/redirect-uri.d.ts +7 -0
- package/dist/lib/util/redirect-uri.d.ts.map +1 -0
- package/dist/lib/util/redirect-uri.js +44 -0
- package/dist/lib/util/redirect-uri.js.map +1 -0
- package/dist/lib/util/time.d.ts +6 -0
- package/dist/lib/util/time.d.ts.map +1 -0
- package/dist/lib/util/time.js +28 -0
- package/dist/lib/util/time.js.map +1 -0
- package/dist/lib/util/type.d.ts +6 -0
- package/dist/lib/util/type.d.ts.map +1 -0
- package/dist/lib/util/type.js +3 -0
- package/dist/lib/util/type.js.map +1 -0
- package/dist/lib/util/well-known.d.ts +3 -0
- package/dist/lib/util/well-known.d.ts.map +1 -0
- package/dist/lib/util/well-known.js +11 -0
- package/dist/lib/util/well-known.js.map +1 -0
- package/dist/metadata/build-metadata.d.ts +14 -0
- package/dist/metadata/build-metadata.d.ts.map +1 -0
- package/dist/metadata/build-metadata.js +132 -0
- package/dist/metadata/build-metadata.js.map +1 -0
- package/dist/oauth-client.d.ts +4 -0
- package/dist/oauth-client.d.ts.map +1 -0
- package/dist/oauth-client.js +19 -0
- package/dist/oauth-client.js.map +1 -0
- package/dist/oauth-dpop.d.ts +3 -0
- package/dist/oauth-dpop.d.ts.map +1 -0
- package/dist/oauth-dpop.js +19 -0
- package/dist/oauth-dpop.js.map +1 -0
- package/dist/oauth-errors.d.ts +20 -0
- package/dist/oauth-errors.d.ts.map +1 -0
- package/dist/oauth-errors.js +43 -0
- package/dist/oauth-errors.js.map +1 -0
- package/dist/oauth-hooks.d.ts +42 -0
- package/dist/oauth-hooks.d.ts.map +1 -0
- package/dist/oauth-hooks.js +3 -0
- package/dist/oauth-hooks.js.map +1 -0
- package/dist/oauth-provider.d.ts +179 -0
- package/dist/oauth-provider.d.ts.map +1 -0
- package/dist/oauth-provider.js +748 -0
- package/dist/oauth-provider.js.map +1 -0
- package/dist/oauth-store.d.ts +11 -0
- package/dist/oauth-store.d.ts.map +1 -0
- package/dist/oauth-store.js +27 -0
- package/dist/oauth-store.js.map +1 -0
- package/dist/oauth-verifier.d.ts +66 -0
- package/dist/oauth-verifier.d.ts.map +1 -0
- package/dist/oauth-verifier.js +94 -0
- package/dist/oauth-verifier.js.map +1 -0
- package/dist/oidc/claims.d.ts +16 -0
- package/dist/oidc/claims.d.ts.map +1 -0
- package/dist/oidc/claims.js +29 -0
- package/dist/oidc/claims.js.map +1 -0
- package/dist/oidc/sub.d.ts +4 -0
- package/dist/oidc/sub.d.ts.map +1 -0
- package/dist/oidc/sub.js +6 -0
- package/dist/oidc/sub.js.map +1 -0
- package/dist/oidc/userinfo.d.ts +7 -0
- package/dist/oidc/userinfo.d.ts.map +1 -0
- package/dist/oidc/userinfo.js +3 -0
- package/dist/oidc/userinfo.js.map +1 -0
- package/dist/output/build-error-payload.d.ts +6 -0
- package/dist/output/build-error-payload.d.ts.map +1 -0
- package/dist/output/build-error-payload.js +108 -0
- package/dist/output/build-error-payload.js.map +1 -0
- package/dist/output/customization.d.ts +37 -0
- package/dist/output/customization.d.ts.map +1 -0
- package/dist/output/customization.js +62 -0
- package/dist/output/customization.js.map +1 -0
- package/dist/output/send-authorize-page.d.ts +43 -0
- package/dist/output/send-authorize-page.d.ts.map +1 -0
- package/dist/output/send-authorize-page.js +49 -0
- package/dist/output/send-authorize-page.js.map +1 -0
- package/dist/output/send-authorize-redirect.d.ts +25 -0
- package/dist/output/send-authorize-redirect.d.ts.map +1 -0
- package/dist/output/send-authorize-redirect.js +72 -0
- package/dist/output/send-authorize-redirect.js.map +1 -0
- package/dist/output/send-error-page.d.ts +5 -0
- package/dist/output/send-error-page.d.ts.map +1 -0
- package/dist/output/send-error-page.js +31 -0
- package/dist/output/send-error-page.js.map +1 -0
- package/dist/output/send-web-page.d.ts +8 -0
- package/dist/output/send-web-page.d.ts.map +1 -0
- package/dist/output/send-web-page.js +48 -0
- package/dist/output/send-web-page.js.map +1 -0
- package/dist/parameters/claims-requested.d.ts +3 -0
- package/dist/parameters/claims-requested.d.ts.map +1 -0
- package/dist/parameters/claims-requested.js +77 -0
- package/dist/parameters/claims-requested.js.map +1 -0
- package/dist/parameters/oidc-payload.d.ts +31 -0
- package/dist/parameters/oidc-payload.d.ts.map +1 -0
- package/dist/parameters/oidc-payload.js +25 -0
- package/dist/parameters/oidc-payload.js.map +1 -0
- package/dist/replay/replay-manager.d.ts +10 -0
- package/dist/replay/replay-manager.d.ts.map +1 -0
- package/dist/replay/replay-manager.js +23 -0
- package/dist/replay/replay-manager.js.map +1 -0
- package/dist/replay/replay-store-memory.d.ts +11 -0
- package/dist/replay/replay-store-memory.d.ts.map +1 -0
- package/dist/replay/replay-store-memory.js +30 -0
- package/dist/replay/replay-store-memory.js.map +1 -0
- package/dist/replay/replay-store-redis.d.ts +16 -0
- package/dist/replay/replay-store-redis.d.ts.map +1 -0
- package/dist/replay/replay-store-redis.js +20 -0
- package/dist/replay/replay-store-redis.js.map +1 -0
- package/dist/replay/replay-store.d.ts +16 -0
- package/dist/replay/replay-store.d.ts.map +1 -0
- package/dist/replay/replay-store.js +22 -0
- package/dist/replay/replay-store.js.map +1 -0
- package/dist/request/code.d.ts +7 -0
- package/dist/request/code.d.ts.map +1 -0
- package/dist/request/code.js +20 -0
- package/dist/request/code.js.map +1 -0
- package/dist/request/request-data.d.ts +21 -0
- package/dist/request/request-data.d.ts.map +1 -0
- package/dist/request/request-data.js +6 -0
- package/dist/request/request-data.js.map +1 -0
- package/dist/request/request-id.d.ts +6 -0
- package/dist/request/request-id.d.ts.map +1 -0
- package/dist/request/request-id.js +18 -0
- package/dist/request/request-id.js.map +1 -0
- package/dist/request/request-info.d.ts +12 -0
- package/dist/request/request-info.d.ts.map +1 -0
- package/dist/request/request-info.js +3 -0
- package/dist/request/request-info.js.map +1 -0
- package/dist/request/request-manager.d.ts +40 -0
- package/dist/request/request-manager.d.ts.map +1 -0
- package/dist/request/request-manager.js +310 -0
- package/dist/request/request-manager.js.map +1 -0
- package/dist/request/request-store-memory.d.ts +16 -0
- package/dist/request/request-store-memory.d.ts.map +1 -0
- package/dist/request/request-store-memory.js +31 -0
- package/dist/request/request-store-memory.js.map +1 -0
- package/dist/request/request-store-redis.d.ts +24 -0
- package/dist/request/request-store-redis.d.ts.map +1 -0
- package/dist/request/request-store-redis.js +58 -0
- package/dist/request/request-store-redis.js.map +1 -0
- package/dist/request/request-store.d.ts +27 -0
- package/dist/request/request-store.d.ts.map +1 -0
- package/dist/request/request-store.js +37 -0
- package/dist/request/request-store.js.map +1 -0
- package/dist/request/request-uri.d.ts +8 -0
- package/dist/request/request-uri.d.ts.map +1 -0
- package/dist/request/request-uri.js +24 -0
- package/dist/request/request-uri.js.map +1 -0
- package/dist/request/types.d.ts +328 -0
- package/dist/request/types.d.ts.map +1 -0
- package/dist/request/types.js +27 -0
- package/dist/request/types.js.map +1 -0
- package/dist/signer/signed-token-payload.d.ts +1694 -0
- package/dist/signer/signed-token-payload.d.ts.map +1 -0
- package/dist/signer/signed-token-payload.js +32 -0
- package/dist/signer/signed-token-payload.js.map +1 -0
- package/dist/signer/signer.d.ts +193 -0
- package/dist/signer/signer.d.ts.map +1 -0
- package/dist/signer/signer.js +101 -0
- package/dist/signer/signer.js.map +1 -0
- package/dist/token/refresh-token.d.ts +7 -0
- package/dist/token/refresh-token.d.ts.map +1 -0
- package/dist/token/refresh-token.js +20 -0
- package/dist/token/refresh-token.js.map +1 -0
- package/dist/token/token-claims.d.ts +1687 -0
- package/dist/token/token-claims.d.ts.map +1 -0
- package/dist/token/token-claims.js +30 -0
- package/dist/token/token-claims.js.map +1 -0
- package/dist/token/token-data.d.ts +20 -0
- package/dist/token/token-data.d.ts.map +1 -0
- package/dist/token/token-data.js +3 -0
- package/dist/token/token-data.js.map +1 -0
- package/dist/token/token-id.d.ts +7 -0
- package/dist/token/token-id.d.ts.map +1 -0
- package/dist/token/token-id.js +20 -0
- package/dist/token/token-id.js.map +1 -0
- package/dist/token/token-manager.d.ts +48 -0
- package/dist/token/token-manager.d.ts.map +1 -0
- package/dist/token/token-manager.js +421 -0
- package/dist/token/token-manager.js.map +1 -0
- package/dist/token/token-store.d.ts +35 -0
- package/dist/token/token-store.d.ts.map +1 -0
- package/dist/token/token-store.js +38 -0
- package/dist/token/token-store.js.map +1 -0
- package/dist/token/types.d.ts +250 -0
- package/dist/token/types.d.ts.map +1 -0
- package/dist/token/types.js +36 -0
- package/dist/token/types.js.map +1 -0
- package/dist/token/verify-token-claims.d.ts +17 -0
- package/dist/token/verify-token-claims.d.ts.map +1 -0
- package/dist/token/verify-token-claims.js +39 -0
- package/dist/token/verify-token-claims.js.map +1 -0
- package/package.json +83 -0
- package/rollup.config.js +55 -0
- package/src/access-token/access-token-type.ts +5 -0
- package/src/account/account-manager.ts +55 -0
- package/src/account/account-store.ts +74 -0
- package/src/account/account.ts +10 -0
- package/src/assets/app/app.tsx +28 -0
- package/src/assets/app/backend-data.ts +65 -0
- package/src/assets/app/components/accept-form.tsx +112 -0
- package/src/assets/app/components/account-identifier.tsx +18 -0
- package/src/assets/app/components/account-picker.tsx +108 -0
- package/src/assets/app/components/client-identifier.tsx +32 -0
- package/src/assets/app/components/client-name.tsx +30 -0
- package/src/assets/app/components/error-card.tsx +41 -0
- package/src/assets/app/components/help-card.tsx +42 -0
- package/src/assets/app/components/layout-title-page.tsx +43 -0
- package/src/assets/app/components/layout-welcome.tsx +58 -0
- package/src/assets/app/components/sign-in-form.tsx +290 -0
- package/src/assets/app/components/sign-up-account-form.tsx +210 -0
- package/src/assets/app/components/sign-up-disclaimer.tsx +44 -0
- package/src/assets/app/components/url-viewer.tsx +70 -0
- package/src/assets/app/cookies.ts +11 -0
- package/src/assets/app/hooks/use-api.ts +104 -0
- package/src/assets/app/hooks/use-bound-dispatch.ts +5 -0
- package/src/assets/app/hooks/use-csrf-token.ts +5 -0
- package/src/assets/app/lib/api.ts +64 -0
- package/src/assets/app/lib/clsx.ts +4 -0
- package/src/assets/app/lib/util.ts +10 -0
- package/src/assets/app/main.css +11 -0
- package/src/assets/app/main.tsx +28 -0
- package/src/assets/app/views/accept-view.tsx +51 -0
- package/src/assets/app/views/authorize-view.tsx +101 -0
- package/src/assets/app/views/error-view.tsx +27 -0
- package/src/assets/app/views/sign-in-view.tsx +121 -0
- package/src/assets/app/views/sign-up-view.tsx +93 -0
- package/src/assets/app/views/welcome-view.tsx +61 -0
- package/src/assets/asset.ts +8 -0
- package/src/assets/assets-middleware.ts +32 -0
- package/src/assets/index.ts +74 -0
- package/src/client/client-auth.ts +45 -0
- package/src/client/client-data.ts +9 -0
- package/src/client/client-id.ts +4 -0
- package/src/client/client-info.ts +13 -0
- package/src/client/client-manager.ts +818 -0
- package/src/client/client-store.ts +38 -0
- package/src/client/client-utils.ts +43 -0
- package/src/client/client.ts +231 -0
- package/src/constants.ts +69 -0
- package/src/device/device-data.ts +11 -0
- package/src/device/device-details.ts +43 -0
- package/src/device/device-id.ts +23 -0
- package/src/device/device-manager.ts +287 -0
- package/src/device/device-store.ts +35 -0
- package/src/device/session-id.ts +22 -0
- package/src/dpop/dpop-manager.ts +147 -0
- package/src/dpop/dpop-nonce.ts +104 -0
- package/src/errors/access-denied-error.ts +26 -0
- package/src/errors/account-selection-required-error.ts +12 -0
- package/src/errors/consent-required-error.ts +12 -0
- package/src/errors/invalid-authorization-details-error.ts +22 -0
- package/src/errors/invalid-client-error.ts +20 -0
- package/src/errors/invalid-client-id-error.ts +20 -0
- package/src/errors/invalid-client-metadata-error.ts +19 -0
- package/src/errors/invalid-dpop-key-binding-error.ts +21 -0
- package/src/errors/invalid-dpop-proof-error.ts +13 -0
- package/src/errors/invalid-grant-error.ts +16 -0
- package/src/errors/invalid-parameters-error.ts +12 -0
- package/src/errors/invalid-redirect-uri-error.ts +17 -0
- package/src/errors/invalid-request-error.ts +30 -0
- package/src/errors/invalid-token-error.ts +59 -0
- package/src/errors/login-required-error.ts +12 -0
- package/src/errors/oauth-error.ts +28 -0
- package/src/errors/unauthorized-client-error.ts +20 -0
- package/src/errors/use-dpop-nonce-error.ts +32 -0
- package/src/errors/www-authenticate-error.ts +65 -0
- package/src/index.ts +15 -0
- package/src/lib/html/README.md +9 -0
- package/src/lib/html/build-document.ts +98 -0
- package/src/lib/html/escapers.ts +66 -0
- package/src/lib/html/html.ts +61 -0
- package/src/lib/html/index.ts +5 -0
- package/src/lib/html/tags.ts +58 -0
- package/src/lib/html/util.ts +21 -0
- package/src/lib/http/README.md +11 -0
- package/src/lib/http/accept.ts +91 -0
- package/src/lib/http/context.ts +11 -0
- package/src/lib/http/index.ts +9 -0
- package/src/lib/http/method.ts +18 -0
- package/src/lib/http/middleware.ts +183 -0
- package/src/lib/http/parser.ts +64 -0
- package/src/lib/http/path.ts +82 -0
- package/src/lib/http/request.ts +141 -0
- package/src/lib/http/response.ts +133 -0
- package/src/lib/http/route.ts +56 -0
- package/src/lib/http/router.ts +118 -0
- package/src/lib/http/stream.ts +78 -0
- package/src/lib/http/types.ts +22 -0
- package/src/lib/http/url.ts +23 -0
- package/src/lib/redis.ts +23 -0
- package/src/lib/util/authorization-header.ts +26 -0
- package/src/lib/util/cast.ts +4 -0
- package/src/lib/util/crypto.ts +27 -0
- package/src/lib/util/date.ts +7 -0
- package/src/lib/util/hostname.ts +19 -0
- package/src/lib/util/redirect-uri.ts +46 -0
- package/src/lib/util/time.ts +33 -0
- package/src/lib/util/type.ts +4 -0
- package/src/lib/util/well-known.ts +8 -0
- package/src/metadata/build-metadata.ts +165 -0
- package/src/oauth-client.ts +3 -0
- package/src/oauth-dpop.ts +2 -0
- package/src/oauth-errors.ts +21 -0
- package/src/oauth-hooks.ts +66 -0
- package/src/oauth-provider.ts +1409 -0
- package/src/oauth-store.ts +11 -0
- package/src/oauth-verifier.ts +219 -0
- package/src/oidc/claims.ts +35 -0
- package/src/oidc/sub.ts +4 -0
- package/src/oidc/userinfo.ts +11 -0
- package/src/output/build-error-payload.ts +143 -0
- package/src/output/customization.ts +96 -0
- package/src/output/send-authorize-page.ts +111 -0
- package/src/output/send-authorize-redirect.ts +130 -0
- package/src/output/send-error-page.ts +41 -0
- package/src/output/send-web-page.ts +66 -0
- package/src/parameters/claims-requested.ts +106 -0
- package/src/parameters/oidc-payload.ts +28 -0
- package/src/replay/replay-manager.ts +38 -0
- package/src/replay/replay-store-memory.ts +36 -0
- package/src/replay/replay-store-redis.ts +31 -0
- package/src/replay/replay-store.ts +44 -0
- package/src/request/code.ts +24 -0
- package/src/request/request-data.ts +26 -0
- package/src/request/request-id.ts +23 -0
- package/src/request/request-info.ts +12 -0
- package/src/request/request-manager.ts +479 -0
- package/src/request/request-store-memory.ts +39 -0
- package/src/request/request-store-redis.ts +71 -0
- package/src/request/request-store.ts +54 -0
- package/src/request/request-uri.ts +29 -0
- package/src/request/types.ts +48 -0
- package/src/signer/signed-token-payload.ts +35 -0
- package/src/signer/signer.ts +165 -0
- package/src/token/refresh-token.ts +31 -0
- package/src/token/token-claims.ts +31 -0
- package/src/token/token-data.ts +33 -0
- package/src/token/token-id.ts +26 -0
- package/src/token/token-manager.ts +591 -0
- package/src/token/token-store.ts +78 -0
- package/src/token/types.ts +86 -0
- package/src/token/verify-token-claims.ts +65 -0
- package/tailwind.config.js +13 -0
- package/tsconfig.backend.json +9 -0
- package/tsconfig.frontend.json +11 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tools.json +8 -0
@@ -0,0 +1,1409 @@
|
|
1
|
+
import { safeFetchWrap } from '@atproto-labs/fetch-node'
|
2
|
+
import { SimpleStore } from '@atproto-labs/simple-store'
|
3
|
+
import { SimpleStoreMemory } from '@atproto-labs/simple-store-memory'
|
4
|
+
import { Jwks, Keyset, SignedJwt, signedJwtSchema } from '@atproto/jwk'
|
5
|
+
import {
|
6
|
+
AccessToken,
|
7
|
+
CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
8
|
+
OAuthAuthenticationRequestParameters,
|
9
|
+
OAuthAuthorizationServerMetadata,
|
10
|
+
OAuthClientIdentification,
|
11
|
+
OAuthClientMetadata,
|
12
|
+
OAuthEndpointName,
|
13
|
+
OAuthTokenResponse,
|
14
|
+
OAuthTokenType,
|
15
|
+
atprotoLoopbackClientMetadata,
|
16
|
+
oauthAuthenticationRequestParametersSchema,
|
17
|
+
} from '@atproto/oauth-types'
|
18
|
+
import { Redis, type RedisOptions } from 'ioredis'
|
19
|
+
import { z } from 'zod'
|
20
|
+
|
21
|
+
import { AccessTokenType } from './access-token/access-token-type.js'
|
22
|
+
import { AccountManager } from './account/account-manager.js'
|
23
|
+
import {
|
24
|
+
AccountInfo,
|
25
|
+
AccountStore,
|
26
|
+
DeviceAccountInfo,
|
27
|
+
LoginCredentials,
|
28
|
+
asAccountStore,
|
29
|
+
} from './account/account-store.js'
|
30
|
+
import { Account } from './account/account.js'
|
31
|
+
import { authorizeAssetsMiddleware } from './assets/assets-middleware.js'
|
32
|
+
import { ClientAuth, authJwkThumbprint } from './client/client-auth.js'
|
33
|
+
import { ClientId, clientIdSchema } from './client/client-id.js'
|
34
|
+
import {
|
35
|
+
ClientManager,
|
36
|
+
LoopbackMetadataGetter,
|
37
|
+
} from './client/client-manager.js'
|
38
|
+
import { ClientStore, ifClientStore } from './client/client-store.js'
|
39
|
+
import { Client } from './client/client.js'
|
40
|
+
import { AUTHENTICATION_MAX_AGE, TOKEN_MAX_AGE } from './constants.js'
|
41
|
+
import { DeviceId } from './device/device-id.js'
|
42
|
+
import { DeviceManager } from './device/device-manager.js'
|
43
|
+
import { DeviceStore, asDeviceStore } from './device/device-store.js'
|
44
|
+
import { AccessDeniedError } from './errors/access-denied-error.js'
|
45
|
+
import { AccountSelectionRequiredError } from './errors/account-selection-required-error.js'
|
46
|
+
import { ConsentRequiredError } from './errors/consent-required-error.js'
|
47
|
+
import { InvalidClientError } from './errors/invalid-client-error.js'
|
48
|
+
import { InvalidGrantError } from './errors/invalid-grant-error.js'
|
49
|
+
import { InvalidParametersError } from './errors/invalid-parameters-error.js'
|
50
|
+
import { InvalidRequestError } from './errors/invalid-request-error.js'
|
51
|
+
import { LoginRequiredError } from './errors/login-required-error.js'
|
52
|
+
import { OAuthError } from './errors/oauth-error.js'
|
53
|
+
import { UnauthorizedClientError } from './errors/unauthorized-client-error.js'
|
54
|
+
import { WWWAuthenticateError } from './errors/www-authenticate-error.js'
|
55
|
+
import {
|
56
|
+
Handler,
|
57
|
+
IncomingMessage,
|
58
|
+
Middleware,
|
59
|
+
Router,
|
60
|
+
ServerResponse,
|
61
|
+
acceptMiddleware,
|
62
|
+
combineMiddlewares,
|
63
|
+
setupCsrfToken,
|
64
|
+
staticJsonHandler,
|
65
|
+
validateCsrfToken,
|
66
|
+
validateFetchMode,
|
67
|
+
validateReferer,
|
68
|
+
validateRequestPayload,
|
69
|
+
validateSameOrigin,
|
70
|
+
writeJson,
|
71
|
+
} from './lib/http/index.js'
|
72
|
+
import { dateToEpoch, dateToRelativeSeconds } from './lib/util/date.js'
|
73
|
+
import { Override } from './lib/util/type.js'
|
74
|
+
import { CustomMetadata, buildMetadata } from './metadata/build-metadata.js'
|
75
|
+
import { OAuthHooks } from './oauth-hooks.js'
|
76
|
+
import { OAuthVerifier, OAuthVerifierOptions } from './oauth-verifier.js'
|
77
|
+
import { Userinfo } from './oidc/userinfo.js'
|
78
|
+
import {
|
79
|
+
buildErrorPayload,
|
80
|
+
buildErrorStatus,
|
81
|
+
} from './output/build-error-payload.js'
|
82
|
+
import { Customization } from './output/customization.js'
|
83
|
+
import {
|
84
|
+
AuthorizationResultAuthorize,
|
85
|
+
sendAuthorizePage,
|
86
|
+
} from './output/send-authorize-page.js'
|
87
|
+
import {
|
88
|
+
AuthorizationResultRedirect,
|
89
|
+
sendAuthorizeRedirect,
|
90
|
+
} from './output/send-authorize-redirect.js'
|
91
|
+
import { sendErrorPage } from './output/send-error-page.js'
|
92
|
+
import { oidcPayload } from './parameters/oidc-payload.js'
|
93
|
+
import { ReplayStore, ifReplayStore } from './replay/replay-store.js'
|
94
|
+
import { RequestInfo } from './request/request-info.js'
|
95
|
+
import { RequestManager } from './request/request-manager.js'
|
96
|
+
import { RequestStoreMemory } from './request/request-store-memory.js'
|
97
|
+
import { RequestStoreRedis } from './request/request-store-redis.js'
|
98
|
+
import { RequestStore, ifRequestStore } from './request/request-store.js'
|
99
|
+
import { RequestUri, requestUriSchema } from './request/request-uri.js'
|
100
|
+
import {
|
101
|
+
AuthorizationRequestJar,
|
102
|
+
AuthorizationRequestQuery,
|
103
|
+
PushedAuthorizationRequest,
|
104
|
+
authorizationRequestQuerySchema,
|
105
|
+
pushedAuthorizationRequestSchema,
|
106
|
+
} from './request/types.js'
|
107
|
+
import { isTokenId } from './token/token-id.js'
|
108
|
+
import { TokenManager } from './token/token-manager.js'
|
109
|
+
import { TokenInfo, TokenStore, asTokenStore } from './token/token-store.js'
|
110
|
+
import {
|
111
|
+
CodeGrantRequest,
|
112
|
+
Introspect,
|
113
|
+
IntrospectionResponse,
|
114
|
+
RefreshGrantRequest,
|
115
|
+
Revoke,
|
116
|
+
TokenRequest,
|
117
|
+
introspectSchema,
|
118
|
+
revokeSchema,
|
119
|
+
tokenRequestSchema,
|
120
|
+
} from './token/types.js'
|
121
|
+
import { VerifyTokenClaimsOptions } from './token/verify-token-claims.js'
|
122
|
+
|
123
|
+
export type OAuthProviderStore = Partial<
|
124
|
+
ClientStore &
|
125
|
+
AccountStore &
|
126
|
+
DeviceStore &
|
127
|
+
TokenStore &
|
128
|
+
RequestStore &
|
129
|
+
ReplayStore
|
130
|
+
>
|
131
|
+
|
132
|
+
export {
|
133
|
+
Keyset,
|
134
|
+
type CustomMetadata,
|
135
|
+
type Customization,
|
136
|
+
type Handler,
|
137
|
+
type OAuthAuthorizationServerMetadata,
|
138
|
+
}
|
139
|
+
|
140
|
+
export type RouterOptions<
|
141
|
+
Req extends IncomingMessage = IncomingMessage,
|
142
|
+
Res extends ServerResponse = ServerResponse,
|
143
|
+
> = {
|
144
|
+
onError?: (req: Req, res: Res, err: unknown, message: string) => void
|
145
|
+
}
|
146
|
+
|
147
|
+
export type OAuthProviderOptions = Override<
|
148
|
+
OAuthVerifierOptions & OAuthHooks,
|
149
|
+
{
|
150
|
+
/**
|
151
|
+
* Maximum age a device/account session can be before requiring
|
152
|
+
* re-authentication. This can be overridden on a authorization request basis
|
153
|
+
* using the `max_age` parameter and on a client basis using the
|
154
|
+
* `default_max_age` client metadata.
|
155
|
+
*/
|
156
|
+
authenticationMaxAge?: number
|
157
|
+
|
158
|
+
/**
|
159
|
+
* Maximum age access & id tokens can be before requiring a refresh.
|
160
|
+
*/
|
161
|
+
tokenMaxAge?: number
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Additional metadata to be included in the discovery document.
|
165
|
+
*/
|
166
|
+
metadata?: CustomMetadata
|
167
|
+
|
168
|
+
/**
|
169
|
+
* UI customizations
|
170
|
+
*/
|
171
|
+
customization?: Customization
|
172
|
+
|
173
|
+
/**
|
174
|
+
* A custom fetch function that can be used to fetch the client metadata from
|
175
|
+
* the internet. By default, the fetch function is a safeFetchWrap() function
|
176
|
+
* that protects against SSRF attacks, large responses & known bad domains. If
|
177
|
+
* you want to disable all protections, you can provide `globalThis.fetch` as
|
178
|
+
* fetch function.
|
179
|
+
*/
|
180
|
+
safeFetch?: typeof globalThis.fetch
|
181
|
+
|
182
|
+
/**
|
183
|
+
* A redis instance to use for replay protection. If not provided, replay
|
184
|
+
* protection will use memory storage.
|
185
|
+
*/
|
186
|
+
redis?: Redis | RedisOptions | string
|
187
|
+
|
188
|
+
/**
|
189
|
+
* This will be used as the default store for all the stores. If a store is
|
190
|
+
* not provided, this store will be used instead. If the `store` does not
|
191
|
+
* implement a specific store, a runtime error will be thrown. Make sure that
|
192
|
+
* this store implements all the interfaces not provided in the other
|
193
|
+
* `<name>Store` options.
|
194
|
+
*/
|
195
|
+
store?: OAuthProviderStore
|
196
|
+
|
197
|
+
accountStore?: AccountStore
|
198
|
+
deviceStore?: DeviceStore
|
199
|
+
clientStore?: ClientStore
|
200
|
+
replayStore?: ReplayStore
|
201
|
+
requestStore?: RequestStore
|
202
|
+
tokenStore?: TokenStore
|
203
|
+
|
204
|
+
/**
|
205
|
+
* In order to speed up the client fetching process, you can provide a cache
|
206
|
+
* to store HTTP responses.
|
207
|
+
*
|
208
|
+
* @note the cached entries should automatically expire after a certain time (typically 10 minutes)
|
209
|
+
*/
|
210
|
+
clientJwksCache?: SimpleStore<string, Jwks>
|
211
|
+
|
212
|
+
/**
|
213
|
+
* In order to speed up the client fetching process, you can provide a cache
|
214
|
+
* to store HTTP responses.
|
215
|
+
*
|
216
|
+
* @note the cached entries should automatically expire after a certain time (typically 10 minutes)
|
217
|
+
*/
|
218
|
+
clientMetadataCache?: SimpleStore<string, OAuthClientMetadata>
|
219
|
+
|
220
|
+
/**
|
221
|
+
* In order to enable loopback clients, you can provide a function that
|
222
|
+
* returns the client metadata for a given loopback URL. This is useful for
|
223
|
+
* development and testing purposes. This function is not called for internet
|
224
|
+
* clients.
|
225
|
+
*
|
226
|
+
* @default is as specified by ATPROTO
|
227
|
+
*/
|
228
|
+
loopbackMetadata?: null | false | LoopbackMetadataGetter
|
229
|
+
}
|
230
|
+
>
|
231
|
+
|
232
|
+
export class OAuthProvider extends OAuthVerifier {
|
233
|
+
public readonly metadata: OAuthAuthorizationServerMetadata
|
234
|
+
public readonly customization?: Customization
|
235
|
+
|
236
|
+
public readonly authenticationMaxAge: number
|
237
|
+
|
238
|
+
public readonly accountManager: AccountManager
|
239
|
+
public readonly deviceStore: DeviceStore
|
240
|
+
public readonly clientManager: ClientManager
|
241
|
+
public readonly requestManager: RequestManager
|
242
|
+
public readonly tokenManager: TokenManager
|
243
|
+
|
244
|
+
public constructor({
|
245
|
+
metadata,
|
246
|
+
customization = undefined,
|
247
|
+
authenticationMaxAge = AUTHENTICATION_MAX_AGE,
|
248
|
+
tokenMaxAge = TOKEN_MAX_AGE,
|
249
|
+
|
250
|
+
safeFetch = safeFetchWrap(),
|
251
|
+
redis,
|
252
|
+
store, // compound store implementation
|
253
|
+
|
254
|
+
// Requires stores
|
255
|
+
accountStore = asAccountStore(store),
|
256
|
+
deviceStore = asDeviceStore(store),
|
257
|
+
tokenStore = asTokenStore(store),
|
258
|
+
|
259
|
+
// These are optional
|
260
|
+
clientStore = ifClientStore(store),
|
261
|
+
replayStore = ifReplayStore(store),
|
262
|
+
requestStore = ifRequestStore(store),
|
263
|
+
|
264
|
+
clientJwksCache = new SimpleStoreMemory({
|
265
|
+
maxSize: 50_000_000,
|
266
|
+
ttl: 600e3,
|
267
|
+
}),
|
268
|
+
clientMetadataCache = new SimpleStoreMemory({
|
269
|
+
maxSize: 50_000_000,
|
270
|
+
ttl: 600e3,
|
271
|
+
}),
|
272
|
+
|
273
|
+
loopbackMetadata = atprotoLoopbackClientMetadata,
|
274
|
+
|
275
|
+
// OAuthHooks & OAuthVerifierOptions
|
276
|
+
...rest
|
277
|
+
}: OAuthProviderOptions) {
|
278
|
+
super({ replayStore, redis, ...rest })
|
279
|
+
|
280
|
+
requestStore ??= redis
|
281
|
+
? new RequestStoreRedis({ redis })
|
282
|
+
: new RequestStoreMemory()
|
283
|
+
|
284
|
+
this.authenticationMaxAge = authenticationMaxAge
|
285
|
+
this.metadata = buildMetadata(this.issuer, this.keyset, metadata)
|
286
|
+
this.customization = customization
|
287
|
+
|
288
|
+
this.deviceStore = deviceStore
|
289
|
+
|
290
|
+
this.accountManager = new AccountManager(accountStore)
|
291
|
+
this.clientManager = new ClientManager(
|
292
|
+
this.keyset,
|
293
|
+
rest,
|
294
|
+
clientStore || null,
|
295
|
+
loopbackMetadata || null,
|
296
|
+
safeFetch,
|
297
|
+
clientJwksCache,
|
298
|
+
clientMetadataCache,
|
299
|
+
)
|
300
|
+
this.requestManager = new RequestManager(
|
301
|
+
requestStore,
|
302
|
+
this.signer,
|
303
|
+
this.metadata,
|
304
|
+
rest,
|
305
|
+
)
|
306
|
+
this.tokenManager = new TokenManager(
|
307
|
+
tokenStore,
|
308
|
+
this.signer,
|
309
|
+
rest,
|
310
|
+
this.accessTokenType,
|
311
|
+
tokenMaxAge,
|
312
|
+
)
|
313
|
+
}
|
314
|
+
|
315
|
+
get jwks(): Jwks {
|
316
|
+
return this.keyset.publicJwks
|
317
|
+
}
|
318
|
+
|
319
|
+
protected loginRequired(
|
320
|
+
client: Client,
|
321
|
+
parameters: OAuthAuthenticationRequestParameters,
|
322
|
+
info: DeviceAccountInfo,
|
323
|
+
) {
|
324
|
+
/** in seconds */
|
325
|
+
const authAge = (Date.now() - info.authenticatedAt.getTime()) / 1e3
|
326
|
+
|
327
|
+
// Fool-proof (invalid date, or suspiciously in the future)
|
328
|
+
if (!Number.isFinite(authAge) || authAge < 0) {
|
329
|
+
return true
|
330
|
+
}
|
331
|
+
|
332
|
+
/** in seconds */
|
333
|
+
const maxAge = parameters.max_age ?? client.metadata.default_max_age
|
334
|
+
|
335
|
+
if (maxAge != null && maxAge < this.authenticationMaxAge) {
|
336
|
+
return authAge >= maxAge
|
337
|
+
} else {
|
338
|
+
return authAge >= this.authenticationMaxAge
|
339
|
+
}
|
340
|
+
}
|
341
|
+
|
342
|
+
protected async authenticateClient(
|
343
|
+
client: Client,
|
344
|
+
endpoint: OAuthEndpointName,
|
345
|
+
credentials: OAuthClientIdentification,
|
346
|
+
): Promise<ClientAuth> {
|
347
|
+
const { clientAuth, nonce } = await client.verifyCredentials(
|
348
|
+
credentials,
|
349
|
+
endpoint,
|
350
|
+
{ audience: this.issuer },
|
351
|
+
)
|
352
|
+
|
353
|
+
if (nonce != null) {
|
354
|
+
const unique = await this.replayManager.uniqueAuth(nonce, client.id)
|
355
|
+
if (!unique) {
|
356
|
+
throw new InvalidClientError(`${clientAuth.method} jti reused`)
|
357
|
+
}
|
358
|
+
}
|
359
|
+
|
360
|
+
return clientAuth
|
361
|
+
}
|
362
|
+
|
363
|
+
protected async decodeJAR(
|
364
|
+
client: Client,
|
365
|
+
input: AuthorizationRequestJar,
|
366
|
+
): Promise<
|
367
|
+
| {
|
368
|
+
payload: OAuthAuthenticationRequestParameters
|
369
|
+
}
|
370
|
+
| {
|
371
|
+
payload: OAuthAuthenticationRequestParameters
|
372
|
+
protectedHeader: { kid: string; alg: string }
|
373
|
+
jkt: string
|
374
|
+
}
|
375
|
+
> {
|
376
|
+
const result = await client.decodeRequestObject(input.request)
|
377
|
+
const payload = oauthAuthenticationRequestParametersSchema.parse(
|
378
|
+
result.payload,
|
379
|
+
)
|
380
|
+
|
381
|
+
if (!result.payload.jti) {
|
382
|
+
throw new InvalidParametersError(
|
383
|
+
payload,
|
384
|
+
'Request object must contain a jti claim',
|
385
|
+
)
|
386
|
+
}
|
387
|
+
|
388
|
+
if (!(await this.replayManager.uniqueJar(result.payload.jti, client.id))) {
|
389
|
+
throw new InvalidParametersError(
|
390
|
+
payload,
|
391
|
+
'Request object jti is not unique',
|
392
|
+
)
|
393
|
+
}
|
394
|
+
|
395
|
+
if ('protectedHeader' in result) {
|
396
|
+
if (!result.protectedHeader.kid) {
|
397
|
+
throw new InvalidParametersError(payload, 'Missing "kid" in header')
|
398
|
+
}
|
399
|
+
|
400
|
+
return {
|
401
|
+
jkt: await authJwkThumbprint(result.key),
|
402
|
+
payload,
|
403
|
+
protectedHeader: result.protectedHeader as {
|
404
|
+
alg: string
|
405
|
+
kid: string
|
406
|
+
},
|
407
|
+
}
|
408
|
+
}
|
409
|
+
|
410
|
+
if ('header' in result) {
|
411
|
+
return {
|
412
|
+
payload,
|
413
|
+
}
|
414
|
+
}
|
415
|
+
|
416
|
+
// Should never happen
|
417
|
+
throw new Error('Invalid request object')
|
418
|
+
}
|
419
|
+
|
420
|
+
/**
|
421
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc9126}
|
422
|
+
*/
|
423
|
+
protected async pushedAuthorizationRequest(
|
424
|
+
input: PushedAuthorizationRequest,
|
425
|
+
dpopJkt: null | string,
|
426
|
+
) {
|
427
|
+
try {
|
428
|
+
const client = await this.clientManager.getClient(input.client_id)
|
429
|
+
const clientAuth = await this.authenticateClient(
|
430
|
+
client,
|
431
|
+
'pushed_authorization_request',
|
432
|
+
input,
|
433
|
+
)
|
434
|
+
|
435
|
+
const { payload: parameters } =
|
436
|
+
'request' in input // Handle JAR
|
437
|
+
? await this.decodeJAR(client, input)
|
438
|
+
: { payload: input }
|
439
|
+
|
440
|
+
const { uri, expiresAt } =
|
441
|
+
await this.requestManager.createAuthorizationRequest(
|
442
|
+
client,
|
443
|
+
clientAuth,
|
444
|
+
parameters,
|
445
|
+
null,
|
446
|
+
dpopJkt,
|
447
|
+
)
|
448
|
+
|
449
|
+
return {
|
450
|
+
request_uri: uri,
|
451
|
+
expires_in: dateToRelativeSeconds(expiresAt),
|
452
|
+
}
|
453
|
+
} catch (err) {
|
454
|
+
// https://datatracker.ietf.org/doc/html/rfc9126#section-2.3-1
|
455
|
+
// > Since initial processing of the pushed authorization request does not
|
456
|
+
// > involve resource owner interaction, error codes related to user
|
457
|
+
// > interaction, such as consent_required defined by [OIDC], are never
|
458
|
+
// > returned.
|
459
|
+
if (err instanceof AccessDeniedError) {
|
460
|
+
throw new InvalidRequestError(err.error_description, err)
|
461
|
+
}
|
462
|
+
throw err
|
463
|
+
}
|
464
|
+
}
|
465
|
+
|
466
|
+
private async loadAuthorizationRequest(
|
467
|
+
client: Client,
|
468
|
+
deviceId: DeviceId,
|
469
|
+
input: AuthorizationRequestQuery,
|
470
|
+
): Promise<RequestInfo> {
|
471
|
+
// Load PAR
|
472
|
+
if ('request_uri' in input) {
|
473
|
+
return this.requestManager.get(input.request_uri, client.id, deviceId)
|
474
|
+
}
|
475
|
+
|
476
|
+
// Handle JAR
|
477
|
+
if ('request' in input) {
|
478
|
+
const requestObject = await this.decodeJAR(client, input)
|
479
|
+
|
480
|
+
if ('protectedHeader' in requestObject && requestObject.protectedHeader) {
|
481
|
+
// Allow using signed JAR during "/authorize" as client authentication.
|
482
|
+
// This allows clients to skip PAR to initiate trusted sessions.
|
483
|
+
const clientAuth: ClientAuth = {
|
484
|
+
method: CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
485
|
+
kid: requestObject.protectedHeader.kid,
|
486
|
+
alg: requestObject.protectedHeader.alg,
|
487
|
+
jkt: requestObject.jkt,
|
488
|
+
}
|
489
|
+
|
490
|
+
return this.requestManager.createAuthorizationRequest(
|
491
|
+
client,
|
492
|
+
clientAuth,
|
493
|
+
requestObject.payload,
|
494
|
+
deviceId,
|
495
|
+
null,
|
496
|
+
)
|
497
|
+
}
|
498
|
+
|
499
|
+
return this.requestManager.createAuthorizationRequest(
|
500
|
+
client,
|
501
|
+
{ method: 'none' },
|
502
|
+
requestObject.payload,
|
503
|
+
deviceId,
|
504
|
+
null,
|
505
|
+
)
|
506
|
+
}
|
507
|
+
|
508
|
+
return this.requestManager.createAuthorizationRequest(
|
509
|
+
client,
|
510
|
+
{ method: 'none' },
|
511
|
+
input,
|
512
|
+
deviceId,
|
513
|
+
null,
|
514
|
+
)
|
515
|
+
}
|
516
|
+
|
517
|
+
private async deleteRequest(
|
518
|
+
uri: RequestUri,
|
519
|
+
parameters: OAuthAuthenticationRequestParameters,
|
520
|
+
) {
|
521
|
+
try {
|
522
|
+
await this.requestManager.delete(uri)
|
523
|
+
} catch (err) {
|
524
|
+
throw AccessDeniedError.from(parameters, err)
|
525
|
+
}
|
526
|
+
}
|
527
|
+
|
528
|
+
protected async authorize(
|
529
|
+
deviceId: DeviceId,
|
530
|
+
input: AuthorizationRequestQuery,
|
531
|
+
): Promise<AuthorizationResultRedirect | AuthorizationResultAuthorize> {
|
532
|
+
const { issuer } = this
|
533
|
+
const client = await this.clientManager.getClient(input.client_id)
|
534
|
+
|
535
|
+
try {
|
536
|
+
const { uri, parameters, clientAuth } =
|
537
|
+
await this.loadAuthorizationRequest(client, deviceId, input)
|
538
|
+
|
539
|
+
try {
|
540
|
+
const sessions = await this.getSessions(
|
541
|
+
client,
|
542
|
+
clientAuth,
|
543
|
+
deviceId,
|
544
|
+
parameters,
|
545
|
+
)
|
546
|
+
|
547
|
+
if (parameters.prompt === 'none') {
|
548
|
+
const ssoSessions = sessions.filter((s) => s.matchesHint)
|
549
|
+
if (ssoSessions.length > 1) {
|
550
|
+
throw new AccountSelectionRequiredError(parameters)
|
551
|
+
}
|
552
|
+
if (ssoSessions.length < 1) {
|
553
|
+
throw new LoginRequiredError(parameters)
|
554
|
+
}
|
555
|
+
|
556
|
+
const ssoSession = ssoSessions[0]!
|
557
|
+
if (ssoSession.loginRequired) {
|
558
|
+
throw new LoginRequiredError(parameters)
|
559
|
+
}
|
560
|
+
if (ssoSession.consentRequired) {
|
561
|
+
throw new ConsentRequiredError(parameters)
|
562
|
+
}
|
563
|
+
|
564
|
+
const redirect = await this.requestManager.setAuthorized(
|
565
|
+
client,
|
566
|
+
uri,
|
567
|
+
deviceId,
|
568
|
+
ssoSession.account,
|
569
|
+
ssoSession.info,
|
570
|
+
)
|
571
|
+
|
572
|
+
return { issuer, client, parameters, redirect }
|
573
|
+
}
|
574
|
+
|
575
|
+
// Automatic SSO when a did was provided
|
576
|
+
if (parameters.prompt == null && parameters.login_hint != null) {
|
577
|
+
const ssoSessions = sessions.filter((s) => s.matchesHint)
|
578
|
+
if (ssoSessions.length === 1) {
|
579
|
+
const ssoSession = ssoSessions[0]!
|
580
|
+
if (!ssoSession.loginRequired && !ssoSession.consentRequired) {
|
581
|
+
const redirect = await this.requestManager.setAuthorized(
|
582
|
+
client,
|
583
|
+
uri,
|
584
|
+
deviceId,
|
585
|
+
ssoSession.account,
|
586
|
+
ssoSession.info,
|
587
|
+
)
|
588
|
+
|
589
|
+
return { issuer, client, parameters, redirect }
|
590
|
+
}
|
591
|
+
}
|
592
|
+
}
|
593
|
+
|
594
|
+
return {
|
595
|
+
issuer,
|
596
|
+
client,
|
597
|
+
parameters,
|
598
|
+
authorize: { uri, sessions },
|
599
|
+
}
|
600
|
+
} catch (err) {
|
601
|
+
await this.deleteRequest(uri, parameters)
|
602
|
+
|
603
|
+
// Transform into an AccessDeniedError to allow redirecting the user
|
604
|
+
// to the client with the error details.
|
605
|
+
throw AccessDeniedError.from(parameters, err)
|
606
|
+
}
|
607
|
+
} catch (err) {
|
608
|
+
if (err instanceof AccessDeniedError) {
|
609
|
+
return {
|
610
|
+
issuer,
|
611
|
+
client,
|
612
|
+
parameters: err.parameters,
|
613
|
+
redirect: err.toJSON(),
|
614
|
+
}
|
615
|
+
}
|
616
|
+
|
617
|
+
throw err
|
618
|
+
}
|
619
|
+
}
|
620
|
+
|
621
|
+
protected async getSessions(
|
622
|
+
client: Client,
|
623
|
+
clientAuth: ClientAuth,
|
624
|
+
deviceId: DeviceId,
|
625
|
+
parameters: OAuthAuthenticationRequestParameters,
|
626
|
+
): Promise<
|
627
|
+
{
|
628
|
+
account: Account
|
629
|
+
info: DeviceAccountInfo
|
630
|
+
|
631
|
+
selected: boolean
|
632
|
+
loginRequired: boolean
|
633
|
+
consentRequired: boolean
|
634
|
+
|
635
|
+
matchesHint: boolean
|
636
|
+
}[]
|
637
|
+
> {
|
638
|
+
const accounts = await this.accountManager.list(deviceId)
|
639
|
+
|
640
|
+
return accounts.map(({ account, info }) => ({
|
641
|
+
account,
|
642
|
+
info,
|
643
|
+
|
644
|
+
selected:
|
645
|
+
parameters.prompt !== 'select_account' &&
|
646
|
+
parameters.login_hint === account.sub,
|
647
|
+
loginRequired:
|
648
|
+
parameters.prompt === 'login' ||
|
649
|
+
this.loginRequired(client, parameters, info),
|
650
|
+
consentRequired:
|
651
|
+
parameters.prompt === 'consent' ||
|
652
|
+
!info.authorizedClients.includes(client.id),
|
653
|
+
|
654
|
+
matchesHint:
|
655
|
+
parameters.login_hint === account.sub || parameters.login_hint == null,
|
656
|
+
}))
|
657
|
+
}
|
658
|
+
|
659
|
+
protected async signIn(
|
660
|
+
deviceId: DeviceId,
|
661
|
+
credentials: LoginCredentials,
|
662
|
+
): Promise<AccountInfo> {
|
663
|
+
return this.accountManager.signIn(credentials, deviceId)
|
664
|
+
}
|
665
|
+
|
666
|
+
protected async acceptRequest(
|
667
|
+
deviceId: DeviceId,
|
668
|
+
uri: RequestUri,
|
669
|
+
clientId: ClientId,
|
670
|
+
sub: string,
|
671
|
+
): Promise<AuthorizationResultRedirect> {
|
672
|
+
const { issuer } = this
|
673
|
+
const client = await this.clientManager.getClient(clientId)
|
674
|
+
|
675
|
+
try {
|
676
|
+
const { parameters, clientAuth } = await this.requestManager.get(
|
677
|
+
uri,
|
678
|
+
clientId,
|
679
|
+
deviceId,
|
680
|
+
)
|
681
|
+
|
682
|
+
try {
|
683
|
+
const { account, info } = await this.accountManager.get(deviceId, sub)
|
684
|
+
|
685
|
+
// The user is trying to authorize without a fresh login
|
686
|
+
if (this.loginRequired(client, parameters, info)) {
|
687
|
+
throw new LoginRequiredError(
|
688
|
+
parameters,
|
689
|
+
'Account authentication required.',
|
690
|
+
)
|
691
|
+
}
|
692
|
+
|
693
|
+
const redirect = await this.requestManager.setAuthorized(
|
694
|
+
client,
|
695
|
+
uri,
|
696
|
+
deviceId,
|
697
|
+
account,
|
698
|
+
info,
|
699
|
+
)
|
700
|
+
|
701
|
+
await this.accountManager.addAuthorizedClient(
|
702
|
+
deviceId,
|
703
|
+
account,
|
704
|
+
client,
|
705
|
+
clientAuth,
|
706
|
+
)
|
707
|
+
|
708
|
+
return { issuer, client, parameters, redirect }
|
709
|
+
} catch (err) {
|
710
|
+
await this.deleteRequest(uri, parameters)
|
711
|
+
|
712
|
+
// throw AccessDeniedError.from(parameters, err)
|
713
|
+
throw err
|
714
|
+
}
|
715
|
+
} catch (err) {
|
716
|
+
if (err instanceof AccessDeniedError) {
|
717
|
+
const { parameters } = err
|
718
|
+
return { issuer, client, parameters, redirect: err.toJSON() }
|
719
|
+
}
|
720
|
+
|
721
|
+
throw err
|
722
|
+
}
|
723
|
+
}
|
724
|
+
|
725
|
+
protected async rejectRequest(
|
726
|
+
deviceId: DeviceId,
|
727
|
+
uri: RequestUri,
|
728
|
+
clientId: ClientId,
|
729
|
+
): Promise<AuthorizationResultRedirect> {
|
730
|
+
try {
|
731
|
+
const { parameters } = await this.requestManager.get(
|
732
|
+
uri,
|
733
|
+
clientId,
|
734
|
+
deviceId,
|
735
|
+
)
|
736
|
+
|
737
|
+
await this.deleteRequest(uri, parameters)
|
738
|
+
|
739
|
+
// Trigger redirect (see catch block)
|
740
|
+
throw new AccessDeniedError(parameters, 'Access denied')
|
741
|
+
} catch (err) {
|
742
|
+
if (err instanceof AccessDeniedError) {
|
743
|
+
return {
|
744
|
+
issuer: this.issuer,
|
745
|
+
client: await this.clientManager.getClient(clientId),
|
746
|
+
parameters: err.parameters,
|
747
|
+
redirect: err.toJSON(),
|
748
|
+
}
|
749
|
+
}
|
750
|
+
|
751
|
+
throw err
|
752
|
+
}
|
753
|
+
}
|
754
|
+
|
755
|
+
protected async token(
|
756
|
+
input: TokenRequest,
|
757
|
+
dpopJkt: null | string,
|
758
|
+
): Promise<OAuthTokenResponse> {
|
759
|
+
const client = await this.clientManager.getClient(input.client_id)
|
760
|
+
const clientAuth = await this.authenticateClient(client, 'token', input)
|
761
|
+
|
762
|
+
if (!client.metadata.grant_types.includes(input.grant_type)) {
|
763
|
+
throw new InvalidGrantError(
|
764
|
+
`"${input.grant_type}" grant type is not allowed for this client`,
|
765
|
+
)
|
766
|
+
}
|
767
|
+
|
768
|
+
if (input.grant_type === 'authorization_code') {
|
769
|
+
return this.codeGrant(client, clientAuth, input, dpopJkt)
|
770
|
+
}
|
771
|
+
|
772
|
+
if (input.grant_type === 'refresh_token') {
|
773
|
+
return this.refreshTokenGrant(client, clientAuth, input, dpopJkt)
|
774
|
+
}
|
775
|
+
|
776
|
+
throw new InvalidGrantError(
|
777
|
+
// @ts-expect-error: fool proof
|
778
|
+
`Grant type "${input.grant_type}" not supported`,
|
779
|
+
)
|
780
|
+
}
|
781
|
+
|
782
|
+
protected async codeGrant(
|
783
|
+
client: Client,
|
784
|
+
clientAuth: ClientAuth,
|
785
|
+
input: CodeGrantRequest,
|
786
|
+
dpopJkt: null | string,
|
787
|
+
): Promise<OAuthTokenResponse> {
|
788
|
+
try {
|
789
|
+
const { sub, deviceId, parameters } = await this.requestManager.findCode(
|
790
|
+
client,
|
791
|
+
clientAuth,
|
792
|
+
input.code,
|
793
|
+
)
|
794
|
+
|
795
|
+
const { account, info } = await this.accountManager.get(deviceId, sub)
|
796
|
+
|
797
|
+
return await this.tokenManager.create(
|
798
|
+
client,
|
799
|
+
clientAuth,
|
800
|
+
account,
|
801
|
+
{ id: deviceId, info },
|
802
|
+
parameters,
|
803
|
+
input,
|
804
|
+
dpopJkt,
|
805
|
+
)
|
806
|
+
} catch (err) {
|
807
|
+
// If a token is replayed, requestManager.findCode will throw. In that
|
808
|
+
// case, we need to revoke any token that was issued for this code.
|
809
|
+
|
810
|
+
await this.tokenManager.revoke(input.code)
|
811
|
+
|
812
|
+
// @TODO (?) in order to protect the user, we should maybe also mark the
|
813
|
+
// account-device association as expired ?
|
814
|
+
|
815
|
+
throw err
|
816
|
+
}
|
817
|
+
}
|
818
|
+
|
819
|
+
async refreshTokenGrant(
|
820
|
+
client: Client,
|
821
|
+
clientAuth: ClientAuth,
|
822
|
+
input: RefreshGrantRequest,
|
823
|
+
dpopJkt: null | string,
|
824
|
+
): Promise<OAuthTokenResponse> {
|
825
|
+
return this.tokenManager.refresh(client, clientAuth, input, dpopJkt)
|
826
|
+
}
|
827
|
+
|
828
|
+
/**
|
829
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7009#section-2.1 rfc7009}
|
830
|
+
*/
|
831
|
+
protected async revoke(input: Revoke) {
|
832
|
+
// @TODO this should also remove the account-device association (or, at
|
833
|
+
// least, mark it as expired)
|
834
|
+
await this.tokenManager.revoke(input.token)
|
835
|
+
}
|
836
|
+
|
837
|
+
/**
|
838
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7662#section-2.1 rfc7662}
|
839
|
+
*/
|
840
|
+
protected async introspect(
|
841
|
+
input: Introspect,
|
842
|
+
): Promise<IntrospectionResponse> {
|
843
|
+
const client = await this.clientManager.getClient(input.client_id)
|
844
|
+
const clientAuth = await this.authenticateClient(
|
845
|
+
client,
|
846
|
+
'introspection',
|
847
|
+
input,
|
848
|
+
)
|
849
|
+
|
850
|
+
// RFC7662 states the following:
|
851
|
+
//
|
852
|
+
// > To prevent token scanning attacks, the endpoint MUST also require some
|
853
|
+
// > form of authorization to access this endpoint, such as client
|
854
|
+
// > authentication as described in OAuth 2.0 [RFC6749] or a separate OAuth
|
855
|
+
// > 2.0 access token such as the bearer token described in OAuth 2.0 Bearer
|
856
|
+
// > Token Usage [RFC6750]. The methods of managing and validating these
|
857
|
+
// > authentication credentials are out of scope of this specification.
|
858
|
+
if (clientAuth.method === 'none') {
|
859
|
+
throw new UnauthorizedClientError('Client authentication required')
|
860
|
+
}
|
861
|
+
|
862
|
+
const start = Date.now()
|
863
|
+
try {
|
864
|
+
const tokenInfo = await this.tokenManager.clientTokenInfo(
|
865
|
+
client,
|
866
|
+
clientAuth,
|
867
|
+
input.token,
|
868
|
+
)
|
869
|
+
|
870
|
+
return {
|
871
|
+
active: true,
|
872
|
+
|
873
|
+
scope: tokenInfo.data.parameters.scope,
|
874
|
+
client_id: tokenInfo.data.clientId,
|
875
|
+
username: tokenInfo.account.preferred_username,
|
876
|
+
token_type: tokenInfo.data.parameters.dpop_jkt ? 'DPoP' : 'Bearer',
|
877
|
+
authorization_details: tokenInfo.data.details ?? undefined,
|
878
|
+
|
879
|
+
aud: tokenInfo.account.aud,
|
880
|
+
exp: dateToEpoch(tokenInfo.data.expiresAt),
|
881
|
+
iat: dateToEpoch(tokenInfo.data.updatedAt),
|
882
|
+
iss: this.signer.issuer,
|
883
|
+
jti: tokenInfo.id,
|
884
|
+
sub: tokenInfo.account.sub,
|
885
|
+
}
|
886
|
+
} catch (err) {
|
887
|
+
// Prevent brute force & timing attack (only for inactive tokens)
|
888
|
+
await new Promise((r) => setTimeout(r, 750 - (Date.now() - start)))
|
889
|
+
|
890
|
+
return {
|
891
|
+
active: false,
|
892
|
+
}
|
893
|
+
}
|
894
|
+
}
|
895
|
+
|
896
|
+
/**
|
897
|
+
* @see {@link https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.3.2 Successful UserInfo Response}
|
898
|
+
*/
|
899
|
+
protected async userinfo({ data, account }: TokenInfo): Promise<Userinfo> {
|
900
|
+
return {
|
901
|
+
...oidcPayload(data.parameters, account),
|
902
|
+
|
903
|
+
sub: account.sub,
|
904
|
+
|
905
|
+
client_id: data.clientId,
|
906
|
+
username: account.preferred_username,
|
907
|
+
}
|
908
|
+
}
|
909
|
+
|
910
|
+
protected async signUserinfo(userinfo: Userinfo): Promise<SignedJwt> {
|
911
|
+
const client = await this.clientManager.getClient(userinfo.client_id)
|
912
|
+
return this.signer.sign(
|
913
|
+
{
|
914
|
+
alg: client.metadata.userinfo_signed_response_alg,
|
915
|
+
typ: 'JWT',
|
916
|
+
},
|
917
|
+
userinfo,
|
918
|
+
)
|
919
|
+
}
|
920
|
+
|
921
|
+
protected override async authenticateToken(
|
922
|
+
tokenType: OAuthTokenType,
|
923
|
+
token: AccessToken,
|
924
|
+
dpopJkt: string | null,
|
925
|
+
verifyOptions?: VerifyTokenClaimsOptions,
|
926
|
+
) {
|
927
|
+
if (isTokenId(token)) {
|
928
|
+
this.assertTokenTypeAllowed(tokenType, AccessTokenType.id)
|
929
|
+
|
930
|
+
return this.tokenManager.authenticateTokenId(
|
931
|
+
tokenType,
|
932
|
+
token,
|
933
|
+
dpopJkt,
|
934
|
+
verifyOptions,
|
935
|
+
)
|
936
|
+
}
|
937
|
+
|
938
|
+
return super.authenticateToken(tokenType, token, dpopJkt, verifyOptions)
|
939
|
+
}
|
940
|
+
|
941
|
+
/**
|
942
|
+
* @returns An http request handler that can be used with node's http server
|
943
|
+
* or as a middleware with express / connect.
|
944
|
+
*/
|
945
|
+
public httpHandler<
|
946
|
+
T = void,
|
947
|
+
Req extends IncomingMessage = IncomingMessage,
|
948
|
+
Res extends ServerResponse = ServerResponse,
|
949
|
+
>(options?: RouterOptions<Req, Res>): Handler<T, Req, Res> {
|
950
|
+
const router = this.buildRouter<T, Req, Res>(options)
|
951
|
+
return router.buildHandler()
|
952
|
+
}
|
953
|
+
|
954
|
+
public buildRouter<
|
955
|
+
T = void,
|
956
|
+
Req extends IncomingMessage = IncomingMessage,
|
957
|
+
Res extends ServerResponse = ServerResponse,
|
958
|
+
>({
|
959
|
+
onError = process.env['NODE_ENV'] === 'development'
|
960
|
+
? (req, res, err, msg): void =>
|
961
|
+
console.error(`OAuthProvider error (${msg}):`, err)
|
962
|
+
: undefined,
|
963
|
+
}: RouterOptions<Req, Res> = {}) {
|
964
|
+
const deviceManager = new DeviceManager(this.deviceStore)
|
965
|
+
|
966
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
967
|
+
const server = this
|
968
|
+
const issuerUrl = new URL(server.issuer)
|
969
|
+
const issuerOrigin = issuerUrl.origin
|
970
|
+
const router = new Router<T, Req, Res>(issuerUrl)
|
971
|
+
|
972
|
+
// Utils
|
973
|
+
|
974
|
+
const csrfCookie = (uri: RequestUri) => `csrf-${uri}`
|
975
|
+
|
976
|
+
/**
|
977
|
+
* Creates a middleware that will serve static JSON content.
|
978
|
+
*/
|
979
|
+
const staticJson = (json: unknown): Middleware<void, Req, Res> =>
|
980
|
+
combineMiddlewares([
|
981
|
+
function (req, res, next) {
|
982
|
+
res.setHeader('Access-Control-Allow-Origin', '*')
|
983
|
+
res.setHeader('Cache-Control', 'max-age=300')
|
984
|
+
next()
|
985
|
+
},
|
986
|
+
staticJsonHandler(json),
|
987
|
+
])
|
988
|
+
|
989
|
+
/**
|
990
|
+
* Wrap an OAuth endpoint in a middleware that will set the appropriate
|
991
|
+
* response headers and format the response as JSON.
|
992
|
+
*/
|
993
|
+
const dynamicJson = <T, TReq extends Req, TRes extends Res, Json>(
|
994
|
+
buildJson: (this: T, req: TReq, res: TRes) => Json | Promise<Json>,
|
995
|
+
status?: number,
|
996
|
+
): Handler<T, TReq, TRes> =>
|
997
|
+
async function (req, res) {
|
998
|
+
res.setHeader('Access-Control-Allow-Origin', '*')
|
999
|
+
|
1000
|
+
// https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
|
1001
|
+
res.setHeader('Cache-Control', 'no-store')
|
1002
|
+
res.setHeader('Pragma', 'no-cache')
|
1003
|
+
|
1004
|
+
// https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
|
1005
|
+
const dpopNonce = server.nextDpopNonce()
|
1006
|
+
if (dpopNonce) {
|
1007
|
+
const name = 'DPoP-Nonce'
|
1008
|
+
res.setHeader(name, dpopNonce)
|
1009
|
+
res.appendHeader('Access-Control-Expose-Headers', name)
|
1010
|
+
}
|
1011
|
+
|
1012
|
+
try {
|
1013
|
+
const result = await buildJson.call(this, req, res)
|
1014
|
+
if (result !== undefined) writeJson(res, result, status)
|
1015
|
+
else if (!res.headersSent) res.writeHead(status ?? 204).end()
|
1016
|
+
} catch (err) {
|
1017
|
+
if (!res.headersSent) {
|
1018
|
+
if (err instanceof WWWAuthenticateError) {
|
1019
|
+
const name = 'WWW-Authenticate'
|
1020
|
+
res.setHeader(name, err.wwwAuthenticateHeader)
|
1021
|
+
res.appendHeader('Access-Control-Expose-Headers', name)
|
1022
|
+
}
|
1023
|
+
|
1024
|
+
writeJson(res, buildErrorPayload(err), buildErrorStatus(err))
|
1025
|
+
} else {
|
1026
|
+
res.destroy()
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
// OAuthError are used to build expected responses, so we don't log
|
1030
|
+
// them as errors.
|
1031
|
+
if (!(err instanceof OAuthError) || err.statusCode >= 500) {
|
1032
|
+
await onError?.(req, res, err, 'Unexpected error')
|
1033
|
+
}
|
1034
|
+
}
|
1035
|
+
}
|
1036
|
+
|
1037
|
+
//- Public OAuth endpoints
|
1038
|
+
|
1039
|
+
/*
|
1040
|
+
* Although OpenID compatibility is not required to implement the Atproto
|
1041
|
+
* OAuth2 specification, we do support OIDC discovery in this
|
1042
|
+
* implementation as we believe this may:
|
1043
|
+
* 1) Make the implementation of Atproto clients easier (since lots of
|
1044
|
+
* libraries support OIDC discovery)
|
1045
|
+
* 2) Allow self hosted PDS' to not implement authentication themselves
|
1046
|
+
* but rely on a trusted Atproto actor to act as their OIDC providers.
|
1047
|
+
* By supporting OIDC in the current implementation, Bluesky's
|
1048
|
+
* Authorization Server server can be used as an OIDC provider for
|
1049
|
+
* these users.
|
1050
|
+
*/
|
1051
|
+
router.get('/.well-known/openid-configuration', staticJson(server.metadata))
|
1052
|
+
|
1053
|
+
router.get(
|
1054
|
+
'/.well-known/oauth-authorization-server',
|
1055
|
+
staticJson(server.metadata),
|
1056
|
+
)
|
1057
|
+
|
1058
|
+
// CORS preflight
|
1059
|
+
router.options<{
|
1060
|
+
endpoint: 'jwks' | 'par' | 'token' | 'revoke' | 'introspect' | 'userinfo'
|
1061
|
+
}>(
|
1062
|
+
/^\/oauth\/(?<endpoint>jwks|par|token|revoke|introspect|userinfo)$/,
|
1063
|
+
function (req, res, _next) {
|
1064
|
+
res
|
1065
|
+
.writeHead(204, {
|
1066
|
+
'Access-Control-Allow-Origin': req.headers['origin'] || '*',
|
1067
|
+
'Access-Control-Allow-Methods':
|
1068
|
+
this.params.endpoint === 'jwks' ? 'GET' : 'POST',
|
1069
|
+
'Access-Control-Allow-Headers': 'Content-Type,Authorization,DPoP',
|
1070
|
+
'Access-Control-Max-Age': '86400', // 1 day
|
1071
|
+
})
|
1072
|
+
.end()
|
1073
|
+
},
|
1074
|
+
)
|
1075
|
+
|
1076
|
+
router.get('/oauth/jwks', staticJson(server.jwks))
|
1077
|
+
|
1078
|
+
router.post(
|
1079
|
+
'/oauth/par',
|
1080
|
+
dynamicJson(async function (req, _res) {
|
1081
|
+
const input = await validateRequestPayload(
|
1082
|
+
req,
|
1083
|
+
pushedAuthorizationRequestSchema,
|
1084
|
+
)
|
1085
|
+
|
1086
|
+
const dpopJkt = await server.checkDpopProof(
|
1087
|
+
req.headers['dpop'],
|
1088
|
+
req.method!,
|
1089
|
+
this.url,
|
1090
|
+
)
|
1091
|
+
|
1092
|
+
return server.pushedAuthorizationRequest(input, dpopJkt)
|
1093
|
+
}, 201),
|
1094
|
+
)
|
1095
|
+
|
1096
|
+
// https://datatracker.ietf.org/doc/html/rfc9126#section-2.3
|
1097
|
+
router.addRoute('*', '/oauth/par', (req, res) => {
|
1098
|
+
res.writeHead(405).end()
|
1099
|
+
})
|
1100
|
+
|
1101
|
+
router.post(
|
1102
|
+
'/oauth/token',
|
1103
|
+
dynamicJson(async function (req, _res) {
|
1104
|
+
const input = await validateRequestPayload(req, tokenRequestSchema)
|
1105
|
+
|
1106
|
+
const dpopJkt = await server.checkDpopProof(
|
1107
|
+
req.headers['dpop'],
|
1108
|
+
req.method!,
|
1109
|
+
this.url,
|
1110
|
+
)
|
1111
|
+
|
1112
|
+
return server.token(input, dpopJkt)
|
1113
|
+
}),
|
1114
|
+
)
|
1115
|
+
|
1116
|
+
router.post(
|
1117
|
+
'/oauth/revoke',
|
1118
|
+
dynamicJson(async function (req, res) {
|
1119
|
+
const input = await validateRequestPayload(req, revokeSchema)
|
1120
|
+
|
1121
|
+
try {
|
1122
|
+
await server.revoke(input)
|
1123
|
+
} catch (err) {
|
1124
|
+
onError?.(req, res, err, 'Failed to revoke token')
|
1125
|
+
}
|
1126
|
+
}),
|
1127
|
+
)
|
1128
|
+
|
1129
|
+
router.get(
|
1130
|
+
'/oauth/revoke',
|
1131
|
+
dynamicJson(async function (req, res) {
|
1132
|
+
validateFetchMode(req, res, ['navigate'])
|
1133
|
+
validateSameOrigin(req, res, issuerOrigin)
|
1134
|
+
|
1135
|
+
const query = Object.fromEntries(this.url.searchParams)
|
1136
|
+
const input = revokeSchema.parse(query, { path: ['query'] })
|
1137
|
+
|
1138
|
+
try {
|
1139
|
+
await server.revoke(input)
|
1140
|
+
} catch (err) {
|
1141
|
+
onError?.(req, res, err, 'Failed to revoke token')
|
1142
|
+
}
|
1143
|
+
|
1144
|
+
// Same as POST + redirect to callback URL
|
1145
|
+
// todo: generate JSONP response (if "callback" is provided)
|
1146
|
+
|
1147
|
+
throw new Error(
|
1148
|
+
'You are successfully logged out. Redirect not implemented',
|
1149
|
+
)
|
1150
|
+
}),
|
1151
|
+
)
|
1152
|
+
|
1153
|
+
router.post(
|
1154
|
+
'/oauth/introspect',
|
1155
|
+
dynamicJson(async function (req, _res) {
|
1156
|
+
const input = await validateRequestPayload(req, introspectSchema)
|
1157
|
+
return server.introspect(input)
|
1158
|
+
}),
|
1159
|
+
)
|
1160
|
+
|
1161
|
+
const userinfoBodySchema = z.object({
|
1162
|
+
access_token: signedJwtSchema.optional(),
|
1163
|
+
})
|
1164
|
+
|
1165
|
+
router.addRoute(
|
1166
|
+
['GET', 'POST'],
|
1167
|
+
'/oauth/userinfo',
|
1168
|
+
acceptMiddleware(
|
1169
|
+
async function (req, _res) {
|
1170
|
+
const body =
|
1171
|
+
req.method === 'POST'
|
1172
|
+
? await validateRequestPayload(req, userinfoBodySchema)
|
1173
|
+
: null
|
1174
|
+
|
1175
|
+
if (body?.access_token && req.headers['authorization']) {
|
1176
|
+
throw new InvalidRequestError(
|
1177
|
+
'access token must be provided in either the authorization header or the request body',
|
1178
|
+
)
|
1179
|
+
}
|
1180
|
+
|
1181
|
+
const auth = await server.authenticateRequest(
|
1182
|
+
req.method!,
|
1183
|
+
this.url,
|
1184
|
+
body?.access_token // Allow credentials to be parsed from body.
|
1185
|
+
? {
|
1186
|
+
authorization: `Bearer ${body.access_token}`,
|
1187
|
+
dpop: undefined, // DPoP can only be used with headers
|
1188
|
+
}
|
1189
|
+
: req.headers,
|
1190
|
+
{
|
1191
|
+
scope: ['profile'],
|
1192
|
+
},
|
1193
|
+
)
|
1194
|
+
|
1195
|
+
const tokenInfo: TokenInfo =
|
1196
|
+
'tokenInfo' in auth
|
1197
|
+
? (auth.tokenInfo as TokenInfo)
|
1198
|
+
: await server.tokenManager.getTokenInfo(
|
1199
|
+
auth.tokenType,
|
1200
|
+
auth.tokenId,
|
1201
|
+
)
|
1202
|
+
|
1203
|
+
return server.userinfo(tokenInfo)
|
1204
|
+
},
|
1205
|
+
{
|
1206
|
+
'': 'application/json',
|
1207
|
+
'application/json': dynamicJson(async function (_req, _res) {
|
1208
|
+
return this.data
|
1209
|
+
}),
|
1210
|
+
'application/jwt': dynamicJson(async function (_req, res) {
|
1211
|
+
const jwt = await server.signUserinfo(this.data)
|
1212
|
+
res.writeHead(200, { 'Content-Type': 'application/jwt' }).end(jwt)
|
1213
|
+
return undefined
|
1214
|
+
}),
|
1215
|
+
},
|
1216
|
+
),
|
1217
|
+
)
|
1218
|
+
|
1219
|
+
//- Private authorization endpoints
|
1220
|
+
|
1221
|
+
router.use(authorizeAssetsMiddleware())
|
1222
|
+
|
1223
|
+
router.get('/oauth/authorize', async function (req, res) {
|
1224
|
+
try {
|
1225
|
+
res.setHeader('Cache-Control', 'no-store')
|
1226
|
+
|
1227
|
+
validateFetchMode(req, res, ['navigate'])
|
1228
|
+
validateSameOrigin(req, res, issuerOrigin)
|
1229
|
+
|
1230
|
+
const query = Object.fromEntries(this.url.searchParams)
|
1231
|
+
const input = await authorizationRequestQuerySchema.parseAsync(query, {
|
1232
|
+
path: ['query'],
|
1233
|
+
})
|
1234
|
+
|
1235
|
+
const { deviceId } = await deviceManager.load(req, res)
|
1236
|
+
const data = await server.authorize(deviceId, input)
|
1237
|
+
|
1238
|
+
switch (true) {
|
1239
|
+
case 'redirect' in data: {
|
1240
|
+
return await sendAuthorizeRedirect(res, data)
|
1241
|
+
}
|
1242
|
+
case 'authorize' in data: {
|
1243
|
+
await setupCsrfToken(req, res, csrfCookie(data.authorize.uri))
|
1244
|
+
return await sendAuthorizePage(res, data, server.customization)
|
1245
|
+
}
|
1246
|
+
default: {
|
1247
|
+
// Should never happen
|
1248
|
+
throw new Error('Unexpected authorization result')
|
1249
|
+
}
|
1250
|
+
}
|
1251
|
+
} catch (err) {
|
1252
|
+
await onError?.(req, res, err, 'Failed to setup authorize')
|
1253
|
+
|
1254
|
+
if (!res.headersSent) {
|
1255
|
+
await sendErrorPage(res, err, server.customization)
|
1256
|
+
}
|
1257
|
+
}
|
1258
|
+
})
|
1259
|
+
|
1260
|
+
const signInPayloadSchema = z.object({
|
1261
|
+
csrf_token: z.string(),
|
1262
|
+
request_uri: requestUriSchema,
|
1263
|
+
client_id: clientIdSchema,
|
1264
|
+
credentials: z.object({
|
1265
|
+
username: z.string(),
|
1266
|
+
password: z.string(),
|
1267
|
+
remember: z.boolean().optional().default(false),
|
1268
|
+
}),
|
1269
|
+
})
|
1270
|
+
|
1271
|
+
router.post('/oauth/authorize/sign-in', async function (req, res) {
|
1272
|
+
validateFetchMode(req, res, ['same-origin'])
|
1273
|
+
validateSameOrigin(req, res, issuerOrigin)
|
1274
|
+
|
1275
|
+
const input = await validateRequestPayload(req, signInPayloadSchema)
|
1276
|
+
|
1277
|
+
validateReferer(req, res, {
|
1278
|
+
origin: issuerOrigin,
|
1279
|
+
pathname: '/oauth/authorize',
|
1280
|
+
})
|
1281
|
+
validateCsrfToken(
|
1282
|
+
req,
|
1283
|
+
res,
|
1284
|
+
input.csrf_token,
|
1285
|
+
csrfCookie(input.request_uri),
|
1286
|
+
)
|
1287
|
+
|
1288
|
+
const { deviceId } = await deviceManager.load(req, res)
|
1289
|
+
|
1290
|
+
const { account, info } = await server.signIn(deviceId, input.credentials)
|
1291
|
+
|
1292
|
+
// Prevent fixation attacks
|
1293
|
+
await deviceManager.rotate(req, res, deviceId)
|
1294
|
+
|
1295
|
+
return writeJson(res, {
|
1296
|
+
account,
|
1297
|
+
consentRequired: !info.authorizedClients.includes(input.client_id),
|
1298
|
+
})
|
1299
|
+
})
|
1300
|
+
|
1301
|
+
const acceptQuerySchema = z.object({
|
1302
|
+
csrf_token: z.string(),
|
1303
|
+
request_uri: requestUriSchema,
|
1304
|
+
client_id: clientIdSchema,
|
1305
|
+
account_sub: z.string(),
|
1306
|
+
})
|
1307
|
+
|
1308
|
+
router.get('/oauth/authorize/accept', async function (req, res) {
|
1309
|
+
try {
|
1310
|
+
res.setHeader('Cache-Control', 'no-store')
|
1311
|
+
|
1312
|
+
validateFetchMode(req, res, ['navigate'])
|
1313
|
+
validateSameOrigin(req, res, issuerOrigin)
|
1314
|
+
|
1315
|
+
const query = Object.fromEntries(this.url.searchParams)
|
1316
|
+
const input = await acceptQuerySchema.parseAsync(query, {
|
1317
|
+
path: ['query'],
|
1318
|
+
})
|
1319
|
+
|
1320
|
+
validateReferer(req, res, {
|
1321
|
+
origin: issuerOrigin,
|
1322
|
+
pathname: '/oauth/authorize',
|
1323
|
+
searchParams: [
|
1324
|
+
['request_uri', input.request_uri],
|
1325
|
+
['client_id', input.client_id],
|
1326
|
+
],
|
1327
|
+
})
|
1328
|
+
validateCsrfToken(
|
1329
|
+
req,
|
1330
|
+
res,
|
1331
|
+
input.csrf_token,
|
1332
|
+
csrfCookie(input.request_uri),
|
1333
|
+
true,
|
1334
|
+
)
|
1335
|
+
|
1336
|
+
const { deviceId } = await deviceManager.load(req, res)
|
1337
|
+
|
1338
|
+
const data = await server.acceptRequest(
|
1339
|
+
deviceId,
|
1340
|
+
input.request_uri,
|
1341
|
+
input.client_id,
|
1342
|
+
input.account_sub,
|
1343
|
+
)
|
1344
|
+
|
1345
|
+
return await sendAuthorizeRedirect(res, data)
|
1346
|
+
} catch (err) {
|
1347
|
+
await onError?.(req, res, err, 'Failed to accept authorization request')
|
1348
|
+
|
1349
|
+
if (!res.headersSent) {
|
1350
|
+
await sendErrorPage(res, err, server.customization)
|
1351
|
+
}
|
1352
|
+
}
|
1353
|
+
})
|
1354
|
+
|
1355
|
+
const rejectQuerySchema = z.object({
|
1356
|
+
csrf_token: z.string(),
|
1357
|
+
request_uri: requestUriSchema,
|
1358
|
+
client_id: clientIdSchema,
|
1359
|
+
})
|
1360
|
+
|
1361
|
+
router.get('/oauth/authorize/reject', async function (req, res) {
|
1362
|
+
try {
|
1363
|
+
res.setHeader('Cache-Control', 'no-store')
|
1364
|
+
|
1365
|
+
validateFetchMode(req, res, ['navigate'])
|
1366
|
+
validateSameOrigin(req, res, issuerOrigin)
|
1367
|
+
|
1368
|
+
const query = Object.fromEntries(this.url.searchParams)
|
1369
|
+
const input = await rejectQuerySchema.parseAsync(query, {
|
1370
|
+
path: ['query'],
|
1371
|
+
})
|
1372
|
+
|
1373
|
+
validateReferer(req, res, {
|
1374
|
+
origin: issuerOrigin,
|
1375
|
+
pathname: '/oauth/authorize',
|
1376
|
+
searchParams: [
|
1377
|
+
['request_uri', input.request_uri],
|
1378
|
+
['client_id', input.client_id],
|
1379
|
+
],
|
1380
|
+
})
|
1381
|
+
validateCsrfToken(
|
1382
|
+
req,
|
1383
|
+
res,
|
1384
|
+
input.csrf_token,
|
1385
|
+
csrfCookie(input.request_uri),
|
1386
|
+
true,
|
1387
|
+
)
|
1388
|
+
|
1389
|
+
const { deviceId } = await deviceManager.load(req, res)
|
1390
|
+
|
1391
|
+
const data = await server.rejectRequest(
|
1392
|
+
deviceId,
|
1393
|
+
input.request_uri,
|
1394
|
+
input.client_id,
|
1395
|
+
)
|
1396
|
+
|
1397
|
+
return await sendAuthorizeRedirect(res, data)
|
1398
|
+
} catch (err) {
|
1399
|
+
await onError?.(req, res, err, 'Failed to reject authorization request')
|
1400
|
+
|
1401
|
+
if (!res.headersSent) {
|
1402
|
+
await sendErrorPage(res, err, server.customization)
|
1403
|
+
}
|
1404
|
+
}
|
1405
|
+
})
|
1406
|
+
|
1407
|
+
return router
|
1408
|
+
}
|
1409
|
+
}
|