@udibo/oauth2 0.14.1 → 0.15.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/README.md +45 -24
- package/{esm → dist}/adapters/hono/_common.d.ts +5 -5
- package/{esm → dist}/adapters/hono/_common.js +1 -1
- package/{esm → dist}/adapters/hono/authorization-server.d.ts +6 -7
- package/{esm → dist}/adapters/hono/authorization-server.js +0 -1
- package/{esm → dist}/adapters/hono/bff/auth-request-store.d.ts +2 -2
- package/{esm → dist}/adapters/hono/bff/auth-request-store.js +2 -2
- package/{esm → dist}/adapters/hono/bff/bff.d.ts +8 -8
- package/{esm → dist}/adapters/hono/bff/bff.js +31 -26
- package/{esm → dist}/adapters/hono/bff/cookie-prefix.js +1 -2
- package/{esm → dist}/adapters/hono/bff/mod.d.ts +5 -6
- package/{esm → dist}/adapters/hono/bff/mod.js +1 -2
- package/{esm → dist}/adapters/hono/bff/proxy.js +2 -1
- package/{esm → dist}/adapters/hono/bff/session-store.d.ts +2 -2
- package/{esm → dist}/adapters/hono/bff/session-store.js +7 -6
- package/{esm → dist}/adapters/hono/bff/testing.d.ts +7 -8
- package/{esm → dist}/adapters/hono/bff/testing.js +19 -22
- package/{esm → dist}/adapters/hono/identity.d.ts +1 -2
- package/{esm → dist}/adapters/hono/identity.js +10 -10
- package/{esm → dist}/adapters/hono/log.d.ts +0 -1
- package/{esm → dist}/adapters/hono/log.js +0 -6
- package/{esm → dist}/adapters/hono/resource-server.d.ts +5 -6
- package/{esm → dist}/adapters/hono/resource-server.js +0 -1
- package/dist/cli/bin.d.ts +2 -0
- package/dist/cli/bin.js +3 -0
- package/dist/cli/idp/config.d.ts +110 -0
- package/dist/cli/idp/config.js +269 -0
- package/dist/cli/idp/mod.d.ts +20 -0
- package/dist/cli/idp/mod.js +175 -0
- package/dist/cli/idp/pages.d.ts +56 -0
- package/dist/cli/idp/pages.js +190 -0
- package/dist/cli/idp/server.d.ts +93 -0
- package/dist/cli/idp/server.js +559 -0
- package/dist/cli/keygen.d.ts +13 -0
- package/dist/cli/keygen.js +30 -0
- package/dist/cli/mod.d.ts +34 -0
- package/dist/cli/mod.js +94 -0
- package/{esm → dist}/client/_http.d.ts +1 -1
- package/{esm → dist}/client/base.d.ts +1 -1
- package/{esm → dist}/client/bff-client.d.ts +1 -1
- package/{esm → dist}/client/bff-client.js +7 -6
- package/{esm → dist}/client/browser-storage.d.ts +1 -1
- package/{esm → dist}/client/check.js +1 -1
- package/{esm → dist}/client/direct-client.d.ts +6 -6
- package/{esm → dist}/client/direct-client.js +11 -13
- package/{esm → dist}/client/discovery-cache.d.ts +1 -1
- package/{esm → dist}/client/discovery-cache.js +2 -2
- package/{esm → dist}/client/events.d.ts +1 -1
- package/{esm → dist}/client/mod.d.ts +11 -12
- package/{esm → dist}/client/mod.js +1 -1
- package/{esm → dist}/errors.d.ts +1 -1
- package/{esm → dist}/errors.js +1 -1
- package/{esm → dist}/identity/events.d.ts +1 -1
- package/{esm → dist}/identity/external/_shared.d.ts +1 -1
- package/{esm → dist}/identity/external/_shared.js +1 -2
- package/{esm → dist}/identity/external/apple-client-secret.d.ts +1 -1
- package/{esm → dist}/identity/external/apple-client-secret.js +6 -7
- package/{esm → dist}/identity/external/apple.d.ts +5 -5
- package/{esm → dist}/identity/external/apple.js +10 -11
- package/{esm → dist}/identity/external/discord.d.ts +3 -3
- package/{esm → dist}/identity/external/discord.js +2 -2
- package/{esm → dist}/identity/external/flow.d.ts +1 -1
- package/{esm → dist}/identity/external/flow.js +2 -2
- package/{esm → dist}/identity/external/github.d.ts +3 -3
- package/{esm → dist}/identity/external/github.js +12 -17
- package/{esm → dist}/identity/external/google.d.ts +5 -5
- package/{esm → dist}/identity/external/google.js +2 -2
- package/{esm → dist}/identity/external/mod.d.ts +12 -13
- package/{esm → dist}/identity/external/mod.js +0 -1
- package/{esm → dist}/identity/external/oauth2.d.ts +1 -1
- package/{esm → dist}/identity/external/oauth2.js +8 -8
- package/{esm → dist}/identity/external/oidc.d.ts +4 -4
- package/{esm → dist}/identity/external/oidc.js +9 -8
- package/{esm → dist}/identity/hibp.d.ts +1 -1
- package/{esm → dist}/identity/hibp.js +1 -1
- package/{esm → dist}/identity/lockout.js +1 -2
- package/{esm → dist}/identity/mfa/mod.d.ts +4 -5
- package/{esm → dist}/identity/mfa/mod.js +1 -2
- package/{esm → dist}/identity/mfa/service.d.ts +3 -3
- package/{esm → dist}/identity/mfa/service.js +4 -3
- package/{esm → dist}/identity/mfa/totp.d.ts +1 -1
- package/{esm → dist}/identity/mfa/totp.js +2 -2
- package/{esm → dist}/identity/migration.d.ts +2 -3
- package/{esm → dist}/identity/migration.js +3 -4
- package/{esm → dist}/identity/mod.d.ts +14 -15
- package/{esm → dist}/identity/mod.js +0 -1
- package/{esm → dist}/identity/otp.d.ts +1 -1
- package/{esm → dist}/identity/otp.js +4 -2
- package/{esm → dist}/identity/password.js +8 -5
- package/{esm → dist}/identity/rate-limit.d.ts +1 -1
- package/{esm → dist}/identity/service.d.ts +10 -10
- package/{esm → dist}/identity/service.js +17 -18
- package/{esm → dist}/identity/session.js +2 -2
- package/{esm → dist}/identity/token-flow.js +3 -2
- package/{esm → dist}/models/authorization-code.d.ts +3 -3
- package/{esm → dist}/models/authorization.d.ts +1 -1
- package/{esm → dist}/models/authorization.js +1 -2
- package/{esm → dist}/models/device-authorization.d.ts +2 -2
- package/{esm → dist}/models/token.d.ts +3 -3
- package/{esm → dist}/react/callback.d.ts +1 -1
- package/{esm → dist}/react/callback.js +0 -1
- package/{esm → dist}/react/components/class-names.d.ts +1 -1
- package/{esm → dist}/react/components/class-names.js +3 -2
- package/{esm → dist}/react/components/internal.d.ts +2 -2
- package/{esm → dist}/react/components/internal.js +5 -11
- package/{esm → dist}/react/components/mfa-challenge-form.d.ts +2 -2
- package/{esm → dist}/react/components/mfa-challenge-form.js +4 -6
- package/{esm → dist}/react/components/mfa-enrollment-form.d.ts +2 -2
- package/{esm → dist}/react/components/mfa-enrollment-form.js +2 -6
- package/{esm → dist}/react/components/mod.d.ts +20 -21
- package/{esm → dist}/react/components/mod.js +1 -2
- package/{esm → dist}/react/components/request-password-reset-form.d.ts +2 -2
- package/{esm → dist}/react/components/request-password-reset-form.js +1 -3
- package/{esm → dist}/react/components/reset-password-form.d.ts +2 -2
- package/{esm → dist}/react/components/reset-password-form.js +2 -2
- package/{esm → dist}/react/components/sign-in-form.d.ts +2 -2
- package/{esm → dist}/react/components/sign-in-form.js +1 -5
- package/{esm → dist}/react/components/sign-up-form.d.ts +2 -2
- package/{esm → dist}/react/components/sign-up-form.js +4 -10
- package/{esm → dist}/react/components/types.d.ts +2 -2
- package/{esm → dist}/react/components/use-auth-form.d.ts +3 -3
- package/{esm → dist}/react/components/use-auth-form.js +4 -7
- package/{esm → dist}/react/components/user-menu.d.ts +2 -2
- package/{esm → dist}/react/components/user-menu.js +9 -14
- package/{esm → dist}/react/context.d.ts +1 -1
- package/{esm → dist}/react/mod.d.ts +6 -7
- package/{esm → dist}/react/mod.js +0 -1
- package/{esm → dist}/react/provider.d.ts +2 -2
- package/{esm → dist}/react/require-auth.js +1 -3
- package/{esm → dist}/react/testing.d.ts +2 -3
- package/{esm → dist}/react/testing.js +1 -2
- package/{esm → dist}/react/use-authorization.d.ts +1 -1
- package/{esm → dist}/react/use-oauth2.d.ts +8 -1
- package/{esm → dist}/react/use-oauth2.js +0 -1
- package/{esm → dist}/server/_clock-skew.js +2 -1
- package/{esm → dist}/server/authorization-server.d.ts +40 -41
- package/{esm → dist}/server/authorization-server.js +32 -42
- package/{esm → dist}/server/client-authentication.d.ts +2 -2
- package/{esm → dist}/server/grants/authorization-code.d.ts +9 -9
- package/{esm → dist}/server/grants/authorization-code.js +5 -6
- package/{esm → dist}/server/grants/client-credentials.d.ts +4 -4
- package/{esm → dist}/server/grants/device-authorization.d.ts +6 -6
- package/{esm → dist}/server/grants/device-authorization.js +4 -4
- package/{esm → dist}/server/grants/grant.d.ts +7 -7
- package/{esm → dist}/server/grants/grant.js +7 -7
- package/{esm → dist}/server/grants/password.d.ts +5 -5
- package/{esm → dist}/server/grants/refresh-token.d.ts +4 -4
- package/{esm → dist}/server/grants/refresh-token.js +5 -6
- package/{esm → dist}/server/introspection-token-reader.d.ts +5 -5
- package/{esm → dist}/server/introspection-token-reader.js +4 -4
- package/{esm → dist}/server/jwks-token-reader.d.ts +5 -5
- package/{esm → dist}/server/jwks-token-reader.js +4 -4
- package/{esm → dist}/server/mod.d.ts +12 -13
- package/{esm → dist}/server/mod.js +0 -1
- package/{esm → dist}/server/public-suffix/mod.d.ts +2 -3
- package/{esm → dist}/server/public-suffix/mod.js +1 -2
- package/{esm → dist}/server/public-suffix/rules.d.ts +1 -1
- package/{esm → dist}/server/public-suffix/rules.js +1 -1
- package/{esm → dist}/server/redirect-uri.js +5 -4
- package/{esm → dist}/server/resource-server.d.ts +11 -12
- package/{esm → dist}/server/resource-server.js +12 -14
- package/{esm → dist}/server/services/authorization-code.d.ts +5 -5
- package/{esm → dist}/server/services/client.d.ts +1 -1
- package/{esm → dist}/server/services/device-authorization.d.ts +3 -3
- package/{esm → dist}/server/services/token.d.ts +6 -6
- package/{esm → dist}/server/services/token.js +3 -1
- package/{esm → dist}/server/services/user.d.ts +1 -1
- package/{esm → dist}/server/signing-keys.d.ts +1 -1
- package/{esm → dist}/server/signing-keys.js +8 -2
- package/{esm → dist}/server/utils/hash.js +1 -1
- package/{esm → dist}/testing/contract/auth-request-storage.d.ts +1 -1
- package/{esm → dist}/testing/contract/auth-request-storage.js +24 -29
- package/{esm → dist}/testing/contract/authorization-code.d.ts +6 -6
- package/{esm → dist}/testing/contract/authorization-code.js +16 -17
- package/{esm → dist}/testing/contract/client.d.ts +4 -4
- package/{esm → dist}/testing/contract/client.js +14 -15
- package/{esm → dist}/testing/contract/device-authorization.d.ts +6 -6
- package/{esm → dist}/testing/contract/device-authorization.js +37 -39
- package/{esm → dist}/testing/contract/identity-session.d.ts +6 -1
- package/{esm → dist}/testing/contract/identity-session.js +44 -36
- package/{esm → dist}/testing/contract/identity-user-store.d.ts +8 -2
- package/{esm → dist}/testing/contract/identity-user-store.js +48 -46
- package/{esm → dist}/testing/contract/lockout-store.d.ts +1 -1
- package/{esm → dist}/testing/contract/lockout-store.js +23 -24
- package/{esm → dist}/testing/contract/mfa-store.d.ts +1 -1
- package/{esm → dist}/testing/contract/mfa-store.js +53 -54
- package/{esm → dist}/testing/contract/mod.d.ts +15 -16
- package/{esm → dist}/testing/contract/mod.js +0 -1
- package/{esm → dist}/testing/contract/otp-store.d.ts +1 -1
- package/{esm → dist}/testing/contract/otp-store.js +33 -34
- package/{esm → dist}/testing/contract/rate-limit-store.d.ts +1 -1
- package/{esm → dist}/testing/contract/rate-limit-store.js +15 -16
- package/{esm → dist}/testing/contract/tenant.js +361 -330
- package/{esm → dist}/testing/contract/token-flow-store.d.ts +1 -1
- package/{esm → dist}/testing/contract/token-flow-store.js +27 -32
- package/{esm → dist}/testing/contract/token-reader.d.ts +3 -3
- package/{esm → dist}/testing/contract/token-reader.js +6 -7
- package/{esm → dist}/testing/contract/token.d.ts +7 -7
- package/{esm → dist}/testing/contract/token.js +48 -53
- package/{esm → dist}/testing/contract/user.d.ts +2 -2
- package/{esm → dist}/testing/contract/user.js +8 -9
- package/{esm → dist}/testing/mod.d.ts +5 -6
- package/{esm → dist}/testing/mod.js +2 -3
- package/{esm → dist}/testing/server.d.ts +7 -6
- package/{esm → dist}/testing/server.js +2 -1
- package/{esm → dist}/testing/services.d.ts +11 -11
- package/{esm → dist}/testing/services.js +8 -8
- package/{esm → dist}/testing/tenant.d.ts +11 -7
- package/{esm → dist}/testing/tenant.js +109 -94
- package/dist/utils/_constant-time.d.ts +12 -0
- package/dist/utils/_constant-time.js +19 -0
- package/{esm → dist}/utils/_default-fetch.js +4 -1
- package/dist/utils/_delay.d.ts +13 -0
- package/dist/utils/_delay.js +28 -0
- package/dist/utils/_encoding.d.ts +39 -0
- package/dist/utils/_encoding.js +126 -0
- package/{esm → dist}/utils/authentication-context.d.ts +1 -1
- package/{esm → dist}/utils/crypto.d.ts +2 -3
- package/{esm → dist}/utils/crypto.js +3 -4
- package/{esm → dist}/utils/pkce.js +1 -1
- package/{esm → dist}/utils/text.js +1 -1
- package/{esm → dist}/utils/url.d.ts +0 -1
- package/{esm → dist}/utils/url.js +0 -1
- package/package.json +156 -33
- package/esm/_dnt.polyfills.d.ts +0 -31
- package/esm/_dnt.polyfills.js +0 -26
- package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.d.ts +0 -29
- package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.js +0 -47
- package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.d.ts +0 -28
- package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.js +0 -65
- package/esm/deps/jsr.io/@std/assert/1.0.19/assert.d.ts +0 -16
- package/esm/deps/jsr.io/@std/assert/1.0.19/assert.js +0 -23
- package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.d.ts +0 -25
- package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.js +0 -30
- package/esm/deps/jsr.io/@std/assert/1.0.19/equal.d.ts +0 -24
- package/esm/deps/jsr.io/@std/assert/1.0.19/equal.js +0 -193
- package/esm/deps/jsr.io/@std/assert/1.0.19/equals.d.ts +0 -36
- package/esm/deps/jsr.io/@std/assert/1.0.19/equals.js +0 -61
- package/esm/deps/jsr.io/@std/assert/1.0.19/exists.d.ts +0 -17
- package/esm/deps/jsr.io/@std/assert/1.0.19/exists.js +0 -27
- package/esm/deps/jsr.io/@std/assert/1.0.19/fail.d.ts +0 -14
- package/esm/deps/jsr.io/@std/assert/1.0.19/fail.js +0 -20
- package/esm/deps/jsr.io/@std/assert/1.0.19/false.d.ts +0 -18
- package/esm/deps/jsr.io/@std/assert/1.0.19/false.js +0 -23
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater.d.ts +0 -19
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater.js +0 -29
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.d.ts +0 -19
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.js +0 -29
- package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.d.ts +0 -22
- package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.js +0 -52
- package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.d.ts +0 -24
- package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.js +0 -53
- package/esm/deps/jsr.io/@std/assert/1.0.19/less.d.ts +0 -18
- package/esm/deps/jsr.io/@std/assert/1.0.19/less.js +0 -28
- package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.d.ts +0 -19
- package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.js +0 -29
- package/esm/deps/jsr.io/@std/assert/1.0.19/match.d.ts +0 -17
- package/esm/deps/jsr.io/@std/assert/1.0.19/match.js +0 -26
- package/esm/deps/jsr.io/@std/assert/1.0.19/mod.d.ts +0 -43
- package/esm/deps/jsr.io/@std/assert/1.0.19/mod.js +0 -45
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.d.ts +0 -26
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.js +0 -39
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.d.ts +0 -19
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.js +0 -29
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.d.ts +0 -17
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.js +0 -26
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.d.ts +0 -22
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.js +0 -32
- package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.d.ts +0 -28
- package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.js +0 -177
- package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.d.ts +0 -41
- package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.js +0 -53
- package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.d.ts +0 -23
- package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.js +0 -58
- package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.d.ts +0 -17
- package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.js +0 -26
- package/esm/deps/jsr.io/@std/assert/1.0.19/throws.d.ts +0 -44
- package/esm/deps/jsr.io/@std/assert/1.0.19/throws.js +0 -44
- package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.d.ts +0 -14
- package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.js +0 -20
- package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.d.ts +0 -14
- package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.js +0 -20
- package/esm/deps/jsr.io/@std/async/1.5.0/delay.d.ts +0 -44
- package/esm/deps/jsr.io/@std/async/1.5.0/delay.js +0 -84
- package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.d.ts +0 -39
- package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.js +0 -61
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.d.ts +0 -22
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.js +0 -51
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.d.ts +0 -34
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.js +0 -192
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.d.ts +0 -34
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.js +0 -113
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.d.ts +0 -3
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.js +0 -13
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.d.ts +0 -8
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.js +0 -2
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.d.ts +0 -1
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.js +0 -26
- package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.d.ts +0 -60
- package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.js +0 -152
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.d.ts +0 -39
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.js +0 -87
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.d.ts +0 -39
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.js +0 -131
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.d.ts +0 -39
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.js +0 -82
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.d.ts +0 -39
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.js +0 -72
- package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.d.ts +0 -38
- package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.js +0 -87
- package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.d.ts +0 -97
- package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.js +0 -99
- package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.d.ts +0 -119
- package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.js +0 -209
- package/esm/deps/jsr.io/@std/http/1.1.3/status.d.ts +0 -343
- package/esm/deps/jsr.io/@std/http/1.1.3/status.js +0 -343
- package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.d.ts +0 -159
- package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.js +0 -223
- package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.d.ts +0 -79
- package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.js +0 -116
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff.d.ts +0 -139
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff.js +0 -277
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.d.ts +0 -98
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.js +0 -181
- package/esm/deps/jsr.io/@std/internal/1.0.14/format.d.ts +0 -29
- package/esm/deps/jsr.io/@std/internal/1.0.14/format.js +0 -68
- package/esm/deps/jsr.io/@std/internal/1.0.14/styles.d.ts +0 -158
- package/esm/deps/jsr.io/@std/internal/1.0.14/styles.js +0 -206
- package/esm/deps/jsr.io/@std/internal/1.0.14/types.d.ts +0 -24
- package/esm/deps/jsr.io/@std/internal/1.0.14/types.js +0 -3
- package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.d.ts +0 -91
- package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.js +0 -347
- package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.d.ts +0 -382
- package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.js +0 -1110
- package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.d.ts +0 -363
- package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.js +0 -616
- package/esm/package.json +0 -3
- /package/{esm → dist}/adapters/hono/bff/cookie-prefix.d.ts +0 -0
- /package/{esm → dist}/adapters/hono/bff/proxy.d.ts +0 -0
- /package/{esm → dist}/client/_http.js +0 -0
- /package/{esm → dist}/client/base.js +0 -0
- /package/{esm → dist}/client/browser-storage.js +0 -0
- /package/{esm → dist}/client/check.d.ts +0 -0
- /package/{esm → dist}/client/events.js +0 -0
- /package/{esm → dist}/client/storage.d.ts +0 -0
- /package/{esm → dist}/client/storage.js +0 -0
- /package/{esm → dist}/identity/captcha.d.ts +0 -0
- /package/{esm → dist}/identity/captcha.js +0 -0
- /package/{esm → dist}/identity/delivery.d.ts +0 -0
- /package/{esm → dist}/identity/delivery.js +0 -0
- /package/{esm → dist}/identity/errors.d.ts +0 -0
- /package/{esm → dist}/identity/errors.js +0 -0
- /package/{esm → dist}/identity/events.js +0 -0
- /package/{esm → dist}/identity/external/_token-exchange.d.ts +0 -0
- /package/{esm → dist}/identity/external/_token-exchange.js +0 -0
- /package/{esm → dist}/identity/external/errors.d.ts +0 -0
- /package/{esm → dist}/identity/external/errors.js +0 -0
- /package/{esm → dist}/identity/external/provider.d.ts +0 -0
- /package/{esm → dist}/identity/external/provider.js +0 -0
- /package/{esm → dist}/identity/identifier.d.ts +0 -0
- /package/{esm → dist}/identity/identifier.js +0 -0
- /package/{esm → dist}/identity/lockout.d.ts +0 -0
- /package/{esm → dist}/identity/mfa/recovery-codes.d.ts +0 -0
- /package/{esm → dist}/identity/mfa/recovery-codes.js +0 -0
- /package/{esm → dist}/identity/password-policy.d.ts +0 -0
- /package/{esm → dist}/identity/password-policy.js +0 -0
- /package/{esm → dist}/identity/password.d.ts +0 -0
- /package/{esm → dist}/identity/rate-limit.js +0 -0
- /package/{esm → dist}/identity/session.d.ts +0 -0
- /package/{esm → dist}/identity/token-flow.d.ts +0 -0
- /package/{esm → dist}/models/authentication.d.ts +0 -0
- /package/{esm → dist}/models/authentication.js +0 -0
- /package/{esm → dist}/models/authorization-code.js +0 -0
- /package/{esm → dist}/models/client.d.ts +0 -0
- /package/{esm → dist}/models/client.js +0 -0
- /package/{esm → dist}/models/device-authorization.js +0 -0
- /package/{esm → dist}/models/responses.d.ts +0 -0
- /package/{esm → dist}/models/responses.js +0 -0
- /package/{esm → dist}/models/scope.d.ts +0 -0
- /package/{esm → dist}/models/scope.js +0 -0
- /package/{esm → dist}/models/token.js +0 -0
- /package/{esm → dist}/react/components/types.js +0 -0
- /package/{esm → dist}/react/context.js +0 -0
- /package/{esm → dist}/react/provider.js +0 -0
- /package/{esm → dist}/react/require-auth.d.ts +0 -0
- /package/{esm → dist}/react/use-authorization.js +0 -0
- /package/{esm → dist}/server/_clock-skew.d.ts +0 -0
- /package/{esm → dist}/server/client-authentication.js +0 -0
- /package/{esm → dist}/server/grants/client-credentials.js +0 -0
- /package/{esm → dist}/server/grants/password.js +0 -0
- /package/{esm → dist}/server/redirect-uri.d.ts +0 -0
- /package/{esm → dist}/server/services/authorization-code.js +0 -0
- /package/{esm → dist}/server/services/client.js +0 -0
- /package/{esm → dist}/server/services/device-authorization.js +0 -0
- /package/{esm → dist}/server/services/user.js +0 -0
- /package/{esm → dist}/server/utils/hash.d.ts +0 -0
- /package/{esm → dist}/testing/contract/_race.d.ts +0 -0
- /package/{esm → dist}/testing/contract/_race.js +0 -0
- /package/{esm → dist}/testing/contract/tenant.d.ts +0 -0
- /package/{esm → dist}/utils/_body.d.ts +0 -0
- /package/{esm → dist}/utils/_body.js +0 -0
- /package/{esm → dist}/utils/_buffer.d.ts +0 -0
- /package/{esm → dist}/utils/_buffer.js +0 -0
- /package/{esm → dist}/utils/_default-fetch.d.ts +0 -0
- /package/{esm → dist}/utils/_timing-safe.d.ts +0 -0
- /package/{esm → dist}/utils/_timing-safe.js +0 -0
- /package/{esm → dist}/utils/authentication-context.js +0 -0
- /package/{esm → dist}/utils/basic-auth.d.ts +0 -0
- /package/{esm → dist}/utils/basic-auth.js +0 -0
- /package/{esm → dist}/utils/pkce.d.ts +0 -0
- /package/{esm → dist}/utils/text.d.ts +0 -0
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* These are distinct from the Hono BFF SessionStore contract.
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { assert,
|
|
7
|
-
import { afterEach, beforeEach, describe, it } from "../../deps/jsr.io/@std/testing/1.0.20/bdd.js";
|
|
6
|
+
import { afterEach, assert, beforeEach, describe, expect, it } from "vitest";
|
|
8
7
|
function summary(id, lastSeenAt = Date.now() - 30_000) {
|
|
9
8
|
return {
|
|
10
9
|
id,
|
|
@@ -40,42 +39,42 @@ export function runRevocableSessionServiceContractTests(options) {
|
|
|
40
39
|
await fixture.addSession(fixture.otherUserId, summary(fixture.sessionId(4)), "live");
|
|
41
40
|
}
|
|
42
41
|
it("reports zero for users without sessions", async () => {
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
expect(await fixture.service.revokeAllByUser(fixture.userId)).toStrictEqual(0);
|
|
43
|
+
expect(await fixture.service.revokeOthers(fixture.userId, fixture.sessionId(99))).toStrictEqual(0);
|
|
45
44
|
});
|
|
46
45
|
it("revokes all of the selected user's sessions immediately and only once", async () => {
|
|
47
46
|
await seed();
|
|
48
|
-
|
|
47
|
+
expect(await fixture.service.revokeAllByUser(fixture.userId)).toStrictEqual(3);
|
|
49
48
|
for (const id of [
|
|
50
49
|
fixture.sessionId(1),
|
|
51
50
|
fixture.sessionId(2),
|
|
52
51
|
fixture.sessionId(3),
|
|
53
52
|
]) {
|
|
54
|
-
|
|
53
|
+
expect(await fixture.isLive(id)).toBe(false);
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
expect(await fixture.isLive(fixture.sessionId(4))).toBe(true);
|
|
56
|
+
expect(await fixture.service.revokeAllByUser(fixture.userId)).toStrictEqual(0);
|
|
58
57
|
});
|
|
59
58
|
it("keeps only the selected current session and preserves another user's sessions", async () => {
|
|
60
59
|
await seed();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
expect(await fixture.service.revokeOthers(fixture.userId, fixture.sessionId(2))).toStrictEqual(2);
|
|
61
|
+
expect(await fixture.isLive(fixture.sessionId(1))).toBe(false);
|
|
62
|
+
expect(await fixture.isLive(fixture.sessionId(3))).toBe(false);
|
|
63
|
+
expect(await fixture.isLive(fixture.sessionId(2))).toBe(true);
|
|
64
|
+
expect(await fixture.isLive(fixture.sessionId(4))).toBe(true);
|
|
65
|
+
expect(await fixture.service.revokeOthers(fixture.userId, fixture.sessionId(2))).toStrictEqual(0);
|
|
67
66
|
});
|
|
68
67
|
it("a foreign keepSessionId cannot exempt the selected user's sessions", async () => {
|
|
69
68
|
await seed();
|
|
70
|
-
|
|
69
|
+
expect(await fixture.service.revokeOthers(fixture.userId, fixture.sessionId(4))).toStrictEqual(3);
|
|
71
70
|
for (const id of [
|
|
72
71
|
fixture.sessionId(1),
|
|
73
72
|
fixture.sessionId(2),
|
|
74
73
|
fixture.sessionId(3),
|
|
75
74
|
]) {
|
|
76
|
-
|
|
75
|
+
expect(await fixture.isLive(id)).toBe(false);
|
|
77
76
|
}
|
|
78
|
-
|
|
77
|
+
expect(await fixture.isLive(fixture.sessionId(4))).toBe(true);
|
|
79
78
|
});
|
|
80
79
|
it("does not double-count concurrent revocations", async () => {
|
|
81
80
|
await seed();
|
|
@@ -85,15 +84,15 @@ export function runRevocableSessionServiceContractTests(options) {
|
|
|
85
84
|
throw result.reason;
|
|
86
85
|
return result.value;
|
|
87
86
|
});
|
|
88
|
-
|
|
87
|
+
expect(counts.reduce((sum, count) => sum + count, 0), "concurrent revocation counts must not count a live session twice").toStrictEqual(3);
|
|
89
88
|
for (const id of [
|
|
90
89
|
fixture.sessionId(1),
|
|
91
90
|
fixture.sessionId(2),
|
|
92
91
|
fixture.sessionId(3),
|
|
93
92
|
]) {
|
|
94
|
-
|
|
93
|
+
expect(await fixture.isLive(id)).toBe(false);
|
|
95
94
|
}
|
|
96
|
-
|
|
95
|
+
expect(await fixture.isLive(fixture.sessionId(4))).toBe(true);
|
|
97
96
|
});
|
|
98
97
|
});
|
|
99
98
|
}
|
|
@@ -114,7 +113,7 @@ export function runListableSessionServiceContractTests(options) {
|
|
|
114
113
|
await fixture?.dispose?.();
|
|
115
114
|
});
|
|
116
115
|
it("returns an empty list for a user without live sessions", async () => {
|
|
117
|
-
|
|
116
|
+
expect(await fixture.service.listByUser(fixture.userId)).toStrictEqual([]);
|
|
118
117
|
});
|
|
119
118
|
it("lists only the owner's live sessions in most-recently-active order", async () => {
|
|
120
119
|
const oldest = summary(fixture.sessionId(1));
|
|
@@ -132,34 +131,43 @@ export function runListableSessionServiceContractTests(options) {
|
|
|
132
131
|
}
|
|
133
132
|
await fixture.addSession(fixture.otherUserId, summary(fixture.sessionId(4)), "live");
|
|
134
133
|
const rows = await fixture.service.listByUser(fixture.userId);
|
|
135
|
-
|
|
134
|
+
expect(rows.map((row) => row.id), "lists must exclude foreign and ended sessions and sort by last activity").toStrictEqual([
|
|
135
|
+
fixture.sessionId(2),
|
|
136
|
+
fixture.sessionId(3),
|
|
137
|
+
fixture.sessionId(1),
|
|
138
|
+
]);
|
|
136
139
|
const expectedRows = [newest, middle, oldest];
|
|
137
140
|
for (const [index, expected] of expectedRows.entries()) {
|
|
138
141
|
const row = rows[index];
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
assert.exists(row);
|
|
143
|
+
expect(row.createdAt).toStrictEqual(expected.createdAt);
|
|
144
|
+
expect(row.lastSeenAt).toStrictEqual(expected.lastSeenAt);
|
|
145
|
+
expect(Object.keys(row).filter((key) => ![
|
|
146
|
+
"id",
|
|
147
|
+
"createdAt",
|
|
148
|
+
"lastSeenAt",
|
|
149
|
+
"userAgent",
|
|
150
|
+
"location",
|
|
151
|
+
].includes(key)), "session summaries must not expose secret/hash or token material").toStrictEqual([]);
|
|
144
152
|
if (row.userAgent !== undefined) {
|
|
145
|
-
|
|
153
|
+
expect(row.userAgent).toStrictEqual(expected.userAgent);
|
|
146
154
|
}
|
|
147
155
|
if (row.location !== undefined) {
|
|
148
|
-
|
|
156
|
+
expect(row.location).toStrictEqual(expected.location);
|
|
149
157
|
}
|
|
150
158
|
}
|
|
151
|
-
|
|
159
|
+
expect((await fixture.service.listByUser(fixture.otherUserId)).map((row) => row.id)).toStrictEqual([fixture.sessionId(4)]);
|
|
152
160
|
});
|
|
153
161
|
it("removes revoked sessions from the authoritative list immediately", async () => {
|
|
154
162
|
await fixture.addSession(fixture.userId, summary(fixture.sessionId(1)), "live");
|
|
155
163
|
await fixture.addSession(fixture.userId, summary(fixture.sessionId(2)), "live");
|
|
156
164
|
await fixture.addSession(fixture.otherUserId, summary(fixture.sessionId(4)), "live");
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
expect(await fixture.service.revokeOthers(fixture.userId, fixture.sessionId(1))).toStrictEqual(1);
|
|
166
|
+
expect((await fixture.service.listByUser(fixture.userId)).map((row) => row.id)).toStrictEqual([fixture.sessionId(1)]);
|
|
167
|
+
expect(await fixture.isLive(fixture.sessionId(2))).toBe(false);
|
|
168
|
+
expect(await fixture.service.revokeAllByUser(fixture.userId)).toStrictEqual(1);
|
|
169
|
+
expect(await fixture.service.listByUser(fixture.userId)).toStrictEqual([]);
|
|
170
|
+
expect((await fixture.service.listByUser(fixture.otherUserId)).map((row) => row.id)).toStrictEqual([fixture.sessionId(4)]);
|
|
163
171
|
});
|
|
164
172
|
});
|
|
165
173
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Reusable checks for app-owned identity users and password credentials.
|
|
3
|
+
* Optional capabilities are selected explicitly so an omitted implementation
|
|
4
|
+
* cannot silently pass a capability the application intends to provide.
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import type { IdentityUser, IdentityUserStore } from "../../identity/service.ts";
|
|
2
8
|
/** App-specific profile and lookup keys for one fresh test user. */
|
|
3
9
|
export interface IdentityUserContractProfile {
|
|
4
10
|
/** Valid sign-up fields for your application. */
|
|
@@ -41,7 +47,7 @@ export interface IdentityUserStoreContractOptions<User extends IdentityUser> {
|
|
|
41
47
|
legacyCredentials?: boolean;
|
|
42
48
|
}
|
|
43
49
|
/**
|
|
44
|
-
* Register the IdentityUserStore suite in a
|
|
50
|
+
* Register the IdentityUserStore suite in a Vitest test file. Credential fixtures
|
|
45
51
|
* are serialized values, not real passwords: these checks verify persistence,
|
|
46
52
|
* lookup, and conditional writes, rather than password hashing. Concurrent
|
|
47
53
|
* checks can expose read-then-write races but cannot prove every interleaving.
|
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* cannot silently pass a capability the application intends to provide.
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
|
-
import { assert,
|
|
8
|
-
import { afterEach, beforeEach, describe, it } from "../../deps/jsr.io/@std/testing/1.0.20/bdd.js";
|
|
7
|
+
import { afterEach, assert, beforeEach, describe, expect, it } from "vitest";
|
|
9
8
|
const ORIGINAL = {
|
|
10
9
|
hash: "11".repeat(32),
|
|
11
10
|
salt: "22".repeat(16),
|
|
@@ -25,7 +24,7 @@ async function drain(calls) {
|
|
|
25
24
|
});
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
28
|
-
* Register the IdentityUserStore suite in a
|
|
27
|
+
* Register the IdentityUserStore suite in a Vitest test file. Credential fixtures
|
|
29
28
|
* are serialized values, not real passwords: these checks verify persistence,
|
|
30
29
|
* lookup, and conditional writes, rather than password hashing. Concurrent
|
|
31
30
|
* checks can expose read-then-write races but cannot prove every interleaving.
|
|
@@ -48,9 +47,9 @@ export function runIdentityUserStoreContractTests(options) {
|
|
|
48
47
|
return await store.create(fixture.makeProfile(sequence).profile, structuredClone(ORIGINAL));
|
|
49
48
|
}
|
|
50
49
|
it("returns undefined for unknown lookup keys and credentials", async () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
expect(await store.findByIdentifier("missing-identity-contract@example.invalid")).toBe(undefined);
|
|
51
|
+
expect(await store.findByEmail("missing-identity-contract@example.invalid")).toBe(undefined);
|
|
52
|
+
expect(await store.getCredential(fixture.unknownUserId)).toBe(undefined);
|
|
54
53
|
});
|
|
55
54
|
it("creates distinct users and resolves each profile's lookup keys", async () => {
|
|
56
55
|
const first = await create();
|
|
@@ -58,24 +57,27 @@ export function runIdentityUserStoreContractTests(options) {
|
|
|
58
57
|
assert(first.id.length > 0);
|
|
59
58
|
assert(second.id.length > 0);
|
|
60
59
|
assert(first.id !== second.id, "distinct users must have distinct ids");
|
|
61
|
-
for (const [sequence, user] of [
|
|
60
|
+
for (const [sequence, user] of [
|
|
61
|
+
[1, first],
|
|
62
|
+
[2, second],
|
|
63
|
+
]) {
|
|
62
64
|
const profile = fixture.makeProfile(sequence);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
expect((await store.findByIdentifier(profile.identifier))?.id).toStrictEqual(user.id);
|
|
66
|
+
expect((await store.findByEmail(profile.email))?.id).toStrictEqual(user.id);
|
|
67
|
+
expect(await store.getCredential(user.id)).toStrictEqual(ORIGINAL);
|
|
66
68
|
}
|
|
67
69
|
});
|
|
68
70
|
it("replaces every credential field without changing another user", async () => {
|
|
69
71
|
const first = await create();
|
|
70
72
|
const second = await create(2);
|
|
71
73
|
await store.setCredential(first.id, structuredClone(REPLACEMENT));
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
expect(await store.getCredential(first.id)).toStrictEqual(REPLACEMENT);
|
|
75
|
+
expect(await store.getCredential(second.id)).toStrictEqual(ORIGINAL);
|
|
74
76
|
await store.setCredential(first.id, {
|
|
75
77
|
hash: ORIGINAL.hash,
|
|
76
78
|
salt: ORIGINAL.salt,
|
|
77
79
|
});
|
|
78
|
-
|
|
80
|
+
expect(await store.getCredential(first.id)).toStrictEqual({
|
|
79
81
|
hash: ORIGINAL.hash,
|
|
80
82
|
salt: ORIGINAL.salt,
|
|
81
83
|
});
|
|
@@ -83,7 +85,7 @@ export function runIdentityUserStoreContractTests(options) {
|
|
|
83
85
|
if (options.replaceCredential) {
|
|
84
86
|
it("compares credential values, including every parameter and absent params", async () => {
|
|
85
87
|
const user = await create();
|
|
86
|
-
|
|
88
|
+
assert.exists(store.replaceCredential, "replaceCredential capability is required");
|
|
87
89
|
const mismatches = [
|
|
88
90
|
{ ...ORIGINAL, hash: REPLACEMENT.hash },
|
|
89
91
|
{ ...ORIGINAL, salt: REPLACEMENT.salt },
|
|
@@ -95,81 +97,81 @@ export function runIdentityUserStoreContractTests(options) {
|
|
|
95
97
|
{ hash: ORIGINAL.hash, salt: ORIGINAL.salt },
|
|
96
98
|
];
|
|
97
99
|
for (const expected of mismatches) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
expect(await store.replaceCredential(user.id, expected, REPLACEMENT)).toBe(false);
|
|
101
|
+
expect(await store.getCredential(user.id), "a failed compare-and-set must preserve the winner").toStrictEqual(ORIGINAL);
|
|
100
102
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
expect(await store.replaceCredential(user.id, structuredClone(ORIGINAL), REPLACEMENT)).toBe(true);
|
|
104
|
+
expect(await store.getCredential(user.id)).toStrictEqual(REPLACEMENT);
|
|
105
|
+
expect(await store.replaceCredential(user.id, ORIGINAL, ORIGINAL)).toBe(false);
|
|
106
|
+
expect(await store.getCredential(user.id), "read after a lost compare-and-set must see the committed credential").toStrictEqual(REPLACEMENT);
|
|
105
107
|
await store.setCredential(user.id, {
|
|
106
108
|
hash: ORIGINAL.hash,
|
|
107
109
|
salt: ORIGINAL.salt,
|
|
108
110
|
});
|
|
109
|
-
|
|
111
|
+
expect(await store.replaceCredential(user.id, {
|
|
110
112
|
hash: ORIGINAL.hash,
|
|
111
113
|
salt: ORIGINAL.salt,
|
|
112
|
-
}, REPLACEMENT)
|
|
114
|
+
}, REPLACEMENT)).toBe(true);
|
|
113
115
|
});
|
|
114
116
|
it("upgrades an absent credential only once", async () => {
|
|
115
117
|
const user = await create();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
assert.exists(store.replaceCredential);
|
|
119
|
+
assert.exists(fixture.removeCredential, "removeCredential setup helper is required");
|
|
118
120
|
await fixture.removeCredential(user.id);
|
|
119
|
-
|
|
121
|
+
expect(await store.getCredential(user.id)).toBe(undefined);
|
|
120
122
|
const results = await drain(Array.from({ length: 8 }, () => store.replaceCredential(user.id, undefined, structuredClone(REPLACEMENT))));
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
expect(results.filter(Boolean).length, "exactly one concurrent imported upgrade may win").toStrictEqual(1);
|
|
124
|
+
expect(await store.getCredential(user.id)).toStrictEqual(REPLACEMENT);
|
|
123
125
|
});
|
|
124
126
|
it("allows exactly one concurrent replacement of the same credential", async () => {
|
|
125
127
|
const user = await create();
|
|
126
128
|
const other = await create(2);
|
|
127
|
-
|
|
129
|
+
assert.exists(store.replaceCredential);
|
|
128
130
|
const candidates = Array.from({ length: 8 }, (_, index) => ({
|
|
129
131
|
...REPLACEMENT,
|
|
130
132
|
hash: (index + 5).toString(16).padStart(2, "0").repeat(32),
|
|
131
133
|
}));
|
|
132
134
|
const results = await drain(candidates.map((credential) => store.replaceCredential(user.id, structuredClone(ORIGINAL), credential)));
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
expect(results.filter(Boolean).length, "exactly one concurrent replacement may win").toStrictEqual(1);
|
|
136
|
+
expect(await store.getCredential(user.id)).toStrictEqual(candidates[results.indexOf(true)]);
|
|
137
|
+
expect(await store.getCredential(other.id)).toStrictEqual(ORIGINAL);
|
|
136
138
|
});
|
|
137
139
|
}
|
|
138
140
|
if (options.emailVerification) {
|
|
139
141
|
it("verifies only the issued-for email and is idempotent", async () => {
|
|
140
142
|
const user = await create();
|
|
141
143
|
const other = await create(2);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
assert.exists(store.markEmailVerified, "email verification capability is required");
|
|
145
|
+
assert.exists(fixture.setEmail);
|
|
146
|
+
assert.exists(fixture.isEmailVerified);
|
|
145
147
|
const originalEmail = fixture.makeProfile(1).email;
|
|
146
148
|
const changedEmail = "changed-identity-contract@example.invalid";
|
|
147
|
-
|
|
149
|
+
expect(await fixture.isEmailVerified(user.id)).toBe(false);
|
|
148
150
|
await fixture.setEmail(user.id, changedEmail);
|
|
149
151
|
await store.markEmailVerified(user.id, originalEmail);
|
|
150
|
-
|
|
152
|
+
expect(await fixture.isEmailVerified(user.id), "a token for an old email must not verify the new email").toBe(false);
|
|
151
153
|
await store.markEmailVerified(user.id, changedEmail);
|
|
152
154
|
await store.markEmailVerified(user.id, changedEmail);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
+
expect(await fixture.isEmailVerified(user.id)).toBe(true);
|
|
156
|
+
expect(await fixture.isEmailVerified(other.id)).toBe(false);
|
|
155
157
|
});
|
|
156
158
|
}
|
|
157
159
|
if (options.legacyCredentials) {
|
|
158
160
|
it("clears the selected imported credential and is idempotent", async () => {
|
|
159
161
|
const user = await create();
|
|
160
162
|
const other = await create(2);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
assert.exists(store.getLegacyCredential, "legacy credential lookup capability is required");
|
|
164
|
+
assert.exists(store.clearLegacyCredential, "legacy credential clearing capability is required");
|
|
165
|
+
assert.exists(fixture.setLegacyCredential);
|
|
166
|
+
expect(await store.getLegacyCredential(user.id)).toBe(null);
|
|
165
167
|
await fixture.setLegacyCredential(user.id, "imported-hash-one");
|
|
166
168
|
await fixture.setLegacyCredential(other.id, "imported-hash-two");
|
|
167
|
-
|
|
169
|
+
expect(await store.getLegacyCredential(user.id)).toStrictEqual("imported-hash-one");
|
|
168
170
|
await store.clearLegacyCredential(user.id);
|
|
169
171
|
await store.clearLegacyCredential(user.id);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
expect(await store.getLegacyCredential(user.id)).toBe(null);
|
|
173
|
+
expect(await store.getLegacyCredential(other.id)).toStrictEqual("imported-hash-two");
|
|
174
|
+
expect(await store.getCredential(user.id)).toStrictEqual(ORIGINAL);
|
|
173
175
|
});
|
|
174
176
|
}
|
|
175
177
|
});
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* @module
|
|
24
24
|
*/
|
|
25
|
-
import type { LockoutStore } from "../../identity/lockout.
|
|
25
|
+
import type { LockoutStore } from "../../identity/lockout.ts";
|
|
26
26
|
/** Options for {@link runLockoutStoreContractTests}. */
|
|
27
27
|
export interface LockoutStoreContractOptions {
|
|
28
28
|
/**
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* @module
|
|
24
24
|
*/
|
|
25
|
-
import {
|
|
26
|
-
import { beforeEach, describe, it } from "../../deps/jsr.io/@std/testing/1.0.20/bdd.js";
|
|
25
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
27
26
|
const USER = "user-1";
|
|
28
27
|
const OTHER_USER = "user-2";
|
|
29
28
|
/**
|
|
@@ -43,45 +42,45 @@ export function runLockoutStoreContractTests(options) {
|
|
|
43
42
|
});
|
|
44
43
|
describe("get", () => {
|
|
45
44
|
it("returns undefined for an account with no record", async () => {
|
|
46
|
-
|
|
45
|
+
expect(await store.get(USER)).toBe(undefined);
|
|
47
46
|
});
|
|
48
47
|
});
|
|
49
48
|
describe("increment", () => {
|
|
50
49
|
it("records the first failure as one", async () => {
|
|
51
50
|
const result = await store.increment(USER, now);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
expect(result.failures).toStrictEqual(1);
|
|
52
|
+
expect(result.lockedUntil).toBe(undefined);
|
|
53
|
+
expect((await store.get(USER))?.failures).toStrictEqual(1);
|
|
55
54
|
});
|
|
56
55
|
it("adds one failure per call", async () => {
|
|
57
56
|
await store.increment(USER, now);
|
|
58
57
|
await store.increment(USER, now);
|
|
59
|
-
|
|
58
|
+
expect((await store.increment(USER, now)).failures).toStrictEqual(3);
|
|
60
59
|
});
|
|
61
60
|
it("counts each account on its own", async () => {
|
|
62
61
|
await store.increment(USER, now);
|
|
63
62
|
await store.increment(USER, now);
|
|
64
|
-
|
|
63
|
+
expect((await store.increment(OTHER_USER, now)).failures).toStrictEqual(1);
|
|
65
64
|
});
|
|
66
65
|
it("keeps an active lock while counting the failure", async () => {
|
|
67
66
|
const lockedUntil = now + 60_000;
|
|
68
67
|
await store.set(USER, { failures: 10, lockedUntil });
|
|
69
68
|
const result = await store.increment(USER, now);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"the lock the store is holding");
|
|
69
|
+
expect(result.failures).toStrictEqual(11);
|
|
70
|
+
expect(result.lockedUntil, "a failed attempt against a locked account must not extend or drop " +
|
|
71
|
+
"the lock the store is holding").toBe(lockedUntil);
|
|
73
72
|
});
|
|
74
73
|
it("starts a fresh count once the lock has expired", async () => {
|
|
75
74
|
await store.set(USER, { failures: 10, lockedUntil: now - 1 });
|
|
76
75
|
const result = await store.increment(USER, now);
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
expect(result.failures).toStrictEqual(1);
|
|
77
|
+
expect(result.lockedUntil, "an expired lock must be dropped, not carried into the next window").toBe(undefined);
|
|
79
78
|
});
|
|
80
79
|
it("gives every concurrent failure its own count", async () => {
|
|
81
80
|
const results = await Promise.all(Array.from({ length: 5 }, () => store.increment(USER, now)));
|
|
82
|
-
|
|
83
|
-
"sharing a count undershoot the threshold");
|
|
84
|
-
|
|
81
|
+
expect(results.map((result) => result.failures).sort((a, b) => a - b), "the lockout triggers on the returned count — concurrent failures " +
|
|
82
|
+
"sharing a count undershoot the threshold").toStrictEqual([1, 2, 3, 4, 5]);
|
|
83
|
+
expect((await store.get(USER))?.failures).toStrictEqual(5);
|
|
85
84
|
});
|
|
86
85
|
});
|
|
87
86
|
describe("set", () => {
|
|
@@ -89,33 +88,33 @@ export function runLockoutStoreContractTests(options) {
|
|
|
89
88
|
const lockedUntil = now + 60_000;
|
|
90
89
|
await store.set(USER, { failures: 10, lockedUntil });
|
|
91
90
|
const record = await store.get(USER);
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
expect(record?.failures).toStrictEqual(10);
|
|
92
|
+
expect(record?.lockedUntil).toBe(lockedUntil);
|
|
94
93
|
});
|
|
95
94
|
it("replaces a record that was already there", async () => {
|
|
96
95
|
await store.increment(USER, now);
|
|
97
96
|
await store.set(USER, { failures: 4 });
|
|
98
97
|
const record = await store.get(USER);
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
expect(record?.failures).toStrictEqual(4);
|
|
99
|
+
expect(record?.lockedUntil).toBe(undefined);
|
|
101
100
|
});
|
|
102
101
|
});
|
|
103
102
|
describe("clear", () => {
|
|
104
103
|
it("drops the account's failures and lock", async () => {
|
|
105
104
|
await store.set(USER, { failures: 10, lockedUntil: now + 60_000 });
|
|
106
105
|
await store.clear(USER);
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
expect(await store.get(USER)).toBe(undefined);
|
|
107
|
+
expect((await store.increment(USER, now)).failures).toStrictEqual(1);
|
|
109
108
|
});
|
|
110
109
|
it("clears only the account it is given", async () => {
|
|
111
110
|
await store.increment(USER, now);
|
|
112
111
|
await store.increment(OTHER_USER, now);
|
|
113
112
|
await store.clear(USER);
|
|
114
|
-
|
|
113
|
+
expect((await store.get(OTHER_USER))?.failures).toStrictEqual(1);
|
|
115
114
|
});
|
|
116
115
|
it("is a no-op for an account with no record", async () => {
|
|
117
116
|
await store.clear(USER);
|
|
118
|
-
|
|
117
|
+
expect(await store.get(USER)).toBe(undefined);
|
|
119
118
|
});
|
|
120
119
|
});
|
|
121
120
|
});
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*
|
|
24
24
|
* @module
|
|
25
25
|
*/
|
|
26
|
-
import type { MfaStore } from "../../identity/mfa/service.
|
|
26
|
+
import type { MfaStore } from "../../identity/mfa/service.ts";
|
|
27
27
|
/** Options for {@link runMfaStoreContractTests}. */
|
|
28
28
|
export interface MfaStoreContractOptions {
|
|
29
29
|
/**
|