@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,287 @@
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http'
|
2
|
+
|
3
|
+
import { serialize as serializeCookie } from 'cookie'
|
4
|
+
import type Keygrip from 'keygrip'
|
5
|
+
import { z } from 'zod'
|
6
|
+
|
7
|
+
import { appendHeader, parseHttpCookies } from '../lib/http/index.js'
|
8
|
+
|
9
|
+
import { SESSION_FIXATION_MAX_AGE } from '../constants.js'
|
10
|
+
import { DeviceData } from './device-data.js'
|
11
|
+
import { extractDeviceDetails } from './device-details.js'
|
12
|
+
import { DeviceId, deviceIdSchema, generateDeviceId } from './device-id.js'
|
13
|
+
import { DeviceStore } from './device-store.js'
|
14
|
+
import { generateSessionId, sessionIdSchema } from './session-id.js'
|
15
|
+
|
16
|
+
export const DEFAULT_OPTIONS = {
|
17
|
+
/**
|
18
|
+
* Controls whether the IP address is read from the `X-Forwarded-For` header
|
19
|
+
* (if `true`), or from the `req.socket.remoteAddress` property (if `false`).
|
20
|
+
*
|
21
|
+
* @default true // (nowadays, most requests are proxied)
|
22
|
+
*/
|
23
|
+
trustProxy: true,
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Amount of time (in ms) after which session IDs will be rotated
|
27
|
+
*
|
28
|
+
* @default 300e3 // (5 minutes)
|
29
|
+
*/
|
30
|
+
rotationRate: 5 * 60e3,
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Cookie options
|
34
|
+
*/
|
35
|
+
cookie: {
|
36
|
+
keys: undefined as undefined | Keygrip,
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Name of the cookie used to identify the device
|
40
|
+
*
|
41
|
+
* @default 'session-id'
|
42
|
+
*/
|
43
|
+
device: 'device-id',
|
44
|
+
|
45
|
+
/**
|
46
|
+
* Name of the cookie used to identify the session
|
47
|
+
*
|
48
|
+
* @default 'session-id'
|
49
|
+
*/
|
50
|
+
session: 'session-id',
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Url path for the cookie
|
54
|
+
*
|
55
|
+
* @default '/oauth/authorize'
|
56
|
+
*/
|
57
|
+
path: '/oauth/authorize',
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Amount of time (in ms) after which the session cookie will expire.
|
61
|
+
* If set to `null`, the cookie will be a session cookie (deleted when the
|
62
|
+
* browser is closed).
|
63
|
+
*
|
64
|
+
* @default 10 * 365.2 * 24 * 60 * 60e3 // 10 years (in ms)
|
65
|
+
*/
|
66
|
+
age: <number | null>(10 * 365.2 * 24 * 60 * 60e3),
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Controls whether the cookie is only sent over HTTPS (if `true`), or also
|
70
|
+
* over HTTP (if `false`). This should **NOT** be set to `false` in
|
71
|
+
* production.
|
72
|
+
*/
|
73
|
+
secure: true,
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Controls whether the cookie is sent along with cross-site requests.
|
77
|
+
*
|
78
|
+
* @default 'lax'
|
79
|
+
*/
|
80
|
+
sameSite: 'lax' as 'lax' | 'strict',
|
81
|
+
},
|
82
|
+
}
|
83
|
+
|
84
|
+
export type DeviceDeviceManagerOptions = typeof DEFAULT_OPTIONS
|
85
|
+
|
86
|
+
const cookieValueSchema = z.tuple([deviceIdSchema, sessionIdSchema])
|
87
|
+
type CookieValue = z.infer<typeof cookieValueSchema>
|
88
|
+
|
89
|
+
/**
|
90
|
+
* This class provides an abstraction for keeping track of DEVICE sessions. It
|
91
|
+
* relies on a {@link DeviceStore} to persist session data and a cookie to
|
92
|
+
* identify the session.
|
93
|
+
*/
|
94
|
+
export class DeviceManager {
|
95
|
+
constructor(
|
96
|
+
private readonly store: DeviceStore,
|
97
|
+
private readonly options: DeviceDeviceManagerOptions = DEFAULT_OPTIONS,
|
98
|
+
) {}
|
99
|
+
|
100
|
+
public async load(
|
101
|
+
req: IncomingMessage,
|
102
|
+
res: ServerResponse,
|
103
|
+
): Promise<{ deviceId: DeviceId }> {
|
104
|
+
const cookie = await this.getCookie(req)
|
105
|
+
if (cookie) {
|
106
|
+
return this.refresh(req, res, cookie.value, cookie.mustRotate)
|
107
|
+
} else {
|
108
|
+
return this.create(req, res)
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
private async create(
|
113
|
+
req: IncomingMessage,
|
114
|
+
res: ServerResponse,
|
115
|
+
): Promise<{ deviceId: DeviceId }> {
|
116
|
+
const { userAgent, ipAddress } = this.getDeviceDetails(req)
|
117
|
+
|
118
|
+
const [deviceId, sessionId] = await Promise.all([
|
119
|
+
generateDeviceId(),
|
120
|
+
generateSessionId(),
|
121
|
+
] as const)
|
122
|
+
|
123
|
+
await this.store.createDevice(deviceId, {
|
124
|
+
sessionId,
|
125
|
+
lastSeenAt: new Date(),
|
126
|
+
userAgent,
|
127
|
+
ipAddress,
|
128
|
+
})
|
129
|
+
|
130
|
+
this.setCookie(res, [deviceId, sessionId])
|
131
|
+
|
132
|
+
return { deviceId }
|
133
|
+
}
|
134
|
+
|
135
|
+
private async refresh(
|
136
|
+
req: IncomingMessage,
|
137
|
+
res: ServerResponse,
|
138
|
+
[deviceId, sessionId]: CookieValue,
|
139
|
+
forceRotate = false,
|
140
|
+
): Promise<{ deviceId: DeviceId }> {
|
141
|
+
const data = await this.store.readDevice(deviceId)
|
142
|
+
if (!data) return this.create(req, res)
|
143
|
+
|
144
|
+
const lastSeenAt = new Date(data.lastSeenAt)
|
145
|
+
const age = Date.now() - lastSeenAt.getTime()
|
146
|
+
|
147
|
+
if (sessionId !== data.sessionId) {
|
148
|
+
if (age <= SESSION_FIXATION_MAX_AGE) {
|
149
|
+
// The cookie was probably rotated by a concurrent request. Let's
|
150
|
+
// update the cookie with the new sessionId.
|
151
|
+
forceRotate = true
|
152
|
+
} else {
|
153
|
+
// Something's wrong. Let's create a new session.
|
154
|
+
await this.store.deleteDevice(deviceId)
|
155
|
+
return this.create(req, res)
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
const details = this.getDeviceDetails(req)
|
160
|
+
|
161
|
+
if (
|
162
|
+
forceRotate ||
|
163
|
+
details.ipAddress !== data.ipAddress ||
|
164
|
+
details.userAgent !== data.userAgent ||
|
165
|
+
age > this.options.rotationRate
|
166
|
+
) {
|
167
|
+
await this.rotate(req, res, deviceId, {
|
168
|
+
ipAddress: details.ipAddress,
|
169
|
+
userAgent: details.userAgent || data.userAgent,
|
170
|
+
})
|
171
|
+
}
|
172
|
+
|
173
|
+
return { deviceId }
|
174
|
+
}
|
175
|
+
|
176
|
+
public async rotate(
|
177
|
+
req: IncomingMessage,
|
178
|
+
res: ServerResponse,
|
179
|
+
deviceId: DeviceId,
|
180
|
+
data?: Partial<Omit<DeviceData, 'sessionId' | 'lastSeenAt'>>,
|
181
|
+
): Promise<void> {
|
182
|
+
const sessionId = await generateSessionId()
|
183
|
+
|
184
|
+
await this.store.updateDevice(deviceId, {
|
185
|
+
...data,
|
186
|
+
sessionId,
|
187
|
+
lastSeenAt: new Date(),
|
188
|
+
})
|
189
|
+
|
190
|
+
this.setCookie(res, [deviceId, sessionId])
|
191
|
+
}
|
192
|
+
|
193
|
+
private async getCookie(
|
194
|
+
req: IncomingMessage,
|
195
|
+
): Promise<{ value: CookieValue; mustRotate: boolean } | null> {
|
196
|
+
const cookies = parseHttpCookies(req)
|
197
|
+
if (!cookies) return null
|
198
|
+
|
199
|
+
const device = this.parseCookie(
|
200
|
+
cookies,
|
201
|
+
this.options.cookie.device,
|
202
|
+
deviceIdSchema,
|
203
|
+
)
|
204
|
+
const session = this.parseCookie(
|
205
|
+
cookies,
|
206
|
+
this.options.cookie.session,
|
207
|
+
sessionIdSchema,
|
208
|
+
)
|
209
|
+
|
210
|
+
// Silently ignore invalid cookies
|
211
|
+
if (!device || !session) {
|
212
|
+
// If the device cookie is valid, let's cleanup the DB
|
213
|
+
if (device) await this.store.deleteDevice(device.value)
|
214
|
+
|
215
|
+
return null
|
216
|
+
}
|
217
|
+
|
218
|
+
return {
|
219
|
+
value: [device.value, session.value],
|
220
|
+
mustRotate: device.mustRotate || session.mustRotate,
|
221
|
+
}
|
222
|
+
}
|
223
|
+
|
224
|
+
private parseCookie<T>(
|
225
|
+
cookies: Record<string, string | undefined>,
|
226
|
+
name: string,
|
227
|
+
schema: z.ZodType<T> | z.ZodEffects<z.ZodTypeAny, T, string>,
|
228
|
+
): null | { value: T; mustRotate: boolean } {
|
229
|
+
const result = schema.safeParse(cookies[name], { path: ['cookie', name] })
|
230
|
+
if (!result.success) return null
|
231
|
+
|
232
|
+
const value = result.data
|
233
|
+
|
234
|
+
if (this.options.cookie.keys) {
|
235
|
+
const hash = cookies[`${name}:hash`]
|
236
|
+
if (!hash) return null
|
237
|
+
|
238
|
+
const idx = this.options.cookie.keys.index(value, hash)
|
239
|
+
if (idx < 0) return null
|
240
|
+
|
241
|
+
return { value, mustRotate: idx !== 0 }
|
242
|
+
}
|
243
|
+
|
244
|
+
return { value, mustRotate: false }
|
245
|
+
}
|
246
|
+
|
247
|
+
private setCookie(res: ServerResponse, cookieValue: null | CookieValue) {
|
248
|
+
this.writeCookie(res, this.options.cookie.device, cookieValue?.[0])
|
249
|
+
this.writeCookie(res, this.options.cookie.session, cookieValue?.[1])
|
250
|
+
}
|
251
|
+
|
252
|
+
private writeCookie(res: ServerResponse, name: string, value?: string) {
|
253
|
+
const cookieOptions = {
|
254
|
+
maxAge: value
|
255
|
+
? this.options.cookie.age == null
|
256
|
+
? undefined
|
257
|
+
: this.options.cookie.age / 1000
|
258
|
+
: 0,
|
259
|
+
httpOnly: true,
|
260
|
+
path: this.options.cookie.path,
|
261
|
+
secure: this.options.cookie.secure !== false,
|
262
|
+
sameSite: this.options.cookie.sameSite === 'lax' ? 'lax' : 'strict',
|
263
|
+
} as const
|
264
|
+
|
265
|
+
appendHeader(
|
266
|
+
res,
|
267
|
+
'Set-Cookie',
|
268
|
+
serializeCookie(name, value || '', cookieOptions),
|
269
|
+
)
|
270
|
+
|
271
|
+
if (this.options.cookie.keys) {
|
272
|
+
appendHeader(
|
273
|
+
res,
|
274
|
+
'Set-Cookie',
|
275
|
+
serializeCookie(
|
276
|
+
`${name}:hash`,
|
277
|
+
value ? this.options.cookie.keys.sign(value) : '',
|
278
|
+
cookieOptions,
|
279
|
+
),
|
280
|
+
)
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
private getDeviceDetails(req: IncomingMessage) {
|
285
|
+
return extractDeviceDetails(req, this.options.trustProxy)
|
286
|
+
}
|
287
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { Awaitable } from '../lib/util/type.js'
|
2
|
+
import { DeviceData } from './device-data.js'
|
3
|
+
import { DeviceId } from './device-id.js'
|
4
|
+
|
5
|
+
// Export all types needed to implement the DeviceStore interface
|
6
|
+
export * from './device-id.js'
|
7
|
+
export * from './device-data.js'
|
8
|
+
export * from './session-id.js'
|
9
|
+
|
10
|
+
export interface DeviceStore {
|
11
|
+
createDevice(deviceId: DeviceId, data: DeviceData): Awaitable<void>
|
12
|
+
readDevice(deviceId: DeviceId): Awaitable<DeviceData | null>
|
13
|
+
updateDevice(deviceId: DeviceId, data: Partial<DeviceData>): Awaitable<void>
|
14
|
+
deleteDevice(deviceId: DeviceId): Awaitable<void>
|
15
|
+
}
|
16
|
+
|
17
|
+
export function isDeviceStore(
|
18
|
+
implementation: Record<string, unknown> & Partial<DeviceStore>,
|
19
|
+
): implementation is Record<string, unknown> & DeviceStore {
|
20
|
+
return (
|
21
|
+
typeof implementation.createDevice === 'function' &&
|
22
|
+
typeof implementation.readDevice === 'function' &&
|
23
|
+
typeof implementation.updateDevice === 'function' &&
|
24
|
+
typeof implementation.deleteDevice === 'function'
|
25
|
+
)
|
26
|
+
}
|
27
|
+
|
28
|
+
export function asDeviceStore(
|
29
|
+
implementation?: Record<string, unknown> & Partial<DeviceStore>,
|
30
|
+
): DeviceStore {
|
31
|
+
if (!implementation || !isDeviceStore(implementation)) {
|
32
|
+
throw new Error('Invalid DeviceStore implementation')
|
33
|
+
}
|
34
|
+
return implementation
|
35
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { z } from 'zod'
|
2
|
+
|
3
|
+
import { SESSION_ID_BYTES_LENGTH, SESSION_ID_PREFIX } from '../constants.js'
|
4
|
+
import { randomHexId } from '../lib/util/crypto.js'
|
5
|
+
|
6
|
+
export const SESSION_ID_LENGTH =
|
7
|
+
SESSION_ID_PREFIX.length + SESSION_ID_BYTES_LENGTH * 2 // hex encoding
|
8
|
+
|
9
|
+
export const sessionIdSchema = z
|
10
|
+
.string()
|
11
|
+
.length(SESSION_ID_LENGTH)
|
12
|
+
.refine(
|
13
|
+
(v): v is `${typeof SESSION_ID_PREFIX}${string}` =>
|
14
|
+
v.startsWith(SESSION_ID_PREFIX),
|
15
|
+
{
|
16
|
+
message: `Invalid session ID format`,
|
17
|
+
},
|
18
|
+
)
|
19
|
+
export type SessionId = z.infer<typeof sessionIdSchema>
|
20
|
+
export const generateSessionId = async (): Promise<SessionId> => {
|
21
|
+
return `${SESSION_ID_PREFIX}${await randomHexId(SESSION_ID_BYTES_LENGTH)}`
|
22
|
+
}
|
@@ -0,0 +1,147 @@
|
|
1
|
+
import { createHash } from 'node:crypto'
|
2
|
+
|
3
|
+
import { EmbeddedJWK, calculateJwkThumbprint, jwtVerify } from 'jose'
|
4
|
+
import { JOSEError } from 'jose/errors'
|
5
|
+
|
6
|
+
import { DPOP_NONCE_MAX_AGE } from '../constants.js'
|
7
|
+
import { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js'
|
8
|
+
import { UseDpopNonceError } from '../errors/use-dpop-nonce-error.js'
|
9
|
+
import { DpopNonce, DpopNonceInput } from './dpop-nonce.js'
|
10
|
+
|
11
|
+
export { DpopNonce, type DpopNonceInput }
|
12
|
+
export type DpopManagerOptions = {
|
13
|
+
/**
|
14
|
+
* Set this to `false` to disable the use of nonces in DPoP proofs. Set this
|
15
|
+
* to a secret Uint8Array or hex encoded string to use a predictable seed for
|
16
|
+
* all nonces (typically useful when multiple instances are running). Leave
|
17
|
+
* undefined to generate a random seed at startup.
|
18
|
+
*/
|
19
|
+
dpopSecret?: false | DpopNonceInput
|
20
|
+
dpopStep?: number
|
21
|
+
}
|
22
|
+
|
23
|
+
export class DpopManager {
|
24
|
+
protected readonly dpopNonce?: DpopNonce
|
25
|
+
|
26
|
+
constructor({ dpopSecret, dpopStep }: DpopManagerOptions = {}) {
|
27
|
+
this.dpopNonce =
|
28
|
+
dpopSecret === false ? undefined : DpopNonce.from(dpopSecret, dpopStep)
|
29
|
+
}
|
30
|
+
|
31
|
+
nextNonce(): string | undefined {
|
32
|
+
return this.dpopNonce?.next()
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3}
|
37
|
+
*/
|
38
|
+
async checkProof(
|
39
|
+
proof: unknown,
|
40
|
+
htm: string, // HTTP Method
|
41
|
+
htu: string | URL, // HTTP URL
|
42
|
+
accessToken?: string, // Access Token
|
43
|
+
) {
|
44
|
+
if (Array.isArray(proof) && proof.length === 1) {
|
45
|
+
proof = proof[0]
|
46
|
+
}
|
47
|
+
|
48
|
+
if (!proof || typeof proof !== 'string') {
|
49
|
+
throw new InvalidDpopProofError('DPoP proof required')
|
50
|
+
}
|
51
|
+
|
52
|
+
const { protectedHeader, payload } = await jwtVerify<{
|
53
|
+
iat: number
|
54
|
+
exp: number
|
55
|
+
jti: string
|
56
|
+
}>(proof, EmbeddedJWK, {
|
57
|
+
typ: 'dpop+jwt',
|
58
|
+
maxTokenAge: 10,
|
59
|
+
clockTolerance: DPOP_NONCE_MAX_AGE / 1e3,
|
60
|
+
requiredClaims: ['iat', 'exp', 'jti'],
|
61
|
+
}).catch((err) => {
|
62
|
+
const message =
|
63
|
+
err instanceof JOSEError
|
64
|
+
? `Invalid DPoP proof (${err.message})`
|
65
|
+
: 'Invalid DPoP proof'
|
66
|
+
throw new InvalidDpopProofError(message, err)
|
67
|
+
})
|
68
|
+
|
69
|
+
if (!payload.jti || typeof payload.jti !== 'string') {
|
70
|
+
throw new InvalidDpopProofError('Invalid or missing jti property')
|
71
|
+
}
|
72
|
+
|
73
|
+
if (payload.exp - payload.iat > DPOP_NONCE_MAX_AGE / 3 / 1e3) {
|
74
|
+
throw new InvalidDpopProofError('DPoP proof validity too long')
|
75
|
+
}
|
76
|
+
|
77
|
+
// Note rfc9110#section-9.1 states that the method name is case-sensitive
|
78
|
+
if (!htm || htm !== payload['htm']) {
|
79
|
+
throw new InvalidDpopProofError('DPoP htm mismatch')
|
80
|
+
}
|
81
|
+
|
82
|
+
if (
|
83
|
+
payload['nonce'] !== undefined &&
|
84
|
+
typeof payload['nonce'] !== 'string'
|
85
|
+
) {
|
86
|
+
throw new InvalidDpopProofError('DPoP nonce must be a string')
|
87
|
+
}
|
88
|
+
|
89
|
+
if (!payload['nonce'] && this.dpopNonce) {
|
90
|
+
throw new UseDpopNonceError()
|
91
|
+
}
|
92
|
+
|
93
|
+
if (payload['nonce'] && !this.dpopNonce?.check(payload['nonce'])) {
|
94
|
+
throw new UseDpopNonceError()
|
95
|
+
}
|
96
|
+
|
97
|
+
const htuNorm = normalizeHtu(htu)
|
98
|
+
if (!htuNorm || htuNorm !== normalizeHtu(payload['htu'])) {
|
99
|
+
throw new InvalidDpopProofError('DPoP htu mismatch')
|
100
|
+
}
|
101
|
+
|
102
|
+
if (accessToken) {
|
103
|
+
const athBuffer = createHash('sha256').update(accessToken).digest()
|
104
|
+
if (payload['ath'] !== athBuffer.toString('base64url')) {
|
105
|
+
throw new InvalidDpopProofError('DPoP ath mismatch')
|
106
|
+
}
|
107
|
+
} else if (payload['ath']) {
|
108
|
+
throw new InvalidDpopProofError('DPoP ath not allowed')
|
109
|
+
}
|
110
|
+
|
111
|
+
try {
|
112
|
+
return {
|
113
|
+
protectedHeader,
|
114
|
+
payload,
|
115
|
+
jkt: await calculateJwkThumbprint(protectedHeader['jwk']!, 'sha256'), // EmbeddedJWK
|
116
|
+
}
|
117
|
+
} catch (err) {
|
118
|
+
const message =
|
119
|
+
err instanceof JOSEError ? err.message : 'Failed to calculate jkt'
|
120
|
+
throw new InvalidDpopProofError(message, err)
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
/**
|
126
|
+
* @note
|
127
|
+
* > The htu claim matches the HTTP URI value for the HTTP request in which the
|
128
|
+
* > JWT was received, ignoring any query and fragment parts.
|
129
|
+
*
|
130
|
+
* > To reduce the likelihood of false negatives, servers SHOULD employ
|
131
|
+
* > syntax-based normalization (Section 6.2.2 of [RFC3986]) and scheme-based
|
132
|
+
* > normalization (Section 6.2.3 of [RFC3986]) before comparing the htu claim.
|
133
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc9449#section-4.3 | RFC9449 section 4.3. Checking DPoP Proofs}
|
134
|
+
*/
|
135
|
+
function normalizeHtu(htu: unknown): string | null {
|
136
|
+
// Optimization
|
137
|
+
if (!htu) return null
|
138
|
+
|
139
|
+
try {
|
140
|
+
const url = new URL(String(htu))
|
141
|
+
url.hash = ''
|
142
|
+
url.search = ''
|
143
|
+
return url.href
|
144
|
+
} catch {
|
145
|
+
return null
|
146
|
+
}
|
147
|
+
}
|
@@ -0,0 +1,104 @@
|
|
1
|
+
import { createHmac, randomBytes } from 'node:crypto'
|
2
|
+
|
3
|
+
import { DPOP_NONCE_MAX_AGE } from '../constants.js'
|
4
|
+
|
5
|
+
function numTo64bits(num: number) {
|
6
|
+
const arr = new Uint8Array(8)
|
7
|
+
arr[7] = (num = num | 0) & 0xff
|
8
|
+
arr[6] = (num >>= 8) & 0xff
|
9
|
+
arr[5] = (num >>= 8) & 0xff
|
10
|
+
arr[4] = (num >>= 8) & 0xff
|
11
|
+
arr[3] = (num >>= 8) & 0xff
|
12
|
+
arr[2] = (num >>= 8) & 0xff
|
13
|
+
arr[1] = (num >>= 8) & 0xff
|
14
|
+
arr[0] = (num >>= 8) & 0xff
|
15
|
+
return arr
|
16
|
+
}
|
17
|
+
|
18
|
+
export type DpopNonceInput = string | Uint8Array | DpopNonce
|
19
|
+
|
20
|
+
export class DpopNonce {
|
21
|
+
#secret: Uint8Array
|
22
|
+
#counter: number
|
23
|
+
|
24
|
+
#prev: string
|
25
|
+
#now: string
|
26
|
+
#next: string
|
27
|
+
|
28
|
+
constructor(
|
29
|
+
protected readonly secret: Uint8Array,
|
30
|
+
protected readonly step: number,
|
31
|
+
) {
|
32
|
+
if (secret.length !== 32) throw new TypeError('Expected 32 bytes')
|
33
|
+
if (this.step < 0 || this.step > DPOP_NONCE_MAX_AGE / 3) {
|
34
|
+
throw new TypeError('Invalid step')
|
35
|
+
}
|
36
|
+
|
37
|
+
this.#secret = Uint8Array.from(secret)
|
38
|
+
this.#counter = (Date.now() / step) | 0
|
39
|
+
|
40
|
+
this.#prev = this.compute(this.#counter - 1)
|
41
|
+
this.#now = this.compute(this.#counter)
|
42
|
+
this.#next = this.compute(this.#counter + 1)
|
43
|
+
}
|
44
|
+
|
45
|
+
protected rotate() {
|
46
|
+
const counter = (Date.now() / this.step) | 0
|
47
|
+
switch (counter - this.#counter) {
|
48
|
+
case 0:
|
49
|
+
// counter === this.#counter => nothing to do
|
50
|
+
return
|
51
|
+
case 1:
|
52
|
+
// Optimization: avoid recomputing #prev & #now
|
53
|
+
this.#prev = this.#now
|
54
|
+
this.#now = this.#next
|
55
|
+
this.#next = this.compute(counter + 1)
|
56
|
+
break
|
57
|
+
case 2:
|
58
|
+
// Optimization: avoid recomputing #prev
|
59
|
+
this.#prev = this.#next
|
60
|
+
this.#now = this.compute(counter)
|
61
|
+
this.#next = this.compute(counter + 1)
|
62
|
+
break
|
63
|
+
default:
|
64
|
+
// All nonces are outdated, so we recompute all of them
|
65
|
+
this.#prev = this.compute(counter - 1)
|
66
|
+
this.#now = this.compute(counter)
|
67
|
+
this.#next = this.compute(counter + 1)
|
68
|
+
break
|
69
|
+
}
|
70
|
+
this.#counter = counter
|
71
|
+
}
|
72
|
+
|
73
|
+
protected compute(counter: number) {
|
74
|
+
return createHmac('sha256', this.#secret)
|
75
|
+
.update(numTo64bits(counter))
|
76
|
+
.digest()
|
77
|
+
.toString('base64url')
|
78
|
+
}
|
79
|
+
|
80
|
+
public next() {
|
81
|
+
this.rotate()
|
82
|
+
return this.#next
|
83
|
+
}
|
84
|
+
|
85
|
+
public check(nonce: string) {
|
86
|
+
return this.#next === nonce || this.#now === nonce || this.#prev === nonce
|
87
|
+
}
|
88
|
+
|
89
|
+
static from(
|
90
|
+
input: DpopNonceInput = randomBytes(32),
|
91
|
+
step = DPOP_NONCE_MAX_AGE / 3,
|
92
|
+
): DpopNonce {
|
93
|
+
if (input instanceof DpopNonce) {
|
94
|
+
return input
|
95
|
+
}
|
96
|
+
if (input instanceof Uint8Array) {
|
97
|
+
return new DpopNonce(input, step)
|
98
|
+
}
|
99
|
+
if (typeof input === 'string') {
|
100
|
+
return new DpopNonce(Buffer.from(input, 'hex'), step)
|
101
|
+
}
|
102
|
+
return new DpopNonce(input, step)
|
103
|
+
}
|
104
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { OAuthAuthenticationRequestParameters } from '@atproto/oauth-types'
|
2
|
+
import { buildErrorPayload } from '../output/build-error-payload.js'
|
3
|
+
import { OAuthError } from './oauth-error.js'
|
4
|
+
|
5
|
+
export class AccessDeniedError extends OAuthError {
|
6
|
+
constructor(
|
7
|
+
public readonly parameters: OAuthAuthenticationRequestParameters,
|
8
|
+
error_description: string,
|
9
|
+
error = 'access_denied',
|
10
|
+
cause?: unknown,
|
11
|
+
) {
|
12
|
+
super(error, error_description, 400, cause)
|
13
|
+
}
|
14
|
+
|
15
|
+
static from(
|
16
|
+
parameters: OAuthAuthenticationRequestParameters,
|
17
|
+
cause?: unknown,
|
18
|
+
) {
|
19
|
+
if (cause && cause instanceof AccessDeniedError) {
|
20
|
+
return cause
|
21
|
+
}
|
22
|
+
|
23
|
+
const { error, error_description } = buildErrorPayload(cause)
|
24
|
+
return new AccessDeniedError(parameters, error_description, error, cause)
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { OAuthAuthenticationRequestParameters } from '@atproto/oauth-types'
|
2
|
+
import { AccessDeniedError } from './access-denied-error.js'
|
3
|
+
|
4
|
+
export class AccountSelectionRequiredError extends AccessDeniedError {
|
5
|
+
constructor(
|
6
|
+
parameters: OAuthAuthenticationRequestParameters,
|
7
|
+
error_description = 'Account selection required',
|
8
|
+
cause?: unknown,
|
9
|
+
) {
|
10
|
+
super(parameters, error_description, 'account_selection_required', cause)
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { OAuthAuthenticationRequestParameters } from '@atproto/oauth-types'
|
2
|
+
import { AccessDeniedError } from './access-denied-error.js'
|
3
|
+
|
4
|
+
export class ConsentRequiredError extends AccessDeniedError {
|
5
|
+
constructor(
|
6
|
+
parameters: OAuthAuthenticationRequestParameters,
|
7
|
+
error_description = 'User consent required',
|
8
|
+
cause?: unknown,
|
9
|
+
) {
|
10
|
+
super(parameters, error_description, 'consent_required', cause)
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { OAuthError } from './oauth-error.js'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @see
|
5
|
+
* {@link https://datatracker.ietf.org/doc/html/rfc9396#section-14.6 | RFC 9396 - OAuth Dynamic Client Registration Metadata Registration Error}
|
6
|
+
*
|
7
|
+
* The AS MUST refuse to process any unknown authorization details type or
|
8
|
+
* authorization details not conforming to the respective type definition. The
|
9
|
+
* AS MUST abort processing and respond with an error
|
10
|
+
* invalid_authorization_details to the client if any of the following are true
|
11
|
+
* of the objects in the authorization_details structure:
|
12
|
+
* - contains an unknown authorization details type value,
|
13
|
+
* - is an object of known type but containing unknown fields,
|
14
|
+
* - contains fields of the wrong type for the authorization details type,
|
15
|
+
* - contains fields with invalid values for the authorization details type, or
|
16
|
+
* - is missing required fields for the authorization details type.
|
17
|
+
*/
|
18
|
+
export class InvalidAuthorizationDetailsError extends OAuthError {
|
19
|
+
constructor(error_description: string, cause?: unknown) {
|
20
|
+
super('invalid_authorization_details', error_description, 400, cause)
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { OAuthError } from './oauth-error.js'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @see
|
5
|
+
* {@link https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 | RFC6749 - Issuing an Access Token }
|
6
|
+
*
|
7
|
+
* Client authentication failed (e.g., unknown client, no client authentication
|
8
|
+
* included, or unsupported authentication method). The authorization server MAY
|
9
|
+
* return an HTTP 401 (Unauthorized) status code to indicate which HTTP
|
10
|
+
* authentication schemes are supported. If the client attempted to
|
11
|
+
* authenticate via the "Authorization" request header field, the authorization
|
12
|
+
* server MUST respond with an HTTP 401 (Unauthorized) status code and include
|
13
|
+
* the "WWW-Authenticate" response header field matching the authentication
|
14
|
+
* scheme used by the client.
|
15
|
+
*/
|
16
|
+
export class InvalidClientError extends OAuthError {
|
17
|
+
constructor(error_description: string, cause?: unknown) {
|
18
|
+
super('invalid_client', error_description, 400, cause)
|
19
|
+
}
|
20
|
+
}
|