@udibo/oauth2 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/LICENSE +20 -0
- package/README.md +174 -0
- package/SECURITY.md +77 -0
- package/esm/_dnt.polyfills.d.ts +17 -0
- package/esm/_dnt.polyfills.js +15 -0
- package/esm/adapters/hono/_common.d.ts +66 -0
- package/esm/adapters/hono/_common.js +108 -0
- package/esm/adapters/hono/authorization-server.d.ts +211 -0
- package/esm/adapters/hono/authorization-server.js +214 -0
- package/esm/adapters/hono/bff/auth-request-store.d.ts +164 -0
- package/esm/adapters/hono/bff/auth-request-store.js +154 -0
- package/esm/adapters/hono/bff/bff.d.ts +852 -0
- package/esm/adapters/hono/bff/bff.js +1194 -0
- package/esm/adapters/hono/bff/cookie-prefix.d.ts +72 -0
- package/esm/adapters/hono/bff/cookie-prefix.js +85 -0
- package/esm/adapters/hono/bff/mod.d.ts +19 -0
- package/esm/adapters/hono/bff/mod.js +19 -0
- package/esm/adapters/hono/bff/proxy.d.ts +108 -0
- package/esm/adapters/hono/bff/proxy.js +314 -0
- package/esm/adapters/hono/bff/session-store.d.ts +250 -0
- package/esm/adapters/hono/bff/session-store.js +216 -0
- package/esm/adapters/hono/bff/testing.d.ts +165 -0
- package/esm/adapters/hono/bff/testing.js +254 -0
- package/esm/adapters/hono/identity.d.ts +117 -0
- package/esm/adapters/hono/identity.js +215 -0
- package/esm/adapters/hono/log.d.ts +49 -0
- package/esm/adapters/hono/log.js +66 -0
- package/esm/adapters/hono/resource-server.d.ts +101 -0
- package/esm/adapters/hono/resource-server.js +108 -0
- package/esm/client/_http.d.ts +91 -0
- package/esm/client/_http.js +303 -0
- package/esm/client/base.d.ts +184 -0
- package/esm/client/base.js +93 -0
- package/esm/client/bff-client.d.ts +235 -0
- package/esm/client/bff-client.js +280 -0
- package/esm/client/browser-storage.d.ts +152 -0
- package/esm/client/browser-storage.js +246 -0
- package/esm/client/check.d.ts +114 -0
- package/esm/client/check.js +97 -0
- package/esm/client/direct-client.d.ts +707 -0
- package/esm/client/direct-client.js +1237 -0
- package/esm/client/discovery-cache.d.ts +138 -0
- package/esm/client/discovery-cache.js +124 -0
- package/esm/client/events.d.ts +54 -0
- package/esm/client/events.js +37 -0
- package/esm/client/mod.d.ts +35 -0
- package/esm/client/mod.js +33 -0
- package/esm/client/storage.d.ts +127 -0
- package/esm/client/storage.js +81 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.d.ts +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.js +47 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.d.ts +28 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.js +65 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assert.d.ts +16 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assert.js +23 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.d.ts +25 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.js +30 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equal.d.ts +24 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equal.js +193 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equals.d.ts +36 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equals.js +61 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/exists.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/exists.js +27 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/fail.d.ts +14 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/fail.js +20 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/false.d.ts +18 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/false.js +23 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.d.ts +22 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.js +52 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.d.ts +24 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.js +53 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less.d.ts +18 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less.js +28 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/match.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/match.js +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/mod.d.ts +43 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/mod.js +45 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.d.ts +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.js +39 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.js +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.d.ts +22 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.js +32 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.d.ts +28 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.js +177 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.d.ts +41 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.js +53 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.d.ts +23 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.js +58 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.js +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/throws.d.ts +44 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/throws.js +44 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.d.ts +14 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.js +20 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.d.ts +14 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.js +20 -0
- package/esm/deps/jsr.io/@std/async/1.5.0/delay.d.ts +44 -0
- package/esm/deps/jsr.io/@std/async/1.5.0/delay.js +84 -0
- package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.d.ts +39 -0
- package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.js +61 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.d.ts +22 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.js +51 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.d.ts +34 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.js +192 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.d.ts +34 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.js +113 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.d.ts +3 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.js +13 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.d.ts +8 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.js +2 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.d.ts +1 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.js +26 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.d.ts +60 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.js +152 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.js +87 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.js +131 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.js +82 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.js +72 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.d.ts +38 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.js +87 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.d.ts +97 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.js +99 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.d.ts +119 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.js +209 -0
- package/esm/deps/jsr.io/@std/http/1.1.3/status.d.ts +343 -0
- package/esm/deps/jsr.io/@std/http/1.1.3/status.js +343 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.d.ts +159 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.js +223 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.d.ts +79 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.js +116 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff.d.ts +139 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff.js +277 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.d.ts +98 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.js +181 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/format.d.ts +29 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/format.js +68 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/styles.d.ts +158 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/styles.js +206 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/types.d.ts +24 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/types.js +3 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.d.ts +91 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.js +347 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.d.ts +382 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.js +1110 -0
- package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.d.ts +363 -0
- package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.js +616 -0
- package/esm/errors.d.ts +203 -0
- package/esm/errors.js +287 -0
- package/esm/identity/captcha.d.ts +147 -0
- package/esm/identity/captcha.js +64 -0
- package/esm/identity/delivery.d.ts +96 -0
- package/esm/identity/delivery.js +41 -0
- package/esm/identity/errors.d.ts +39 -0
- package/esm/identity/errors.js +46 -0
- package/esm/identity/events.d.ts +242 -0
- package/esm/identity/events.js +39 -0
- package/esm/identity/external/_shared.d.ts +86 -0
- package/esm/identity/external/_shared.js +107 -0
- package/esm/identity/external/_token-exchange.d.ts +61 -0
- package/esm/identity/external/_token-exchange.js +179 -0
- package/esm/identity/external/apple-client-secret.d.ts +82 -0
- package/esm/identity/external/apple-client-secret.js +133 -0
- package/esm/identity/external/apple.d.ts +64 -0
- package/esm/identity/external/apple.js +248 -0
- package/esm/identity/external/discord.d.ts +38 -0
- package/esm/identity/external/discord.js +62 -0
- package/esm/identity/external/errors.d.ts +48 -0
- package/esm/identity/external/errors.js +36 -0
- package/esm/identity/external/flow.d.ts +107 -0
- package/esm/identity/external/flow.js +158 -0
- package/esm/identity/external/github.d.ts +42 -0
- package/esm/identity/external/github.js +164 -0
- package/esm/identity/external/google.d.ts +52 -0
- package/esm/identity/external/google.js +38 -0
- package/esm/identity/external/mod.d.ts +63 -0
- package/esm/identity/external/mod.js +61 -0
- package/esm/identity/external/oauth2.d.ts +110 -0
- package/esm/identity/external/oauth2.js +158 -0
- package/esm/identity/external/oidc.d.ts +81 -0
- package/esm/identity/external/oidc.js +250 -0
- package/esm/identity/external/provider.d.ts +132 -0
- package/esm/identity/external/provider.js +12 -0
- package/esm/identity/hibp.d.ts +67 -0
- package/esm/identity/hibp.js +87 -0
- package/esm/identity/identifier.d.ts +44 -0
- package/esm/identity/identifier.js +47 -0
- package/esm/identity/lockout.d.ts +155 -0
- package/esm/identity/lockout.js +117 -0
- package/esm/identity/mfa/mod.d.ts +42 -0
- package/esm/identity/mfa/mod.js +42 -0
- package/esm/identity/mfa/recovery-codes.d.ts +96 -0
- package/esm/identity/mfa/recovery-codes.js +80 -0
- package/esm/identity/mfa/service.d.ts +338 -0
- package/esm/identity/mfa/service.js +351 -0
- package/esm/identity/mfa/totp.d.ts +186 -0
- package/esm/identity/mfa/totp.js +194 -0
- package/esm/identity/migration.d.ts +155 -0
- package/esm/identity/migration.js +243 -0
- package/esm/identity/mod.d.ts +65 -0
- package/esm/identity/mod.js +64 -0
- package/esm/identity/otp.d.ts +255 -0
- package/esm/identity/otp.js +245 -0
- package/esm/identity/password-policy.d.ts +45 -0
- package/esm/identity/password-policy.js +60 -0
- package/esm/identity/password.d.ts +171 -0
- package/esm/identity/password.js +160 -0
- package/esm/identity/rate-limit.d.ts +192 -0
- package/esm/identity/rate-limit.js +168 -0
- package/esm/identity/service.d.ts +579 -0
- package/esm/identity/service.js +835 -0
- package/esm/identity/session.d.ts +122 -0
- package/esm/identity/session.js +48 -0
- package/esm/identity/token-flow.d.ts +214 -0
- package/esm/identity/token-flow.js +173 -0
- package/esm/models/authentication.d.ts +24 -0
- package/esm/models/authentication.js +1 -0
- package/esm/models/authorization-code.d.ts +63 -0
- package/esm/models/authorization-code.js +1 -0
- package/esm/models/authorization.d.ts +165 -0
- package/esm/models/authorization.js +160 -0
- package/esm/models/client.d.ts +78 -0
- package/esm/models/client.js +1 -0
- package/esm/models/device-authorization.d.ts +30 -0
- package/esm/models/device-authorization.js +1 -0
- package/esm/models/responses.d.ts +93 -0
- package/esm/models/responses.js +1 -0
- package/esm/models/scope.d.ts +81 -0
- package/esm/models/scope.js +125 -0
- package/esm/models/token.d.ts +135 -0
- package/esm/models/token.js +1 -0
- package/esm/package.json +3 -0
- package/esm/react/callback.d.ts +52 -0
- package/esm/react/callback.js +71 -0
- package/esm/react/components/class-names.d.ts +91 -0
- package/esm/react/components/class-names.js +118 -0
- package/esm/react/components/internal.d.ts +62 -0
- package/esm/react/components/internal.js +102 -0
- package/esm/react/components/mfa-challenge-form.d.ts +66 -0
- package/esm/react/components/mfa-challenge-form.js +46 -0
- package/esm/react/components/mfa-enrollment-form.d.ts +83 -0
- package/esm/react/components/mfa-enrollment-form.js +41 -0
- package/esm/react/components/mod.d.ts +103 -0
- package/esm/react/components/mod.js +93 -0
- package/esm/react/components/request-password-reset-form.d.ts +61 -0
- package/esm/react/components/request-password-reset-form.js +41 -0
- package/esm/react/components/reset-password-form.d.ts +66 -0
- package/esm/react/components/reset-password-form.js +56 -0
- package/esm/react/components/sign-in-form.d.ts +74 -0
- package/esm/react/components/sign-in-form.js +43 -0
- package/esm/react/components/sign-up-form.d.ts +85 -0
- package/esm/react/components/sign-up-form.js +57 -0
- package/esm/react/components/types.d.ts +208 -0
- package/esm/react/components/types.js +14 -0
- package/esm/react/components/use-auth-form.d.ts +118 -0
- package/esm/react/components/use-auth-form.js +172 -0
- package/esm/react/components/user-menu.d.ts +79 -0
- package/esm/react/components/user-menu.js +73 -0
- package/esm/react/context.d.ts +51 -0
- package/esm/react/context.js +16 -0
- package/esm/react/mod.d.ts +26 -0
- package/esm/react/mod.js +26 -0
- package/esm/react/provider.d.ts +40 -0
- package/esm/react/provider.js +189 -0
- package/esm/react/require-auth.d.ts +31 -0
- package/esm/react/require-auth.js +37 -0
- package/esm/react/testing.d.ts +322 -0
- package/esm/react/testing.js +461 -0
- package/esm/react/use-authorization.d.ts +31 -0
- package/esm/react/use-authorization.js +36 -0
- package/esm/react/use-oauth2.d.ts +112 -0
- package/esm/react/use-oauth2.js +127 -0
- package/esm/server/authorization-server.d.ts +736 -0
- package/esm/server/authorization-server.js +1304 -0
- package/esm/server/client-authentication.d.ts +28 -0
- package/esm/server/client-authentication.js +51 -0
- package/esm/server/grants/authorization-code.d.ts +179 -0
- package/esm/server/grants/authorization-code.js +232 -0
- package/esm/server/grants/client-credentials.d.ts +52 -0
- package/esm/server/grants/client-credentials.js +53 -0
- package/esm/server/grants/device-authorization.d.ts +103 -0
- package/esm/server/grants/device-authorization.js +156 -0
- package/esm/server/grants/grant.d.ts +165 -0
- package/esm/server/grants/grant.js +173 -0
- package/esm/server/grants/password.d.ts +80 -0
- package/esm/server/grants/password.js +92 -0
- package/esm/server/grants/refresh-token.d.ts +80 -0
- package/esm/server/grants/refresh-token.js +136 -0
- package/esm/server/introspection-token-reader.d.ts +153 -0
- package/esm/server/introspection-token-reader.js +164 -0
- package/esm/server/jwks-token-reader.d.ts +263 -0
- package/esm/server/jwks-token-reader.js +508 -0
- package/esm/server/mod.d.ts +23 -0
- package/esm/server/mod.js +15 -0
- package/esm/server/public-suffix/mod.d.ts +62 -0
- package/esm/server/public-suffix/mod.js +103 -0
- package/esm/server/public-suffix/rules.d.ts +23 -0
- package/esm/server/public-suffix/rules.js +23 -0
- package/esm/server/redirect-uri.d.ts +187 -0
- package/esm/server/redirect-uri.js +331 -0
- package/esm/server/resource-server.d.ts +286 -0
- package/esm/server/resource-server.js +365 -0
- package/esm/server/services/authorization-code.d.ts +84 -0
- package/esm/server/services/authorization-code.js +35 -0
- package/esm/server/services/client.d.ts +35 -0
- package/esm/server/services/client.js +1 -0
- package/esm/server/services/device-authorization.d.ts +134 -0
- package/esm/server/services/device-authorization.js +7 -0
- package/esm/server/services/token.d.ts +242 -0
- package/esm/server/services/token.js +78 -0
- package/esm/server/services/user.d.ts +29 -0
- package/esm/server/services/user.js +7 -0
- package/esm/server/signing-keys.d.ts +201 -0
- package/esm/server/signing-keys.js +225 -0
- package/esm/server/utils/hash.d.ts +6 -0
- package/esm/server/utils/hash.js +11 -0
- package/esm/testing/contract/authorization-code.d.ts +37 -0
- package/esm/testing/contract/authorization-code.js +93 -0
- package/esm/testing/contract/client.d.ts +85 -0
- package/esm/testing/contract/client.js +126 -0
- package/esm/testing/contract/device-authorization.d.ts +37 -0
- package/esm/testing/contract/device-authorization.js +193 -0
- package/esm/testing/contract/lockout-store.d.ts +47 -0
- package/esm/testing/contract/lockout-store.js +122 -0
- package/esm/testing/contract/mfa-store.d.ts +50 -0
- package/esm/testing/contract/mfa-store.js +226 -0
- package/esm/testing/contract/mod.d.ts +39 -0
- package/esm/testing/contract/mod.js +39 -0
- package/esm/testing/contract/otp-store.d.ts +48 -0
- package/esm/testing/contract/otp-store.js +197 -0
- package/esm/testing/contract/rate-limit-store.d.ts +52 -0
- package/esm/testing/contract/rate-limit-store.js +105 -0
- package/esm/testing/contract/token-flow-store.d.ts +54 -0
- package/esm/testing/contract/token-flow-store.js +179 -0
- package/esm/testing/contract/token-reader.d.ts +77 -0
- package/esm/testing/contract/token-reader.js +78 -0
- package/esm/testing/contract/token.d.ts +61 -0
- package/esm/testing/contract/token.js +287 -0
- package/esm/testing/contract/user.d.ts +63 -0
- package/esm/testing/contract/user.js +75 -0
- package/esm/testing/mod.d.ts +34 -0
- package/esm/testing/mod.js +34 -0
- package/esm/testing/server.d.ts +197 -0
- package/esm/testing/server.js +193 -0
- package/esm/testing/services.d.ts +373 -0
- package/esm/testing/services.js +703 -0
- package/esm/utils/_buffer.d.ts +10 -0
- package/esm/utils/_buffer.js +12 -0
- package/esm/utils/_timing-safe.d.ts +13 -0
- package/esm/utils/_timing-safe.js +22 -0
- package/esm/utils/authentication-context.d.ts +3 -0
- package/esm/utils/authentication-context.js +14 -0
- package/esm/utils/basic-auth.d.ts +50 -0
- package/esm/utils/basic-auth.js +106 -0
- package/esm/utils/crypto.d.ts +97 -0
- package/esm/utils/crypto.js +162 -0
- package/esm/utils/pkce.d.ts +151 -0
- package/esm/utils/pkce.js +179 -0
- package/esm/utils/text.d.ts +24 -0
- package/esm/utils/text.js +28 -0
- package/esm/utils/url.d.ts +66 -0
- package/esm/utils/url.js +90 -0
- package/package.json +92 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type { ClientInterface } from "../../models/client.js";
|
|
2
|
+
import type { AuthenticationContext } from "../../models/authentication.js";
|
|
3
|
+
import type { RefreshToken, Token } from "../../models/token.js";
|
|
4
|
+
import type { AbstractScope, BasicScope } from "../../models/scope.js";
|
|
5
|
+
import type { ClientServiceInterface } from "./client.js";
|
|
6
|
+
import type { UserServiceInterface } from "./user.js";
|
|
7
|
+
/**
|
|
8
|
+
* Minimal interface for reading/validating tokens.
|
|
9
|
+
*
|
|
10
|
+
* This is all a resource server needs to authenticate requests.
|
|
11
|
+
* Implement this when your resource server validates tokens against
|
|
12
|
+
* an external authorization server (e.g., via introspection) or
|
|
13
|
+
* a shared token store.
|
|
14
|
+
*/
|
|
15
|
+
export interface TokenReaderInterface<Client extends ClientInterface, User, Scope extends AbstractScope = BasicScope> {
|
|
16
|
+
/** Retrieves an existing token by access token string. */
|
|
17
|
+
getToken(accessToken: string): Promise<Token<Client, User, Scope> | undefined>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Full service interface for token lifecycle management.
|
|
21
|
+
*
|
|
22
|
+
* Extends {@linkcode TokenReaderInterface} with methods for generating,
|
|
23
|
+
* saving, and revoking tokens. Used by the authorization server and
|
|
24
|
+
* its grant types.
|
|
25
|
+
*
|
|
26
|
+
* **A token need not have a user.** The client credentials grant (RFC 6749
|
|
27
|
+
* §4.4) has no resource owner, so it calls `acceptedScope`,
|
|
28
|
+
* `generateAccessToken`, and `accessTokenExpiresAt` with `user: undefined` and
|
|
29
|
+
* saves a {@linkcode Token} whose `user` is unset. An implementation that
|
|
30
|
+
* dereferences `user` without checking will throw on those requests, and a
|
|
31
|
+
* store with a non-nullable user column cannot persist the result — decide
|
|
32
|
+
* scope and lifetime from the client alone when it is absent.
|
|
33
|
+
* `generateRefreshToken` and `refreshTokenExpiresAt` always receive a user:
|
|
34
|
+
* a token with no resource owner is never issued a refresh token.
|
|
35
|
+
*/
|
|
36
|
+
export interface TokenServiceInterface<Client extends ClientInterface, User, Scope extends AbstractScope = BasicScope> extends TokenReaderInterface<Client, User, Scope> {
|
|
37
|
+
/** Lifetime of access tokens in seconds. */
|
|
38
|
+
accessTokenLifetime: number;
|
|
39
|
+
/** Lifetime of refresh tokens in seconds. */
|
|
40
|
+
refreshTokenLifetime: number;
|
|
41
|
+
/**
|
|
42
|
+
* Validates and returns the accepted scope for a client and user. Return a
|
|
43
|
+
* scope to grant it (it may narrow the requested scope, RFC 6749 §3.3),
|
|
44
|
+
* `null`/`undefined` to accept the requested scope as-is, or `false` to
|
|
45
|
+
* reject with `invalid_scope`. `user` is `undefined` for a client acting as
|
|
46
|
+
* its own resource owner (client credentials).
|
|
47
|
+
* @returns The accepted scope, null/undefined to accept as-is, or false to reject.
|
|
48
|
+
*/
|
|
49
|
+
acceptedScope(client: Client, user: User | undefined, scope?: Scope | null): Promise<Scope | null | undefined | false>;
|
|
50
|
+
/**
|
|
51
|
+
* Generates an access token string. `user` is `undefined` for a client
|
|
52
|
+
* acting as its own resource owner (client credentials) — a signed-token
|
|
53
|
+
* generator names the client as the subject then, per RFC 9068 §2.2.
|
|
54
|
+
* Forward `authenticationContext` to a signed-token generator so it uses
|
|
55
|
+
* the credential's event, including the original event on refresh.
|
|
56
|
+
*/
|
|
57
|
+
generateAccessToken(client: Client, user: User | undefined, scope?: Scope | null, authenticationContext?: AuthenticationContext): Promise<string>;
|
|
58
|
+
/** Generates a refresh token string. */
|
|
59
|
+
generateRefreshToken(client: Client, user: User, scope?: Scope | null): Promise<string | undefined>;
|
|
60
|
+
/**
|
|
61
|
+
* Gets the date that a new access token would expire at. `user` is
|
|
62
|
+
* `undefined` for a client acting as its own resource owner (client
|
|
63
|
+
* credentials).
|
|
64
|
+
*/
|
|
65
|
+
accessTokenExpiresAt(client: Client, user: User | undefined, scope?: Scope | null): Promise<Date | undefined>;
|
|
66
|
+
/** Gets the date that a new refresh token would expire at. */
|
|
67
|
+
refreshTokenExpiresAt(client: Client, user: User, scope?: Scope | null): Promise<Date | undefined>;
|
|
68
|
+
/**
|
|
69
|
+
* Gets the absolute date a rotation family stops being refreshable, from the
|
|
70
|
+
* family's anchor ({@linkcode RefreshToken.familyCreatedAt}).
|
|
71
|
+
*
|
|
72
|
+
* **This method is the whole cap.** The grants clamp a rotation's access and
|
|
73
|
+
* refresh expiries to the date it returns and answer `invalid_grant` once
|
|
74
|
+
* that date has passed. Return `undefined` — or leave the method off
|
|
75
|
+
* entirely, which is the default — and the family is never capped, however
|
|
76
|
+
* the service is otherwise configured. Implement it to cap per client (see
|
|
77
|
+
* {@linkcode AbstractTokenService.refreshTokenMaxLifetime} for the
|
|
78
|
+
* service-wide version, which is what the abstract implementation reads).
|
|
79
|
+
*
|
|
80
|
+
* `user` is the refreshed token's own — `undefined` when the store returns a
|
|
81
|
+
* token that has none, so cap from the client alone in that case.
|
|
82
|
+
*/
|
|
83
|
+
refreshTokenFamilyExpiresAt?(client: Client, user: User | undefined, familyCreatedAt: Date, scope?: Scope | null): Promise<Date | undefined>;
|
|
84
|
+
/** Retrieves an existing token by refresh token. */
|
|
85
|
+
getRefreshToken(refreshToken: string): Promise<RefreshToken<Client, User, Scope> | undefined>;
|
|
86
|
+
/** Persists a refresh token and returns the saved record. */
|
|
87
|
+
save(token: RefreshToken<Client, User, Scope>): Promise<RefreshToken<Client, User, Scope>>;
|
|
88
|
+
/** Persists an access token and returns the saved record. */
|
|
89
|
+
save(token: Token<Client, User, Scope>): Promise<Token<Client, User, Scope>>;
|
|
90
|
+
/**
|
|
91
|
+
* Persists either token kind and returns the saved record. Preserve
|
|
92
|
+
* `authenticationContext` on save and every read, including refresh and
|
|
93
|
+
* revoked records; never reconstruct it from a user's current session.
|
|
94
|
+
*/
|
|
95
|
+
save(token: Token<Client, User, Scope> | RefreshToken<Client, User, Scope>): Promise<Token<Client, User, Scope> | RefreshToken<Client, User, Scope>>;
|
|
96
|
+
/** Revokes a token record. Resolves true if a token was revoked. */
|
|
97
|
+
revoke(token: Token<Client, User, Scope> | RefreshToken<Client, User, Scope>): Promise<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Revokes a token by string, using `hint` (`access_token`/`refresh_token`,
|
|
100
|
+
* RFC 7009 §2.1) to disambiguate which kind to look up first.
|
|
101
|
+
*/
|
|
102
|
+
revoke(token: string, hint?: string | null): Promise<boolean>;
|
|
103
|
+
/** Revokes a token given as a record or string. Resolves true if revoked. */
|
|
104
|
+
revoke(token: Token<Client, User, Scope> | RefreshToken<Client, User, Scope> | string, hint?: string | null): Promise<boolean>;
|
|
105
|
+
/**
|
|
106
|
+
* Revokes a refresh token that has just been superseded by rotation.
|
|
107
|
+
* Atomically claims the active token: resolves `true` once, and `false` for
|
|
108
|
+
* concurrent or repeated claims. The grant saves a successor only after a
|
|
109
|
+
* successful claim. Apply the same guarantee to `revoke` when it is the fallback.
|
|
110
|
+
*
|
|
111
|
+
* Rotation is bookkeeping inside a **continuing** grant: the person stays
|
|
112
|
+
* signed in and the successor token takes over. Implement this when
|
|
113
|
+
* {@linkcode revoke} carries end-of-grant side effects — ending the session
|
|
114
|
+
* the token belongs to, cascading to sibling records — so a routine refresh
|
|
115
|
+
* cannot terminate the very session it renews. When absent, the refresh
|
|
116
|
+
* grant falls back to {@linkcode revoke}.
|
|
117
|
+
*/
|
|
118
|
+
revokeRotated?(token: RefreshToken<Client, User, Scope>): Promise<boolean>;
|
|
119
|
+
/**
|
|
120
|
+
* Revokes all tokens issued with the given authorization code.
|
|
121
|
+
* Returns true if any tokens were revoked (indicating code replay).
|
|
122
|
+
*
|
|
123
|
+
* Per RFC 6819 Section 4.4.1.1, if an authorization code is used more
|
|
124
|
+
* than once, the authorization server MUST revoke all tokens previously
|
|
125
|
+
* issued based on that authorization code.
|
|
126
|
+
*
|
|
127
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.1.1
|
|
128
|
+
*/
|
|
129
|
+
revokeCode(code: string): Promise<boolean>;
|
|
130
|
+
/**
|
|
131
|
+
* Looks up a refresh token that has already been revoked/rotated out —
|
|
132
|
+
* the signal the refresh grant's reuse detection needs. Implement this
|
|
133
|
+
* (with {@linkcode revokeFamily}) by keeping revoked refresh-token records
|
|
134
|
+
* findable instead of deleting them; when absent, reuse detection is
|
|
135
|
+
* silently disabled.
|
|
136
|
+
*/
|
|
137
|
+
getRevokedRefreshToken?(refreshToken: string): Promise<RefreshToken<Client, User, Scope> | undefined>;
|
|
138
|
+
/**
|
|
139
|
+
* Revokes every token belonging to a rotation family — called by the
|
|
140
|
+
* refresh grant when a rotated-out member is replayed, so a stolen token
|
|
141
|
+
* can't keep a session alive. Returns true if anything was revoked.
|
|
142
|
+
*/
|
|
143
|
+
revokeFamily?(familyId: string): Promise<boolean>;
|
|
144
|
+
}
|
|
145
|
+
/** Options for configuring an abstract token service. */
|
|
146
|
+
export interface AbstractTokenServiceOptions<Client extends ClientInterface, User> {
|
|
147
|
+
/** Lifetime of access tokens in seconds. Defaults to 3600 (1 hour). */
|
|
148
|
+
accessTokenLifetime?: number;
|
|
149
|
+
/** Lifetime of refresh tokens in seconds. Defaults to 1209600 (14 days). */
|
|
150
|
+
refreshTokenLifetime?: number;
|
|
151
|
+
/**
|
|
152
|
+
* Absolute lifetime of a refresh-token family in seconds, read by this
|
|
153
|
+
* class's {@linkcode AbstractTokenService.refreshTokenFamilyExpiresAt}. Omit
|
|
154
|
+
* for the historical behavior, an uncapped sliding window. Must be at least
|
|
155
|
+
* `refreshTokenLifetime`.
|
|
156
|
+
*/
|
|
157
|
+
refreshTokenMaxLifetime?: number;
|
|
158
|
+
/** Client service for hydrating tokens. */
|
|
159
|
+
clientService?: ClientServiceInterface<Client, User>;
|
|
160
|
+
/** User service for hydrating tokens. */
|
|
161
|
+
userService?: UserServiceInterface<User>;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Abstract token service with sensible defaults for token generation.
|
|
165
|
+
*
|
|
166
|
+
* Provides default implementations for:
|
|
167
|
+
* - `acceptedScope` — accepts scope as-is
|
|
168
|
+
* - `generateAccessToken` / `generateRefreshToken` — crypto.randomUUID()
|
|
169
|
+
* - `accessTokenExpiresAt` / `refreshTokenExpiresAt` — based on lifetime settings
|
|
170
|
+
*
|
|
171
|
+
* Subclasses must implement the storage methods:
|
|
172
|
+
* `getToken`, `getRefreshToken`, `save`, `revoke`, `revokeCode`
|
|
173
|
+
*/
|
|
174
|
+
export declare abstract class AbstractTokenService<Client extends ClientInterface, User, Scope extends AbstractScope = BasicScope> implements TokenServiceInterface<Client, User, Scope> {
|
|
175
|
+
/** Lifetime of access tokens in seconds. */
|
|
176
|
+
accessTokenLifetime: number;
|
|
177
|
+
/** Lifetime of refresh tokens in seconds. */
|
|
178
|
+
refreshTokenLifetime: number;
|
|
179
|
+
/**
|
|
180
|
+
* Absolute lifetime of a refresh-token family in seconds, or `undefined` for
|
|
181
|
+
* an uncapped family. Enforced only through this class's
|
|
182
|
+
* {@linkcode refreshTokenFamilyExpiresAt}: an override that ignores it
|
|
183
|
+
* ignores the cap.
|
|
184
|
+
*/
|
|
185
|
+
refreshTokenMaxLifetime?: number;
|
|
186
|
+
/** Client service used by subclasses to hydrate tokens. */
|
|
187
|
+
protected clientService?: ClientServiceInterface<Client, User>;
|
|
188
|
+
/** User service used by subclasses to hydrate tokens. */
|
|
189
|
+
protected userService?: UserServiceInterface<User>;
|
|
190
|
+
/**
|
|
191
|
+
* Lifetimes default to 3600 seconds (access) and 1209600 seconds / 14 days
|
|
192
|
+
* (refresh) when not supplied. `refreshTokenMaxLifetime` has no default —
|
|
193
|
+
* refresh-token families are uncapped unless one is configured.
|
|
194
|
+
*
|
|
195
|
+
* @throws {TypeError} If `refreshTokenLifetime` exceeds
|
|
196
|
+
* `refreshTokenMaxLifetime`, which would truncate every issued refresh token.
|
|
197
|
+
*/
|
|
198
|
+
constructor(options?: AbstractTokenServiceOptions<Client, User>);
|
|
199
|
+
/**
|
|
200
|
+
* Accepts the requested scope unchanged. Override to narrow or reject scope
|
|
201
|
+
* (return `false` to reject with `invalid_scope`).
|
|
202
|
+
*/
|
|
203
|
+
acceptedScope(_client: Client, _user: User | undefined, scope?: Scope | null): Promise<Scope | null | undefined | false>;
|
|
204
|
+
/** Generates an opaque access token string. Override to issue a different format. */
|
|
205
|
+
generateAccessToken(_client: Client, _user: User | undefined, _scope?: Scope | null, _authenticationContext?: AuthenticationContext): Promise<string>;
|
|
206
|
+
/** Generates an opaque refresh token string. Override to issue a different format. */
|
|
207
|
+
generateRefreshToken(_client: Client, _user: User, _scope?: Scope | null): Promise<string | undefined>;
|
|
208
|
+
/** Computes the access token expiry as now plus {@linkcode accessTokenLifetime}. */
|
|
209
|
+
accessTokenExpiresAt(_client: Client, _user: User | undefined, _scope?: Scope | null): Promise<Date | undefined>;
|
|
210
|
+
/** Computes the refresh token expiry as now plus {@linkcode refreshTokenLifetime}. */
|
|
211
|
+
refreshTokenExpiresAt(_client: Client, _user: User, _scope?: Scope | null): Promise<Date | undefined>;
|
|
212
|
+
/**
|
|
213
|
+
* Computes the family's absolute expiry as its anchor plus
|
|
214
|
+
* {@linkcode refreshTokenMaxLifetime}, or `undefined` when no cap is
|
|
215
|
+
* configured. Override to cap per client instead of service-wide.
|
|
216
|
+
*/
|
|
217
|
+
refreshTokenFamilyExpiresAt(_client: Client, _user: User | undefined, familyCreatedAt: Date, _scope?: Scope | null): Promise<Date | undefined>;
|
|
218
|
+
/** Retrieves a persisted token by its access token string. */
|
|
219
|
+
abstract getToken(accessToken: string): Promise<Token<Client, User, Scope> | undefined>;
|
|
220
|
+
/** Retrieves a persisted token by its refresh token string. */
|
|
221
|
+
abstract getRefreshToken(refreshToken: string): Promise<RefreshToken<Client, User, Scope> | undefined>;
|
|
222
|
+
/** Persists a refresh token and returns the saved record. */
|
|
223
|
+
abstract save(token: RefreshToken<Client, User, Scope>): Promise<RefreshToken<Client, User, Scope>>;
|
|
224
|
+
/** Persists an access token and returns the saved record. */
|
|
225
|
+
abstract save(token: Token<Client, User, Scope>): Promise<Token<Client, User, Scope>>;
|
|
226
|
+
/** Persists either token kind and returns the saved record. */
|
|
227
|
+
abstract save(token: Token<Client, User, Scope> | RefreshToken<Client, User, Scope>): Promise<Token<Client, User, Scope> | RefreshToken<Client, User, Scope>>;
|
|
228
|
+
/** Revokes a token record. Resolves true if a token was revoked. */
|
|
229
|
+
abstract revoke(token: Token<Client, User, Scope> | RefreshToken<Client, User, Scope>): Promise<boolean>;
|
|
230
|
+
/**
|
|
231
|
+
* Revokes a token by string, using `hint` (`access_token`/`refresh_token`,
|
|
232
|
+
* RFC 7009 §2.1) to disambiguate which kind to look up first.
|
|
233
|
+
*/
|
|
234
|
+
abstract revoke(token: string, hint?: string | null): Promise<boolean>;
|
|
235
|
+
/** Revokes a token given as a record or string. Resolves true if revoked. */
|
|
236
|
+
abstract revoke(token: Token<Client, User, Scope> | RefreshToken<Client, User, Scope> | string, hint?: string | null): Promise<boolean>;
|
|
237
|
+
/**
|
|
238
|
+
* Revokes every token issued from the given authorization code, returning
|
|
239
|
+
* true if any were revoked (signaling code replay per RFC 6819 §4.4.1.1).
|
|
240
|
+
*/
|
|
241
|
+
abstract revokeCode(code: string): Promise<boolean>;
|
|
242
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract token service with sensible defaults for token generation.
|
|
3
|
+
*
|
|
4
|
+
* Provides default implementations for:
|
|
5
|
+
* - `acceptedScope` — accepts scope as-is
|
|
6
|
+
* - `generateAccessToken` / `generateRefreshToken` — crypto.randomUUID()
|
|
7
|
+
* - `accessTokenExpiresAt` / `refreshTokenExpiresAt` — based on lifetime settings
|
|
8
|
+
*
|
|
9
|
+
* Subclasses must implement the storage methods:
|
|
10
|
+
* `getToken`, `getRefreshToken`, `save`, `revoke`, `revokeCode`
|
|
11
|
+
*/
|
|
12
|
+
export class AbstractTokenService {
|
|
13
|
+
/** Lifetime of access tokens in seconds. */
|
|
14
|
+
accessTokenLifetime;
|
|
15
|
+
/** Lifetime of refresh tokens in seconds. */
|
|
16
|
+
refreshTokenLifetime;
|
|
17
|
+
/**
|
|
18
|
+
* Absolute lifetime of a refresh-token family in seconds, or `undefined` for
|
|
19
|
+
* an uncapped family. Enforced only through this class's
|
|
20
|
+
* {@linkcode refreshTokenFamilyExpiresAt}: an override that ignores it
|
|
21
|
+
* ignores the cap.
|
|
22
|
+
*/
|
|
23
|
+
refreshTokenMaxLifetime;
|
|
24
|
+
/** Client service used by subclasses to hydrate tokens. */
|
|
25
|
+
clientService;
|
|
26
|
+
/** User service used by subclasses to hydrate tokens. */
|
|
27
|
+
userService;
|
|
28
|
+
/**
|
|
29
|
+
* Lifetimes default to 3600 seconds (access) and 1209600 seconds / 14 days
|
|
30
|
+
* (refresh) when not supplied. `refreshTokenMaxLifetime` has no default —
|
|
31
|
+
* refresh-token families are uncapped unless one is configured.
|
|
32
|
+
*
|
|
33
|
+
* @throws {TypeError} If `refreshTokenLifetime` exceeds
|
|
34
|
+
* `refreshTokenMaxLifetime`, which would truncate every issued refresh token.
|
|
35
|
+
*/
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.accessTokenLifetime = options?.accessTokenLifetime ?? 3600;
|
|
38
|
+
this.refreshTokenLifetime = options?.refreshTokenLifetime ?? 1209600;
|
|
39
|
+
this.refreshTokenMaxLifetime = options?.refreshTokenMaxLifetime;
|
|
40
|
+
if (this.refreshTokenMaxLifetime !== undefined &&
|
|
41
|
+
this.refreshTokenLifetime > this.refreshTokenMaxLifetime) {
|
|
42
|
+
throw new TypeError("refreshTokenLifetime must not exceed refreshTokenMaxLifetime");
|
|
43
|
+
}
|
|
44
|
+
this.clientService = options?.clientService;
|
|
45
|
+
this.userService = options?.userService;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Accepts the requested scope unchanged. Override to narrow or reject scope
|
|
49
|
+
* (return `false` to reject with `invalid_scope`).
|
|
50
|
+
*/
|
|
51
|
+
acceptedScope(_client, _user, scope) {
|
|
52
|
+
return Promise.resolve(scope);
|
|
53
|
+
}
|
|
54
|
+
/** Generates an opaque access token string. Override to issue a different format. */
|
|
55
|
+
generateAccessToken(_client, _user, _scope, _authenticationContext) {
|
|
56
|
+
return Promise.resolve(crypto.randomUUID());
|
|
57
|
+
}
|
|
58
|
+
/** Generates an opaque refresh token string. Override to issue a different format. */
|
|
59
|
+
generateRefreshToken(_client, _user, _scope) {
|
|
60
|
+
return Promise.resolve(crypto.randomUUID());
|
|
61
|
+
}
|
|
62
|
+
/** Computes the access token expiry as now plus {@linkcode accessTokenLifetime}. */
|
|
63
|
+
accessTokenExpiresAt(_client, _user, _scope) {
|
|
64
|
+
return Promise.resolve(new Date(Date.now() + this.accessTokenLifetime * 1000));
|
|
65
|
+
}
|
|
66
|
+
/** Computes the refresh token expiry as now plus {@linkcode refreshTokenLifetime}. */
|
|
67
|
+
refreshTokenExpiresAt(_client, _user, _scope) {
|
|
68
|
+
return Promise.resolve(new Date(Date.now() + this.refreshTokenLifetime * 1000));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Computes the family's absolute expiry as its anchor plus
|
|
72
|
+
* {@linkcode refreshTokenMaxLifetime}, or `undefined` when no cap is
|
|
73
|
+
* configured. Override to cap per client instead of service-wide.
|
|
74
|
+
*/
|
|
75
|
+
refreshTokenFamilyExpiresAt(_client, _user, familyCreatedAt, _scope) {
|
|
76
|
+
return Promise.resolve(this.refreshTokenMaxLifetime == null ? undefined : new Date(familyCreatedAt.getTime() + this.refreshTokenMaxLifetime * 1000));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service interface for user management.
|
|
3
|
+
* Provides user lookup and authentication.
|
|
4
|
+
*/
|
|
5
|
+
export interface UserServiceInterface<User> {
|
|
6
|
+
/** Retrieves a user by their unique identifier. */
|
|
7
|
+
get(id: string): Promise<User | undefined>;
|
|
8
|
+
/**
|
|
9
|
+
* Retrieves an authenticated user if the username/password combination is
|
|
10
|
+
* correct.
|
|
11
|
+
*
|
|
12
|
+
* Only the password grant calls this, and the grant issues tokens for
|
|
13
|
+
* whatever user this returns — it applies **no second-factor check of its
|
|
14
|
+
* own**. This method is therefore the only place an MFA policy can be
|
|
15
|
+
* enforced on that flow: return `undefined` or throw for a user who owes a
|
|
16
|
+
* second factor, or accept that their password alone mints tokens.
|
|
17
|
+
*
|
|
18
|
+
* Throwing an OAuth2 error (e.g. `InvalidGrantError`) surfaces it on the
|
|
19
|
+
* token response as that error; anything else becomes a `server_error`.
|
|
20
|
+
*/
|
|
21
|
+
getAuthenticated(username: string, password: string): Promise<User | undefined>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The password hashing primitives, re-exported from the identity layer so a
|
|
25
|
+
* credential minted through one entrypoint verifies through the other. See
|
|
26
|
+
* `@udibo/oauth2/identity` for the work-factor constants and the
|
|
27
|
+
* `PasswordIdentityService` that records them on the credential.
|
|
28
|
+
*/
|
|
29
|
+
export { generateSalt, hashPassword, verifyPassword, } from "../../identity/password.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The password hashing primitives, re-exported from the identity layer so a
|
|
3
|
+
* credential minted through one entrypoint verifies through the other. See
|
|
4
|
+
* `@udibo/oauth2/identity` for the work-factor constants and the
|
|
5
|
+
* `PasswordIdentityService` that records them on the credential.
|
|
6
|
+
*/
|
|
7
|
+
export { generateSalt, hashPassword, verifyPassword, } from "../../identity/password.js";
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OIDC signing keys and compact JWS signing — Web Crypto only, no
|
|
3
|
+
* dependencies. Backs id_token issuance, the JWKS endpoint, and the optional
|
|
4
|
+
* signed (JWT) access-token generator.
|
|
5
|
+
*
|
|
6
|
+
* Keys are ES256 (the OIDC-recommended default with broad library support).
|
|
7
|
+
* Generate once with {@link generateSigningKey}, persist via
|
|
8
|
+
* {@link exportSigningKeyJwk}, and load on boot with {@link importSigningKeyJwk}
|
|
9
|
+
* so every instance of a multi-instance deploy signs with the same key (an
|
|
10
|
+
* ephemeral per-instance key would fail verification across instances).
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import type { AuthenticationContext } from "../models/authentication.js";
|
|
15
|
+
/** A ready-to-use signing key: private half signs, public JWK is published. */
|
|
16
|
+
export interface SigningKey {
|
|
17
|
+
/** Key id, stamped into JWS headers and the JWKS entry. */
|
|
18
|
+
kid: string;
|
|
19
|
+
/** JWS algorithm. Always `ES256` today. */
|
|
20
|
+
alg: "ES256";
|
|
21
|
+
/** The private key used to sign. */
|
|
22
|
+
privateKey: CryptoKey;
|
|
23
|
+
/** The public JWK (no private material) served from the JWKS endpoint. */
|
|
24
|
+
publicJwk: JsonWebKey & {
|
|
25
|
+
kid: string;
|
|
26
|
+
alg: string;
|
|
27
|
+
use: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Provides the server's current signing key. A provider seam so key storage
|
|
32
|
+
* and rotation stay app-owned; {@link StaticSigningKeyProvider} covers the
|
|
33
|
+
* single-key case.
|
|
34
|
+
*/
|
|
35
|
+
export interface SigningKeyProvider {
|
|
36
|
+
/** The key new tokens are signed with. */
|
|
37
|
+
getSigningKey(): Promise<SigningKey>;
|
|
38
|
+
/** Every public key a verifier may need (current + not-yet-expired old). */
|
|
39
|
+
getPublicJwks(): Promise<{
|
|
40
|
+
keys: JsonWebKey[];
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/** A {@link SigningKeyProvider} over one fixed key. */
|
|
44
|
+
export declare class StaticSigningKeyProvider implements SigningKeyProvider {
|
|
45
|
+
#private;
|
|
46
|
+
/** Fixes the single key this provider signs with and publishes. */
|
|
47
|
+
constructor(key: SigningKey);
|
|
48
|
+
/** The fixed key passed at construction. */
|
|
49
|
+
getSigningKey(): Promise<SigningKey>;
|
|
50
|
+
/** A JWKS containing only the fixed key's public JWK. */
|
|
51
|
+
getPublicJwks(): Promise<{
|
|
52
|
+
keys: JsonWebKey[];
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
/** Options for {@link RotatingSigningKeyProvider}. */
|
|
56
|
+
export interface RotatingSigningKeyProviderOptions {
|
|
57
|
+
/** The key new tokens are signed with. Its `kid` is stamped into every JWS. */
|
|
58
|
+
current: SigningKey;
|
|
59
|
+
/**
|
|
60
|
+
* Keys retired from signing but still published in JWKS so tokens minted
|
|
61
|
+
* before the rotation keep verifying. Omit (or pass `[]`) for the single-key
|
|
62
|
+
* steady state, where this provider behaves exactly like
|
|
63
|
+
* {@link StaticSigningKeyProvider}.
|
|
64
|
+
*/
|
|
65
|
+
previous?: SigningKey[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A {@link SigningKeyProvider} for rotating the signing key through a JWKS
|
|
69
|
+
* grace window: sign with {@link RotatingSigningKeyProviderOptions.current}
|
|
70
|
+
* only, while {@link RotatingSigningKeyProviderOptions.previous} keys stay
|
|
71
|
+
* published so outstanding tokens keep validating until they expire and every
|
|
72
|
+
* consumer has refetched JWKS.
|
|
73
|
+
*
|
|
74
|
+
* Rotation is: promote the new key to `current`, move the old one into
|
|
75
|
+
* `previous`, and hold that grace window for at least the longest token
|
|
76
|
+
* lifetime before dropping the old key from `previous`. Because
|
|
77
|
+
* {@link signJwt} stamps the current key's `kid` into every JWS header and each
|
|
78
|
+
* published JWK carries its own `kid`, verifiers select the right key by `kid`
|
|
79
|
+
* across the window. Keys are published current-first and de-duplicated by
|
|
80
|
+
* `kid`.
|
|
81
|
+
*
|
|
82
|
+
* A single-key configuration (`previous` empty or omitted) publishes exactly
|
|
83
|
+
* the one key and is behaviourally identical to
|
|
84
|
+
* {@link StaticSigningKeyProvider} — adopting this provider does not change a
|
|
85
|
+
* deployment that has not started a rotation.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* const provider = new RotatingSigningKeyProvider({
|
|
90
|
+
* current: await importSigningKeyJwk(JSON.parse(currentJwk)),
|
|
91
|
+
* previous: [await importSigningKeyJwk(JSON.parse(oldJwk))],
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare class RotatingSigningKeyProvider implements SigningKeyProvider {
|
|
96
|
+
#private;
|
|
97
|
+
/** Fixes the current signing key and the published set (current + previous). */
|
|
98
|
+
constructor(options: RotatingSigningKeyProviderOptions);
|
|
99
|
+
/** The current key — the only key new tokens are ever signed with. */
|
|
100
|
+
getSigningKey(): Promise<SigningKey>;
|
|
101
|
+
/** Public JWKs for the current key and every still-published previous key. */
|
|
102
|
+
getPublicJwks(): Promise<{
|
|
103
|
+
keys: JsonWebKey[];
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
106
|
+
/** Maps a user to a JWT `sub` claim: the user's `id` property, else the user itself. */
|
|
107
|
+
export declare function defaultSubjectOf(user: unknown): string;
|
|
108
|
+
/** Generate a fresh ES256 signing key with a random `kid`. */
|
|
109
|
+
export declare function generateSigningKey(): Promise<SigningKey>;
|
|
110
|
+
/**
|
|
111
|
+
* Export a signing key (including private material) as a JWK for persistence
|
|
112
|
+
* — e.g. into a secret-manager entry the app loads on boot. Guard it like any
|
|
113
|
+
* credential.
|
|
114
|
+
*/
|
|
115
|
+
export declare function exportSigningKeyJwk(key: SigningKey): Promise<JsonWebKey & {
|
|
116
|
+
kid: string;
|
|
117
|
+
}>;
|
|
118
|
+
/** Import a signing key previously exported with {@link exportSigningKeyJwk}. */
|
|
119
|
+
export declare function importSigningKeyJwk(jwk: JsonWebKey & {
|
|
120
|
+
kid?: string;
|
|
121
|
+
}): Promise<SigningKey>;
|
|
122
|
+
/**
|
|
123
|
+
* Sign a compact JWS (`header.payload.signature`) with the given key —
|
|
124
|
+
* the primitive behind id_tokens and JWT access tokens.
|
|
125
|
+
*/
|
|
126
|
+
export declare function signJwt(key: SigningKey, payload: Record<string, unknown>, header?: Record<string, unknown>): Promise<string>;
|
|
127
|
+
/** Options for {@link verifyJwt}. */
|
|
128
|
+
export interface VerifyJwtOptions {
|
|
129
|
+
/**
|
|
130
|
+
* Accept a payload whose `exp` has passed. Only for a token used as a
|
|
131
|
+
* **hint** rather than as authority — OIDC RP-Initiated Logout's
|
|
132
|
+
* `id_token_hint` is the case this exists for, since the id_token naming the
|
|
133
|
+
* session to end has usually outlived its own lifetime by the time anyone
|
|
134
|
+
* logs out. Never set it for a token that authorizes something.
|
|
135
|
+
*
|
|
136
|
+
* @default false
|
|
137
|
+
*/
|
|
138
|
+
ignoreExpiration?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Verify a compact JWS against a public JWK and return its payload, or
|
|
142
|
+
* `undefined` when the signature (or shape) is invalid or the payload's `exp`
|
|
143
|
+
* is in the past. Exported for tests and lightweight in-process verification;
|
|
144
|
+
* production resource servers should verify against the JWKS endpoint with
|
|
145
|
+
* their JOSE library of choice.
|
|
146
|
+
*/
|
|
147
|
+
export declare function verifyJwt(jwt: string, publicJwk: JsonWebKey, options?: VerifyJwtOptions): Promise<Record<string, unknown> | undefined>;
|
|
148
|
+
/**
|
|
149
|
+
* Build a `generateAccessToken` implementation that issues **signed JWT
|
|
150
|
+
* access tokens** (RFC 9068 `at+jwt`) instead of opaque strings — the
|
|
151
|
+
* "verifiable signed-token option": resource servers can validate offline
|
|
152
|
+
* against the JWKS endpoint. Wire it into your token service:
|
|
153
|
+
*
|
|
154
|
+
* ```ts
|
|
155
|
+
* class JwtTokenService extends MyTokenService {
|
|
156
|
+
* override generateAccessToken = createJwtAccessTokenGenerator({
|
|
157
|
+
* signingKeys, issuer: "https://auth.example.com",
|
|
158
|
+
* });
|
|
159
|
+
* }
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* The token store still persists the (hashed) JWT string, so revocation and
|
|
163
|
+
* introspection keep working exactly as with opaque tokens.
|
|
164
|
+
*/
|
|
165
|
+
export declare function createJwtAccessTokenGenerator(options: {
|
|
166
|
+
/** Keys the tokens are signed with (also served from JWKS). */
|
|
167
|
+
signingKeys: SigningKeyProvider;
|
|
168
|
+
/** The `iss` claim — must match the advertised issuer. */
|
|
169
|
+
issuer: string;
|
|
170
|
+
/**
|
|
171
|
+
* The `aud` claim — per RFC 9068 an identifier of the resource server(s)
|
|
172
|
+
* the token is intended for (e.g. `https://api.example.com`). Defaults to
|
|
173
|
+
* the client id, which single-resource validators must then accept.
|
|
174
|
+
*/
|
|
175
|
+
audience?: string;
|
|
176
|
+
/** Token lifetime in seconds for the `exp` claim. Defaults to 3600. */
|
|
177
|
+
lifetimeSeconds?: number;
|
|
178
|
+
/** Maps a user to the `sub` claim. Defaults to the user's `id` property. */
|
|
179
|
+
subjectOf?: (user: unknown) => string;
|
|
180
|
+
/**
|
|
181
|
+
* Extra claims for the resource owner's tokens — the same seam
|
|
182
|
+
* `AuthorizationServer`'s `userClaims` option gives the id_token, so one
|
|
183
|
+
* claims computation can feed both. Called only when a user is present: a
|
|
184
|
+
* client-credentials token has no resource owner, so it never carries user
|
|
185
|
+
* claims. The protocol claims (`iss`, `sub`, `aud`, `client_id`, `iat`,
|
|
186
|
+
* `exp`, `jti`, `scope`) always win — a returned claim under one of those
|
|
187
|
+
* names is discarded, never merged.
|
|
188
|
+
* The fourth argument is the token's recorded authentication event. Return
|
|
189
|
+
* its fields to emit them, without consulting a newer session. Custom token
|
|
190
|
+
* service wrappers must forward the fourth `generateAccessToken` argument.
|
|
191
|
+
*/
|
|
192
|
+
userClaims?: (user: unknown, scope?: {
|
|
193
|
+
toString(): string;
|
|
194
|
+
} | null, client?: {
|
|
195
|
+
id: string;
|
|
196
|
+
}, authenticationContext?: AuthenticationContext) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
197
|
+
}): (client: {
|
|
198
|
+
id: string;
|
|
199
|
+
}, user: unknown, scope?: {
|
|
200
|
+
toString(): string;
|
|
201
|
+
} | null, authenticationContext?: AuthenticationContext) => Promise<string>;
|