@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,591 @@
|
|
1
|
+
import { isSignedJwt, SignedJwt } from '@atproto/jwk'
|
2
|
+
import {
|
3
|
+
AccessToken,
|
4
|
+
CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
5
|
+
OAuthAuthenticationRequestParameters,
|
6
|
+
OAuthTokenResponse,
|
7
|
+
OAuthTokenType,
|
8
|
+
} from '@atproto/oauth-types'
|
9
|
+
import { createHash } from 'node:crypto'
|
10
|
+
|
11
|
+
import { AccessTokenType } from '../access-token/access-token-type.js'
|
12
|
+
import { DeviceAccountInfo } from '../account/account-store.js'
|
13
|
+
import { Account } from '../account/account.js'
|
14
|
+
import { ClientAuth } from '../client/client-auth.js'
|
15
|
+
import { Client } from '../client/client.js'
|
16
|
+
import {
|
17
|
+
AUTHENTICATED_REFRESH_INACTIVITY_TIMEOUT,
|
18
|
+
AUTHENTICATED_REFRESH_LIFETIME,
|
19
|
+
TOKEN_MAX_AGE,
|
20
|
+
UNAUTHENTICATED_REFRESH_INACTIVITY_TIMEOUT,
|
21
|
+
UNAUTHENTICATED_REFRESH_LIFETIME,
|
22
|
+
} from '../constants.js'
|
23
|
+
import { DeviceId } from '../device/device-id.js'
|
24
|
+
import { InvalidDpopKeyBindingError } from '../errors/invalid-dpop-key-binding-error.js'
|
25
|
+
import { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js'
|
26
|
+
import { InvalidGrantError } from '../errors/invalid-grant-error.js'
|
27
|
+
import { InvalidRequestError } from '../errors/invalid-request-error.js'
|
28
|
+
import { InvalidTokenError } from '../errors/invalid-token-error.js'
|
29
|
+
import { dateToEpoch, dateToRelativeSeconds } from '../lib/util/date.js'
|
30
|
+
import { compareRedirectUri } from '../lib/util/redirect-uri.js'
|
31
|
+
import { OAuthHooks } from '../oauth-hooks.js'
|
32
|
+
import { isCode } from '../request/code.js'
|
33
|
+
import { Signer } from '../signer/signer.js'
|
34
|
+
import { generateRefreshToken, isRefreshToken } from './refresh-token.js'
|
35
|
+
import { TokenClaims } from './token-claims.js'
|
36
|
+
import { TokenData } from './token-data.js'
|
37
|
+
import {
|
38
|
+
TokenId,
|
39
|
+
generateTokenId,
|
40
|
+
isTokenId,
|
41
|
+
tokenIdSchema,
|
42
|
+
} from './token-id.js'
|
43
|
+
import { TokenInfo, TokenStore } from './token-store.js'
|
44
|
+
import { CodeGrantRequest, RefreshGrantRequest } from './types.js'
|
45
|
+
import {
|
46
|
+
VerifyTokenClaimsOptions,
|
47
|
+
VerifyTokenClaimsResult,
|
48
|
+
verifyTokenClaims,
|
49
|
+
} from './verify-token-claims.js'
|
50
|
+
|
51
|
+
export type AuthenticateTokenIdResult = VerifyTokenClaimsResult & {
|
52
|
+
tokenInfo: TokenInfo
|
53
|
+
}
|
54
|
+
|
55
|
+
export class TokenManager {
|
56
|
+
constructor(
|
57
|
+
protected readonly store: TokenStore,
|
58
|
+
protected readonly signer: Signer,
|
59
|
+
protected readonly hooks: OAuthHooks,
|
60
|
+
protected readonly accessTokenType: AccessTokenType,
|
61
|
+
protected readonly tokenMaxAge = TOKEN_MAX_AGE,
|
62
|
+
) {}
|
63
|
+
|
64
|
+
protected createTokenExpiry(now = new Date()) {
|
65
|
+
return new Date(now.getTime() + this.tokenMaxAge)
|
66
|
+
}
|
67
|
+
|
68
|
+
protected useJwtAccessToken(account: Account) {
|
69
|
+
if (this.accessTokenType === AccessTokenType.auto) {
|
70
|
+
return this.signer.issuer !== account.aud
|
71
|
+
}
|
72
|
+
|
73
|
+
return this.accessTokenType === AccessTokenType.jwt
|
74
|
+
}
|
75
|
+
|
76
|
+
async create(
|
77
|
+
client: Client,
|
78
|
+
clientAuth: ClientAuth,
|
79
|
+
account: Account,
|
80
|
+
device: null | { id: DeviceId; info: DeviceAccountInfo },
|
81
|
+
parameters: OAuthAuthenticationRequestParameters,
|
82
|
+
input: CodeGrantRequest,
|
83
|
+
dpopJkt: null | string,
|
84
|
+
): Promise<OAuthTokenResponse> {
|
85
|
+
if (client.metadata.dpop_bound_access_tokens && !dpopJkt) {
|
86
|
+
throw new InvalidDpopProofError('DPoP proof required')
|
87
|
+
}
|
88
|
+
|
89
|
+
if (!parameters.dpop_jkt) {
|
90
|
+
if (dpopJkt) parameters = { ...parameters, dpop_jkt: dpopJkt }
|
91
|
+
} else if (!dpopJkt) {
|
92
|
+
throw new InvalidDpopProofError('DPoP proof required')
|
93
|
+
} else if (parameters.dpop_jkt !== dpopJkt) {
|
94
|
+
throw new InvalidDpopKeyBindingError()
|
95
|
+
}
|
96
|
+
|
97
|
+
if (clientAuth.method === CLIENT_ASSERTION_TYPE_JWT_BEARER) {
|
98
|
+
// Clients **must not** use their private key to sign DPoP proofs.
|
99
|
+
if (parameters.dpop_jkt && clientAuth.jkt === parameters.dpop_jkt) {
|
100
|
+
throw new InvalidRequestError(
|
101
|
+
'The DPoP proof must be signed with a different key than the client assertion',
|
102
|
+
)
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
if (!client.metadata.grant_types.includes(input.grant_type)) {
|
107
|
+
throw new InvalidGrantError(
|
108
|
+
`This client is not allowed to use the "${input.grant_type}" grant type`,
|
109
|
+
)
|
110
|
+
}
|
111
|
+
|
112
|
+
switch (input.grant_type) {
|
113
|
+
case 'authorization_code':
|
114
|
+
if (!parameters.code_challenge || !parameters.code_challenge_method) {
|
115
|
+
throw new InvalidGrantError('PKCE is required')
|
116
|
+
}
|
117
|
+
|
118
|
+
if (!parameters.redirect_uri) {
|
119
|
+
const redirect_uri = client.metadata.redirect_uris.find((uri) =>
|
120
|
+
compareRedirectUri(uri, input.redirect_uri),
|
121
|
+
)
|
122
|
+
if (redirect_uri) {
|
123
|
+
parameters = { ...parameters, redirect_uri }
|
124
|
+
} else {
|
125
|
+
throw new InvalidGrantError(`Invalid redirect_uri`)
|
126
|
+
}
|
127
|
+
} else if (parameters.redirect_uri !== input.redirect_uri) {
|
128
|
+
throw new InvalidGrantError(
|
129
|
+
'This code was issued for another redirect_uri',
|
130
|
+
)
|
131
|
+
}
|
132
|
+
|
133
|
+
break
|
134
|
+
|
135
|
+
default:
|
136
|
+
throw new Error(`Unsupported grant type "${input.grant_type}"`)
|
137
|
+
}
|
138
|
+
|
139
|
+
if (parameters.code_challenge) {
|
140
|
+
if (!('code_verifier' in input) || !input.code_verifier) {
|
141
|
+
throw new InvalidGrantError('code_verifier is required')
|
142
|
+
}
|
143
|
+
switch (parameters.code_challenge_method) {
|
144
|
+
case undefined: // Default is "plain" (per spec)
|
145
|
+
case 'plain': {
|
146
|
+
if (parameters.code_challenge !== input.code_verifier) {
|
147
|
+
throw new InvalidGrantError('Invalid code_verifier')
|
148
|
+
}
|
149
|
+
break
|
150
|
+
}
|
151
|
+
case 'S256': {
|
152
|
+
// Because the code_challenge is base64url-encoded, we will decode
|
153
|
+
// it in order to compare based on bytes.
|
154
|
+
const inputChallenge = Buffer.from(
|
155
|
+
parameters.code_challenge,
|
156
|
+
'base64',
|
157
|
+
)
|
158
|
+
const computedChallenge = createHash('sha256')
|
159
|
+
.update(input.code_verifier)
|
160
|
+
.digest()
|
161
|
+
if (inputChallenge.compare(computedChallenge) !== 0) {
|
162
|
+
throw new InvalidGrantError('Invalid code_verifier')
|
163
|
+
}
|
164
|
+
break
|
165
|
+
}
|
166
|
+
default: {
|
167
|
+
throw new InvalidRequestError(
|
168
|
+
`Unsupported code_challenge_method ${parameters.code_challenge_method}`,
|
169
|
+
)
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
const code = 'code' in input ? input.code : undefined
|
175
|
+
if (code) {
|
176
|
+
const tokenInfo = await this.store.findTokenByCode(code)
|
177
|
+
if (tokenInfo) {
|
178
|
+
await this.store.deleteToken(tokenInfo.id)
|
179
|
+
throw new InvalidGrantError(`Code replayed`)
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
const tokenId = await generateTokenId()
|
184
|
+
const scopes = parameters.scope?.split(' ')
|
185
|
+
const refreshToken = scopes?.includes('offline_access')
|
186
|
+
? await generateRefreshToken()
|
187
|
+
: undefined
|
188
|
+
|
189
|
+
const now = new Date()
|
190
|
+
const expiresAt = this.createTokenExpiry(now)
|
191
|
+
|
192
|
+
const authorizationDetails = await this.hooks.onAuthorizationDetails?.call(
|
193
|
+
null,
|
194
|
+
{ client, parameters, account },
|
195
|
+
)
|
196
|
+
|
197
|
+
const tokenData: TokenData = {
|
198
|
+
createdAt: now,
|
199
|
+
updatedAt: now,
|
200
|
+
expiresAt,
|
201
|
+
clientId: client.id,
|
202
|
+
clientAuth,
|
203
|
+
deviceId: device?.id ?? null,
|
204
|
+
sub: account.sub,
|
205
|
+
parameters,
|
206
|
+
details: authorizationDetails ?? null,
|
207
|
+
code: code ?? null,
|
208
|
+
}
|
209
|
+
|
210
|
+
await this.store.createToken(tokenId, tokenData, refreshToken)
|
211
|
+
|
212
|
+
const accessToken: AccessToken = !this.useJwtAccessToken(account)
|
213
|
+
? tokenId
|
214
|
+
: await this.signer.accessToken(client, parameters, account, {
|
215
|
+
// We don't specify the alg here. We suppose the Resource server will be
|
216
|
+
// able to verify the token using any alg.
|
217
|
+
alg: undefined,
|
218
|
+
exp: expiresAt,
|
219
|
+
iat: now,
|
220
|
+
jti: tokenId,
|
221
|
+
cnf: parameters.dpop_jkt ? { jkt: parameters.dpop_jkt } : undefined,
|
222
|
+
authorization_details: authorizationDetails,
|
223
|
+
})
|
224
|
+
|
225
|
+
const idToken = scopes?.includes('openid')
|
226
|
+
? await this.signer.idToken(client, parameters, account, {
|
227
|
+
exp: expiresAt,
|
228
|
+
iat: now,
|
229
|
+
// If there is no deviceInfo, we are in a "password_grant" context
|
230
|
+
auth_time: device?.info.authenticatedAt || new Date(),
|
231
|
+
access_token: accessToken,
|
232
|
+
code,
|
233
|
+
})
|
234
|
+
: undefined
|
235
|
+
|
236
|
+
return this.buildTokenResponse(
|
237
|
+
client,
|
238
|
+
accessToken,
|
239
|
+
refreshToken,
|
240
|
+
idToken,
|
241
|
+
expiresAt,
|
242
|
+
parameters,
|
243
|
+
account,
|
244
|
+
authorizationDetails,
|
245
|
+
)
|
246
|
+
}
|
247
|
+
|
248
|
+
protected async buildTokenResponse(
|
249
|
+
client: Client,
|
250
|
+
accessToken: AccessToken,
|
251
|
+
refreshToken: string | undefined,
|
252
|
+
idToken: SignedJwt | undefined,
|
253
|
+
expiresAt: Date,
|
254
|
+
parameters: OAuthAuthenticationRequestParameters,
|
255
|
+
account: Account,
|
256
|
+
authorizationDetails: null | any,
|
257
|
+
): Promise<OAuthTokenResponse> {
|
258
|
+
const tokenResponse: OAuthTokenResponse = {
|
259
|
+
access_token: accessToken,
|
260
|
+
token_type: parameters.dpop_jkt ? 'DPoP' : 'Bearer',
|
261
|
+
refresh_token: refreshToken,
|
262
|
+
id_token: idToken,
|
263
|
+
scope: parameters.scope ?? '',
|
264
|
+
authorization_details: authorizationDetails,
|
265
|
+
get expires_in() {
|
266
|
+
return dateToRelativeSeconds(expiresAt)
|
267
|
+
},
|
268
|
+
|
269
|
+
// ATPROTO extension: add the sub claim to the token response to allow
|
270
|
+
// clients to resolve the PDS url (audience) using the did resolution
|
271
|
+
// mechanism.
|
272
|
+
sub: account.sub,
|
273
|
+
}
|
274
|
+
|
275
|
+
await this.hooks.onTokenResponse?.call(null, tokenResponse, {
|
276
|
+
client,
|
277
|
+
parameters,
|
278
|
+
account,
|
279
|
+
})
|
280
|
+
|
281
|
+
return tokenResponse
|
282
|
+
}
|
283
|
+
|
284
|
+
protected async validateAccess(
|
285
|
+
client: Client,
|
286
|
+
clientAuth: ClientAuth,
|
287
|
+
tokenInfo: TokenInfo,
|
288
|
+
) {
|
289
|
+
if (tokenInfo.data.clientId !== client.id) {
|
290
|
+
throw new InvalidGrantError(`Token was not issued to this client`)
|
291
|
+
}
|
292
|
+
|
293
|
+
if (tokenInfo.info?.authorizedClients.includes(client.id) === false) {
|
294
|
+
throw new InvalidGrantError(`Client no longer trusted by user`)
|
295
|
+
}
|
296
|
+
|
297
|
+
if (tokenInfo.data.clientAuth.method !== clientAuth.method) {
|
298
|
+
throw new InvalidGrantError(`Client authentication method mismatch`)
|
299
|
+
}
|
300
|
+
|
301
|
+
if (!(await client.validateClientAuth(tokenInfo.data.clientAuth))) {
|
302
|
+
throw new InvalidGrantError(`Client authentication mismatch`)
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
async refresh(
|
307
|
+
client: Client,
|
308
|
+
clientAuth: ClientAuth,
|
309
|
+
input: RefreshGrantRequest,
|
310
|
+
dpopJkt: null | string,
|
311
|
+
): Promise<OAuthTokenResponse> {
|
312
|
+
const tokenInfo = await this.store.findTokenByRefreshToken(
|
313
|
+
input.refresh_token,
|
314
|
+
)
|
315
|
+
if (!tokenInfo?.currentRefreshToken) {
|
316
|
+
throw new InvalidGrantError(`Invalid refresh token`)
|
317
|
+
}
|
318
|
+
|
319
|
+
const { account, info, data } = tokenInfo
|
320
|
+
const { parameters } = data
|
321
|
+
|
322
|
+
try {
|
323
|
+
if (tokenInfo.currentRefreshToken !== input.refresh_token) {
|
324
|
+
throw new InvalidGrantError(`refresh token replayed`)
|
325
|
+
}
|
326
|
+
|
327
|
+
await this.validateAccess(client, clientAuth, tokenInfo)
|
328
|
+
|
329
|
+
if (parameters.dpop_jkt) {
|
330
|
+
if (!dpopJkt) {
|
331
|
+
throw new InvalidDpopProofError('DPoP proof required')
|
332
|
+
} else if (parameters.dpop_jkt !== dpopJkt) {
|
333
|
+
throw new InvalidDpopKeyBindingError()
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
const lastActivity = data.updatedAt
|
338
|
+
const inactivityTimeout =
|
339
|
+
clientAuth.method === 'none' && !client.info.isFirstParty
|
340
|
+
? UNAUTHENTICATED_REFRESH_INACTIVITY_TIMEOUT
|
341
|
+
: AUTHENTICATED_REFRESH_INACTIVITY_TIMEOUT
|
342
|
+
if (lastActivity.getTime() + inactivityTimeout < Date.now()) {
|
343
|
+
throw new InvalidGrantError(`Refresh token exceeded inactivity timeout`)
|
344
|
+
}
|
345
|
+
|
346
|
+
const lifetime =
|
347
|
+
clientAuth.method === 'none' && !client.info.isFirstParty
|
348
|
+
? UNAUTHENTICATED_REFRESH_LIFETIME
|
349
|
+
: AUTHENTICATED_REFRESH_LIFETIME
|
350
|
+
if (data.createdAt.getTime() + lifetime < Date.now()) {
|
351
|
+
throw new InvalidGrantError(`Refresh token expired`)
|
352
|
+
}
|
353
|
+
|
354
|
+
const authorization_details =
|
355
|
+
await this.hooks.onAuthorizationDetails?.call(null, {
|
356
|
+
client,
|
357
|
+
parameters,
|
358
|
+
account,
|
359
|
+
})
|
360
|
+
|
361
|
+
const nextTokenId = await generateTokenId()
|
362
|
+
const nextRefreshToken = await generateRefreshToken()
|
363
|
+
|
364
|
+
const now = new Date()
|
365
|
+
const expiresAt = this.createTokenExpiry(now)
|
366
|
+
|
367
|
+
await this.store.rotateToken(
|
368
|
+
tokenInfo.id,
|
369
|
+
nextTokenId,
|
370
|
+
nextRefreshToken,
|
371
|
+
{
|
372
|
+
updatedAt: now,
|
373
|
+
expiresAt,
|
374
|
+
// When clients rotate their public keys, we store the key that was
|
375
|
+
// used by the client to authenticate itself while requesting new
|
376
|
+
// tokens. The validateAccess() method will ensure that the client
|
377
|
+
// still advertises the key that was used to issue the previous
|
378
|
+
// refresh token. If a client stops advertising a key, all tokens
|
379
|
+
// bound to that key will no longer be be refreshable. This allows
|
380
|
+
// clients to proactively invalidate tokens when a key is compromised.
|
381
|
+
// Note that the original DPoP key cannot be rotated. This protects
|
382
|
+
// users in case the ownership of the client id changes. In the latter
|
383
|
+
// case, a malicious actor could still advertises the public keys of
|
384
|
+
// the previous owner, but the new owner would not be able to present
|
385
|
+
// a valid DPoP proof.
|
386
|
+
clientAuth,
|
387
|
+
},
|
388
|
+
)
|
389
|
+
|
390
|
+
const accessToken: AccessToken = !this.useJwtAccessToken(account)
|
391
|
+
? nextTokenId
|
392
|
+
: await this.signer.accessToken(client, parameters, account, {
|
393
|
+
// We don't specify the alg here. We suppose the Resource server will be
|
394
|
+
// able to verify the token using any alg.
|
395
|
+
alg: undefined,
|
396
|
+
exp: expiresAt,
|
397
|
+
iat: now,
|
398
|
+
jti: nextTokenId,
|
399
|
+
cnf: parameters.dpop_jkt ? { jkt: parameters.dpop_jkt } : undefined,
|
400
|
+
authorization_details,
|
401
|
+
})
|
402
|
+
|
403
|
+
// https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.3.3
|
404
|
+
//
|
405
|
+
// > In addition to the response parameters specified by OAuth 2.0, the
|
406
|
+
// > following parameters MUST be included in the response:
|
407
|
+
// > - id_token: ID Token value associated with the authenticated session.
|
408
|
+
const scopes = parameters.scope?.split(' ')
|
409
|
+
const idToken = scopes?.includes('openid')
|
410
|
+
? await this.signer.idToken(client, parameters, account, {
|
411
|
+
exp: expiresAt,
|
412
|
+
iat: now,
|
413
|
+
auth_time: info?.authenticatedAt,
|
414
|
+
access_token: accessToken,
|
415
|
+
})
|
416
|
+
: undefined
|
417
|
+
|
418
|
+
return this.buildTokenResponse(
|
419
|
+
client,
|
420
|
+
accessToken,
|
421
|
+
nextRefreshToken,
|
422
|
+
idToken,
|
423
|
+
expiresAt,
|
424
|
+
parameters,
|
425
|
+
account,
|
426
|
+
authorization_details,
|
427
|
+
)
|
428
|
+
} catch (err) {
|
429
|
+
if (err instanceof InvalidRequestError) {
|
430
|
+
// Consider the refresh token might be compromised if sanity checks
|
431
|
+
// failed.
|
432
|
+
await this.store.deleteToken(tokenInfo.id)
|
433
|
+
}
|
434
|
+
throw err
|
435
|
+
}
|
436
|
+
}
|
437
|
+
|
438
|
+
/**
|
439
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7009#section-2.2 | RFC7009 Section 2.2}
|
440
|
+
*/
|
441
|
+
async revoke(token: string): Promise<void> {
|
442
|
+
switch (true) {
|
443
|
+
case isTokenId(token): {
|
444
|
+
await this.store.deleteToken(token)
|
445
|
+
return
|
446
|
+
}
|
447
|
+
|
448
|
+
case isSignedJwt(token): {
|
449
|
+
const { payload } = await this.signer.verify(token, {
|
450
|
+
clockTolerance: Infinity,
|
451
|
+
})
|
452
|
+
const tokenId = tokenIdSchema.parse(payload.jti)
|
453
|
+
await this.store.deleteToken(tokenId)
|
454
|
+
return
|
455
|
+
}
|
456
|
+
|
457
|
+
case isRefreshToken(token): {
|
458
|
+
const tokenInfo = await this.store.findTokenByRefreshToken(token)
|
459
|
+
if (tokenInfo) await this.store.deleteToken(tokenInfo.id)
|
460
|
+
return
|
461
|
+
}
|
462
|
+
|
463
|
+
case isCode(token): {
|
464
|
+
const tokenInfo = await this.store.findTokenByCode(token)
|
465
|
+
if (tokenInfo) await this.store.deleteToken(tokenInfo.id)
|
466
|
+
return
|
467
|
+
}
|
468
|
+
|
469
|
+
default:
|
470
|
+
// No error should be returned if the token is not valid
|
471
|
+
return
|
472
|
+
}
|
473
|
+
}
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Allows an (authenticated) client to obtain information about a token.
|
477
|
+
*
|
478
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc7662 RFC7662}
|
479
|
+
*/
|
480
|
+
async clientTokenInfo(
|
481
|
+
client: Client,
|
482
|
+
clientAuth: ClientAuth,
|
483
|
+
token: string,
|
484
|
+
): Promise<TokenInfo> {
|
485
|
+
const tokenInfo = await this.findTokenInfo(token)
|
486
|
+
if (!tokenInfo) {
|
487
|
+
throw new InvalidGrantError(`Invalid token`)
|
488
|
+
}
|
489
|
+
|
490
|
+
try {
|
491
|
+
await this.validateAccess(client, clientAuth, tokenInfo)
|
492
|
+
} catch (err) {
|
493
|
+
await this.store.deleteToken(tokenInfo.id)
|
494
|
+
throw err
|
495
|
+
}
|
496
|
+
|
497
|
+
if (tokenInfo.data.expiresAt.getTime() < Date.now()) {
|
498
|
+
throw new InvalidGrantError(`Token expired`)
|
499
|
+
}
|
500
|
+
|
501
|
+
return tokenInfo
|
502
|
+
}
|
503
|
+
|
504
|
+
protected async findTokenInfo(token: string): Promise<TokenInfo | null> {
|
505
|
+
switch (true) {
|
506
|
+
case isTokenId(token):
|
507
|
+
return this.store.readToken(token)
|
508
|
+
|
509
|
+
case isSignedJwt(token): {
|
510
|
+
const { payload } = await this.signer
|
511
|
+
.verifyAccessToken(token)
|
512
|
+
.catch((_) => ({ payload: null }))
|
513
|
+
if (!payload) return null
|
514
|
+
|
515
|
+
const tokenInfo = await this.store.readToken(payload.jti)
|
516
|
+
if (!tokenInfo) return null
|
517
|
+
|
518
|
+
// Audience changed (e.g. user was moved to another resource server)
|
519
|
+
if (payload.aud !== tokenInfo.account.aud) {
|
520
|
+
return null
|
521
|
+
}
|
522
|
+
|
523
|
+
// Invalid store implementation ?
|
524
|
+
if (payload.sub !== tokenInfo.account.sub) {
|
525
|
+
throw new Error(
|
526
|
+
`Account sub (${tokenInfo.account.sub}) does not match token sub (${payload.sub})`,
|
527
|
+
)
|
528
|
+
}
|
529
|
+
|
530
|
+
return tokenInfo
|
531
|
+
}
|
532
|
+
|
533
|
+
case isRefreshToken(token): {
|
534
|
+
const tokenInfo = await this.store.findTokenByRefreshToken(token)
|
535
|
+
if (!tokenInfo?.currentRefreshToken) return null
|
536
|
+
if (tokenInfo.currentRefreshToken !== token) return null
|
537
|
+
return tokenInfo
|
538
|
+
}
|
539
|
+
|
540
|
+
default:
|
541
|
+
// Should never happen
|
542
|
+
return null
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
async getTokenInfo(tokenType: OAuthTokenType, tokenId: TokenId) {
|
547
|
+
const tokenInfo = await this.store.readToken(tokenId)
|
548
|
+
|
549
|
+
if (!tokenInfo) {
|
550
|
+
throw new InvalidTokenError(tokenType, `Invalid token`)
|
551
|
+
}
|
552
|
+
|
553
|
+
if (!(tokenInfo.data.expiresAt.getTime() > Date.now())) {
|
554
|
+
throw new InvalidTokenError(tokenType, `Token expired`)
|
555
|
+
}
|
556
|
+
|
557
|
+
return tokenInfo
|
558
|
+
}
|
559
|
+
|
560
|
+
async authenticateTokenId(
|
561
|
+
tokenType: OAuthTokenType,
|
562
|
+
token: TokenId,
|
563
|
+
dpopJkt: string | null,
|
564
|
+
verifyOptions?: VerifyTokenClaimsOptions,
|
565
|
+
): Promise<AuthenticateTokenIdResult> {
|
566
|
+
const tokenInfo = await this.getTokenInfo(tokenType, token)
|
567
|
+
const { parameters } = tokenInfo.data
|
568
|
+
|
569
|
+
// Construct a list of claim, as if the token was a JWT.
|
570
|
+
const claims: TokenClaims = {
|
571
|
+
aud: tokenInfo.account.aud,
|
572
|
+
sub: tokenInfo.account.sub,
|
573
|
+
exp: dateToEpoch(tokenInfo.data.expiresAt),
|
574
|
+
iat: dateToEpoch(tokenInfo.data.updatedAt),
|
575
|
+
scope: tokenInfo.data.parameters.scope,
|
576
|
+
client_id: tokenInfo.data.clientId,
|
577
|
+
cnf: parameters.dpop_jkt ? { jkt: parameters.dpop_jkt } : undefined,
|
578
|
+
}
|
579
|
+
|
580
|
+
const result = verifyTokenClaims(
|
581
|
+
token,
|
582
|
+
token,
|
583
|
+
tokenType,
|
584
|
+
dpopJkt,
|
585
|
+
claims,
|
586
|
+
verifyOptions,
|
587
|
+
)
|
588
|
+
|
589
|
+
return { ...result, tokenInfo }
|
590
|
+
}
|
591
|
+
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import { DeviceAccountInfo } from '../account/account-store.js'
|
2
|
+
import { Account } from '../account/account.js'
|
3
|
+
import { Awaitable } from '../lib/util/type.js'
|
4
|
+
import { Code } from '../request/code.js'
|
5
|
+
import { RefreshToken } from './refresh-token.js'
|
6
|
+
import { TokenData } from './token-data.js'
|
7
|
+
import { TokenId } from './token-id.js'
|
8
|
+
|
9
|
+
// Export all types needed to implement the TokenStore interface
|
10
|
+
export * from './token-id.js'
|
11
|
+
export * from './token-data.js'
|
12
|
+
export * from './refresh-token.js'
|
13
|
+
export type { Awaitable }
|
14
|
+
|
15
|
+
export type TokenInfo = {
|
16
|
+
id: TokenId
|
17
|
+
data: TokenData
|
18
|
+
account: Account
|
19
|
+
info?: DeviceAccountInfo
|
20
|
+
currentRefreshToken: null | RefreshToken
|
21
|
+
}
|
22
|
+
|
23
|
+
export type NewTokenData = Pick<
|
24
|
+
TokenData,
|
25
|
+
'clientAuth' | 'expiresAt' | 'updatedAt'
|
26
|
+
>
|
27
|
+
|
28
|
+
export interface TokenStore {
|
29
|
+
createToken(
|
30
|
+
tokenId: TokenId,
|
31
|
+
data: TokenData,
|
32
|
+
refreshToken?: RefreshToken,
|
33
|
+
): Awaitable<void>
|
34
|
+
|
35
|
+
readToken(tokenId: TokenId): Awaitable<null | TokenInfo>
|
36
|
+
|
37
|
+
deleteToken(tokenId: TokenId): Awaitable<void>
|
38
|
+
|
39
|
+
rotateToken(
|
40
|
+
tokenId: TokenId,
|
41
|
+
newTokenId: TokenId,
|
42
|
+
newRefreshToken: RefreshToken,
|
43
|
+
newData: NewTokenData,
|
44
|
+
): Awaitable<void>
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Find a token by its refresh token. Note that previous refresh tokens
|
48
|
+
* should also return the token. The data model is reponsible for storing
|
49
|
+
* old refresh tokens when a new one is issued.
|
50
|
+
*/
|
51
|
+
findTokenByRefreshToken(
|
52
|
+
refreshToken: RefreshToken,
|
53
|
+
): Awaitable<null | TokenInfo>
|
54
|
+
|
55
|
+
findTokenByCode(code: Code): Awaitable<null | TokenInfo>
|
56
|
+
}
|
57
|
+
|
58
|
+
export function isTokenStore(
|
59
|
+
implementation: Record<string, unknown> & Partial<TokenStore>,
|
60
|
+
): implementation is Record<string, unknown> & TokenStore {
|
61
|
+
return (
|
62
|
+
typeof implementation.createToken === 'function' &&
|
63
|
+
typeof implementation.readToken === 'function' &&
|
64
|
+
typeof implementation.rotateToken === 'function' &&
|
65
|
+
typeof implementation.deleteToken === 'function' &&
|
66
|
+
typeof implementation.findTokenByCode === 'function' &&
|
67
|
+
typeof implementation.findTokenByRefreshToken === 'function'
|
68
|
+
)
|
69
|
+
}
|
70
|
+
|
71
|
+
export function asTokenStore(
|
72
|
+
implementation?: Record<string, unknown> & Partial<TokenStore>,
|
73
|
+
): TokenStore {
|
74
|
+
if (!implementation || !isTokenStore(implementation)) {
|
75
|
+
throw new Error('Invalid TokenStore implementation')
|
76
|
+
}
|
77
|
+
return implementation
|
78
|
+
}
|