@udibo/oauth2 0.14.0 → 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 +22 -12
- package/{esm → dist}/adapters/hono/bff/bff.js +35 -29
- 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
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
*
|
|
26
26
|
* @module
|
|
27
27
|
*/
|
|
28
|
-
import type { ClientInterface } from "../models/client.
|
|
29
|
-
import type { AuthorizationCode } from "../models/authorization-code.
|
|
30
|
-
import type { RefreshToken, Token } from "../models/token.
|
|
31
|
-
import type { DeviceAuthorization } from "../models/device-authorization.
|
|
32
|
-
import type { AbstractScope, BasicScope } from "../models/scope.
|
|
33
|
-
import type { AuthorizationCodeServiceInterface } from "../server/services/authorization-code.
|
|
34
|
-
import type { ClientServiceInterface } from "../server/services/client.
|
|
35
|
-
import type { DeviceAuthorizationServiceInterface } from "../server/services/device-authorization.
|
|
36
|
-
import type { TokenServiceInterface } from "../server/services/token.
|
|
37
|
-
import type { UserServiceInterface } from "../server/services/user.
|
|
38
|
-
import { type PasswordCredential, type PasswordHasherLike } from "../identity/password.
|
|
28
|
+
import type { ClientInterface } from "../models/client.ts";
|
|
29
|
+
import type { AuthorizationCode } from "../models/authorization-code.ts";
|
|
30
|
+
import type { RefreshToken, Token } from "../models/token.ts";
|
|
31
|
+
import type { DeviceAuthorization } from "../models/device-authorization.ts";
|
|
32
|
+
import type { AbstractScope, BasicScope } from "../models/scope.ts";
|
|
33
|
+
import type { AuthorizationCodeServiceInterface } from "../server/services/authorization-code.ts";
|
|
34
|
+
import type { ClientServiceInterface } from "../server/services/client.ts";
|
|
35
|
+
import type { DeviceAuthorizationServiceInterface } from "../server/services/device-authorization.ts";
|
|
36
|
+
import type { TokenServiceInterface } from "../server/services/token.ts";
|
|
37
|
+
import type { UserServiceInterface } from "../server/services/user.ts";
|
|
38
|
+
import { type PasswordCredential, type PasswordHasherLike } from "../identity/password.ts";
|
|
39
39
|
/** Options for {@link MemoryUserService}. */
|
|
40
40
|
export interface MemoryUserServiceOptions {
|
|
41
41
|
/**
|
|
@@ -42,8 +42,9 @@ export class MemoryUserService {
|
|
|
42
42
|
#passwords;
|
|
43
43
|
/** Applies the password-hasher default; construct with no arguments for it. */
|
|
44
44
|
constructor(options = {}) {
|
|
45
|
-
this.#passwords =
|
|
46
|
-
|
|
45
|
+
this.#passwords =
|
|
46
|
+
options.passwords ??
|
|
47
|
+
new PasswordIdentityService({ iterations: FIXTURE_PBKDF2_ITERATIONS });
|
|
47
48
|
}
|
|
48
49
|
/** Adds a user with the given password. Throws on duplicate id or username. */
|
|
49
50
|
async add(user, password) {
|
|
@@ -73,7 +74,7 @@ export class MemoryUserService {
|
|
|
73
74
|
const credential = this.#credentialsByUserId.get(user.id);
|
|
74
75
|
if (!credential)
|
|
75
76
|
return undefined;
|
|
76
|
-
return await this.#passwords.verify(password, credential)
|
|
77
|
+
return (await this.#passwords.verify(password, credential))
|
|
77
78
|
? user
|
|
78
79
|
: undefined;
|
|
79
80
|
}
|
|
@@ -163,7 +164,7 @@ export class MemoryClientService {
|
|
|
163
164
|
return secret ? undefined : client;
|
|
164
165
|
if (!secret)
|
|
165
166
|
return undefined;
|
|
166
|
-
if (await sha256Hash(secret) !== storedHash)
|
|
167
|
+
if ((await sha256Hash(secret)) !== storedHash)
|
|
167
168
|
return undefined;
|
|
168
169
|
return client;
|
|
169
170
|
}
|
|
@@ -216,8 +217,8 @@ export class MemoryTokenService {
|
|
|
216
217
|
this.#clientService = options.clientService;
|
|
217
218
|
this.#userService = options.userService;
|
|
218
219
|
this.accessTokenLifetime = options.accessTokenLifetime ?? 60 * 60;
|
|
219
|
-
this.refreshTokenLifetime =
|
|
220
|
-
14 * 24 * 60 * 60;
|
|
220
|
+
this.refreshTokenLifetime =
|
|
221
|
+
options.refreshTokenLifetime ?? 14 * 24 * 60 * 60;
|
|
221
222
|
this.refreshTokenMaxLifetime = options.refreshTokenMaxLifetime;
|
|
222
223
|
}
|
|
223
224
|
/**
|
|
@@ -260,8 +261,7 @@ export class MemoryTokenService {
|
|
|
260
261
|
*/
|
|
261
262
|
refreshTokenFamilyExpiresAt(client, _user, familyCreatedAt, _scope) {
|
|
262
263
|
const maxLifetime = client
|
|
263
|
-
.refreshTokenMaxLifetime ??
|
|
264
|
-
this.refreshTokenMaxLifetime;
|
|
264
|
+
.refreshTokenMaxLifetime ?? this.refreshTokenMaxLifetime;
|
|
265
265
|
return Promise.resolve(maxLifetime == null
|
|
266
266
|
? undefined
|
|
267
267
|
: new Date(familyCreatedAt.getTime() + maxLifetime * 1000));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClientInterface } from "../models/client.
|
|
1
|
+
import type { ClientInterface } from "../models/client.ts";
|
|
2
2
|
/** A person in a {@linkcode FakeTenant}. */
|
|
3
3
|
export interface FakeTenantUser {
|
|
4
4
|
/** The subject every token and answer names. */
|
|
@@ -306,18 +306,22 @@ export interface FakeTenant {
|
|
|
306
306
|
*
|
|
307
307
|
* @example
|
|
308
308
|
* ```ts
|
|
309
|
+
* import type { AddressInfo } from "node:net";
|
|
310
|
+
* import { serve } from "@hono/node-server";
|
|
309
311
|
* import { createFakeTenant } from "@udibo/oauth2/testing";
|
|
310
312
|
*
|
|
311
|
-
* const server =
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
313
|
+
* const server = serve({
|
|
314
|
+
* fetch: (request) => tenant.fetch(request),
|
|
315
|
+
* hostname: "127.0.0.1",
|
|
316
|
+
* port: 0,
|
|
317
|
+
* });
|
|
318
|
+
* const { port } = server.address() as AddressInfo;
|
|
315
319
|
* const tenant = await createFakeTenant({
|
|
316
|
-
* issuer: `http://127.0.0.1:${
|
|
320
|
+
* issuer: `http://127.0.0.1:${port}`,
|
|
317
321
|
* });
|
|
318
322
|
* await tenant.addUser({ id: "user-1", username: "ada" });
|
|
319
323
|
* tenant.signInAs("user-1");
|
|
320
|
-
*
|
|
324
|
+
* server.close();
|
|
321
325
|
* ```
|
|
322
326
|
*/
|
|
323
327
|
export declare function createFakeTenant(options: FakeTenantOptions): Promise<FakeTenant>;
|
|
@@ -40,7 +40,7 @@ const OIDC_SCOPES = ["openid", "profile", "email"];
|
|
|
40
40
|
const RESOURCE_GRANTS_READ = "resource_grants.read";
|
|
41
41
|
const RESOURCE_TYPE_MAX_LENGTH = 64;
|
|
42
42
|
const RESOURCE_ID_MAX_LENGTH = 255;
|
|
43
|
-
//
|
|
43
|
+
// oxlint-disable-next-line no-control-regex
|
|
44
44
|
const ASCII_CONTROL_CHARACTERS = /[\x00-\x1f\x7f]/;
|
|
45
45
|
const UNSTATED_ALLOWLIST = "A client_credentials application has to state the scopes its tokens may " +
|
|
46
46
|
"be granted: pass `scopes`, an empty list to deny every scope.";
|
|
@@ -62,11 +62,11 @@ const LOGIN_SESSION_COOKIE = "fake_tenant_session";
|
|
|
62
62
|
const ORGANIZATION_NOT_FOUND = "Organization not found";
|
|
63
63
|
const MEMBER_NOT_FOUND = "organization member not found";
|
|
64
64
|
class ConfidentialClientCredentialsGrant extends ClientCredentialsGrant {
|
|
65
|
-
isConfidential;
|
|
66
65
|
constructor(options, isConfidential) {
|
|
67
66
|
super(options);
|
|
68
67
|
this.isConfidential = isConfidential;
|
|
69
68
|
}
|
|
69
|
+
isConfidential;
|
|
70
70
|
async getAuthenticatedClient(request, body) {
|
|
71
71
|
const client = await super.getAuthenticatedClient(request, body);
|
|
72
72
|
if (!this.isConfidential(client)) {
|
|
@@ -76,29 +76,31 @@ class ConfidentialClientCredentialsGrant extends ClientCredentialsGrant {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
function machineScopeCeiling(client) {
|
|
79
|
-
return client.scopes?.filter((scope) => MACHINE_SCOPES.includes(scope)) ??
|
|
80
|
-
[];
|
|
79
|
+
return client.scopes?.filter((scope) => MACHINE_SCOPES.includes(scope)) ?? [];
|
|
81
80
|
}
|
|
82
81
|
function isBuiltInRole(slug) {
|
|
83
82
|
return BUILT_IN_ROLES.some((role) => role.slug === slug);
|
|
84
83
|
}
|
|
85
84
|
function roleSlugOf(permissions) {
|
|
86
85
|
const slug = permissions
|
|
87
|
-
.map((permission) => permission
|
|
86
|
+
.map((permission) => permission
|
|
87
|
+
.toLowerCase()
|
|
88
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
89
|
+
.replace(/^-+|-+$/g, ""))
|
|
88
90
|
.filter(Boolean)
|
|
89
91
|
.join("-");
|
|
90
92
|
return slug || "grant";
|
|
91
93
|
}
|
|
92
94
|
class TenantTokenService extends MemoryTokenService {
|
|
93
|
-
issuanceOf;
|
|
94
|
-
recordCredential;
|
|
95
|
-
mintJwt;
|
|
96
95
|
constructor(options, issuanceOf, recordCredential, mintJwt) {
|
|
97
96
|
super(options);
|
|
98
97
|
this.issuanceOf = issuanceOf;
|
|
99
98
|
this.recordCredential = recordCredential;
|
|
100
99
|
this.mintJwt = mintJwt;
|
|
101
100
|
}
|
|
101
|
+
issuanceOf;
|
|
102
|
+
recordCredential;
|
|
103
|
+
mintJwt;
|
|
102
104
|
acceptedScope(client, user, scope) {
|
|
103
105
|
if (!scope || user)
|
|
104
106
|
return Promise.resolve(scope);
|
|
@@ -126,7 +128,8 @@ function problem(status, detail, extensions = {}) {
|
|
|
126
128
|
}
|
|
127
129
|
function asPermissions(value) {
|
|
128
130
|
const list = typeof value === "string" ? [value] : value;
|
|
129
|
-
if (!Array.isArray(list) ||
|
|
131
|
+
if (!Array.isArray(list) ||
|
|
132
|
+
list.length === 0 ||
|
|
130
133
|
list.length > MAX_PERMISSIONS ||
|
|
131
134
|
!list.every((entry) => typeof entry === "string" && entry.length > 0)) {
|
|
132
135
|
return undefined;
|
|
@@ -181,7 +184,9 @@ function metadataProblem(value) {
|
|
|
181
184
|
return undefined;
|
|
182
185
|
}
|
|
183
186
|
function nameProblem(value) {
|
|
184
|
-
if (typeof value !== "string" ||
|
|
187
|
+
if (typeof value !== "string" ||
|
|
188
|
+
value.length < 1 ||
|
|
189
|
+
value.length > 100 ||
|
|
185
190
|
CONTROL_CHARACTERS.test(value)) {
|
|
186
191
|
return "name must be 1 to 100 characters with no control characters";
|
|
187
192
|
}
|
|
@@ -272,18 +277,22 @@ function noStore(response) {
|
|
|
272
277
|
*
|
|
273
278
|
* @example
|
|
274
279
|
* ```ts
|
|
280
|
+
* import type { AddressInfo } from "node:net";
|
|
281
|
+
* import { serve } from "@hono/node-server";
|
|
275
282
|
* import { createFakeTenant } from "@udibo/oauth2/testing";
|
|
276
283
|
*
|
|
277
|
-
* const server =
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
284
|
+
* const server = serve({
|
|
285
|
+
* fetch: (request) => tenant.fetch(request),
|
|
286
|
+
* hostname: "127.0.0.1",
|
|
287
|
+
* port: 0,
|
|
288
|
+
* });
|
|
289
|
+
* const { port } = server.address() as AddressInfo;
|
|
281
290
|
* const tenant = await createFakeTenant({
|
|
282
|
-
* issuer: `http://127.0.0.1:${
|
|
291
|
+
* issuer: `http://127.0.0.1:${port}`,
|
|
283
292
|
* });
|
|
284
293
|
* await tenant.addUser({ id: "user-1", username: "ada" });
|
|
285
294
|
* tenant.signInAs("user-1");
|
|
286
|
-
*
|
|
295
|
+
* server.close();
|
|
287
296
|
* ```
|
|
288
297
|
*/
|
|
289
298
|
export async function createFakeTenant(options) {
|
|
@@ -306,9 +315,10 @@ export async function createFakeTenant(options) {
|
|
|
306
315
|
const pendingIssuance = new Map();
|
|
307
316
|
const issuanceQueue = new Map();
|
|
308
317
|
const acceptedRoles = (organizationId, userId) => organizationId
|
|
309
|
-
? organizations
|
|
310
|
-
.
|
|
311
|
-
.
|
|
318
|
+
? (organizations
|
|
319
|
+
.get(organizationId)
|
|
320
|
+
?.grants.filter((grant) => grant.userId === userId && grant.acceptedAt)
|
|
321
|
+
.map((grant) => grant.role) ?? [])
|
|
312
322
|
: [];
|
|
313
323
|
const isMember = (organizationId, userId) => acceptedRoles(organizationId, userId).length > 0;
|
|
314
324
|
const holdsAny = (organizationId, userId, roles) => acceptedRoles(organizationId, userId).some((role) => roles.includes(role));
|
|
@@ -317,14 +327,14 @@ export async function createFakeTenant(options) {
|
|
|
317
327
|
assignment.userId === userId);
|
|
318
328
|
const appRolePermissions = (organizationId, userId) => appRolesHeld(organizationId, userId).flatMap((assignment) => definedRoleById(assignment.roleId)?.permissions ?? []);
|
|
319
329
|
const permissionsIn = (userId, organizationId) => new Set([
|
|
320
|
-
...users.get(userId)?.permissions ?? [],
|
|
321
|
-
...isMember(organizationId, userId)
|
|
330
|
+
...(users.get(userId)?.permissions ?? []),
|
|
331
|
+
...(isMember(organizationId, userId)
|
|
322
332
|
? [
|
|
323
|
-
...organizations.get(organizationId).permissions.get(userId) ??
|
|
324
|
-
[],
|
|
333
|
+
...(organizations.get(organizationId).permissions.get(userId) ??
|
|
334
|
+
[]),
|
|
325
335
|
...appRolePermissions(organizationId, userId),
|
|
326
336
|
]
|
|
327
|
-
: [],
|
|
337
|
+
: []),
|
|
328
338
|
]);
|
|
329
339
|
const organizationClaims = (userId, organizationId) => {
|
|
330
340
|
if (!organizationId || !isMember(organizationId, userId))
|
|
@@ -348,9 +358,7 @@ export async function createFakeTenant(options) {
|
|
|
348
358
|
const clientService = new MemoryClientService(userService);
|
|
349
359
|
const tokenService = new TenantTokenService({ clientService, userService }, (userId) => pendingIssuance.get(userId), (accessToken, issuance, client) => credentials.set(accessToken, {
|
|
350
360
|
...issuance,
|
|
351
|
-
boundSessionId: client.type === "third-party"
|
|
352
|
-
? undefined
|
|
353
|
-
: issuance.sessionId,
|
|
361
|
+
boundSessionId: client.type === "third-party" ? undefined : issuance.sessionId,
|
|
354
362
|
}), (client, user, scope, organizationId) => createJwtAccessTokenGenerator({
|
|
355
363
|
signingKeys,
|
|
356
364
|
issuer,
|
|
@@ -400,7 +408,7 @@ export async function createFakeTenant(options) {
|
|
|
400
408
|
preferred_username: user.username,
|
|
401
409
|
name: user.name,
|
|
402
410
|
email: user.email,
|
|
403
|
-
email_verified: user.email ? user.emailVerified ?? true : undefined,
|
|
411
|
+
email_verified: user.email ? (user.emailVerified ?? true) : undefined,
|
|
404
412
|
...organizationClaims(user.id, signedIn?.userId === user.id ? signedIn.organizationId : undefined),
|
|
405
413
|
}),
|
|
406
414
|
canIntrospectToken: (client, token) => isConfidential(client) && client.id === token.client.id,
|
|
@@ -467,7 +475,7 @@ export async function createFakeTenant(options) {
|
|
|
467
475
|
const caller = await authenticated(request, personOnly);
|
|
468
476
|
if (caller instanceof Response)
|
|
469
477
|
return caller;
|
|
470
|
-
const body = await request.json().catch(() => null);
|
|
478
|
+
const body = (await request.json().catch(() => null));
|
|
471
479
|
const asked = asPermissions(body?.permissions);
|
|
472
480
|
if (!asked)
|
|
473
481
|
return problem(400, "permissions is invalid");
|
|
@@ -507,13 +515,15 @@ export async function createFakeTenant(options) {
|
|
|
507
515
|
const caller = await authenticated(request, personOnly);
|
|
508
516
|
if (caller instanceof Response)
|
|
509
517
|
return caller;
|
|
510
|
-
const body = await request.json().catch(() => null);
|
|
518
|
+
const body = (await request.json().catch(() => null));
|
|
511
519
|
const asked = asPermissions(body?.permissions);
|
|
512
520
|
if (!asked)
|
|
513
521
|
return problem(400, "permissions is invalid");
|
|
514
522
|
const type = body?.resource?.type;
|
|
515
523
|
const ids = body?.resource?.ids;
|
|
516
|
-
if (typeof type !== "string" ||
|
|
524
|
+
if (typeof type !== "string" ||
|
|
525
|
+
!Array.isArray(ids) ||
|
|
526
|
+
ids.length === 0 ||
|
|
517
527
|
ids.length > MAX_BATCH_IDS ||
|
|
518
528
|
!ids.every((id) => typeof id === "string" && id.length > 0)) {
|
|
519
529
|
return problem(400, "resource is invalid");
|
|
@@ -585,7 +595,7 @@ export async function createFakeTenant(options) {
|
|
|
585
595
|
"Path=/",
|
|
586
596
|
"HttpOnly",
|
|
587
597
|
"SameSite=Lax",
|
|
588
|
-
...issuer.startsWith("https:") ? ["Secure"] : [],
|
|
598
|
+
...(issuer.startsWith("https:") ? ["Secure"] : []),
|
|
589
599
|
].join("; ");
|
|
590
600
|
const authorize = async (request) => {
|
|
591
601
|
const chosen = signedIn;
|
|
@@ -598,7 +608,7 @@ export async function createFakeTenant(options) {
|
|
|
598
608
|
return Promise.resolve(null);
|
|
599
609
|
const organizationId = requested
|
|
600
610
|
? requestedOrganization(requested, user.id)
|
|
601
|
-
: chosen.organizationId ?? soleOrganizationOf(user.id);
|
|
611
|
+
: (chosen.organizationId ?? soleOrganizationOf(user.id));
|
|
602
612
|
const continued = continuedSession(request, user.id);
|
|
603
613
|
if (continued)
|
|
604
614
|
continued.lastActiveAt = new Date();
|
|
@@ -625,11 +635,11 @@ export async function createFakeTenant(options) {
|
|
|
625
635
|
headers,
|
|
626
636
|
});
|
|
627
637
|
};
|
|
628
|
-
const isLive = async (accessToken) => await tokenService.getToken(accessToken) !== undefined;
|
|
638
|
+
const isLive = async (accessToken) => (await tokenService.getToken(accessToken)) !== undefined;
|
|
629
639
|
const liveCredentials = async (matches) => {
|
|
630
640
|
const live = [];
|
|
631
641
|
for (const [accessToken, credential] of credentials) {
|
|
632
|
-
if (matches(credential, accessToken) && await isLive(accessToken)) {
|
|
642
|
+
if (matches(credential, accessToken) && (await isLive(accessToken))) {
|
|
633
643
|
live.push([accessToken, credential]);
|
|
634
644
|
}
|
|
635
645
|
}
|
|
@@ -659,9 +669,13 @@ export async function createFakeTenant(options) {
|
|
|
659
669
|
}
|
|
660
670
|
};
|
|
661
671
|
const token = async (request) => {
|
|
662
|
-
const form = await request
|
|
672
|
+
const form = await request
|
|
673
|
+
.clone()
|
|
674
|
+
.formData()
|
|
675
|
+
.catch(() => null);
|
|
663
676
|
const organizationId = form?.get("organization_id");
|
|
664
|
-
if (typeof organizationId === "string" &&
|
|
677
|
+
if (typeof organizationId === "string" &&
|
|
678
|
+
organizationId.length > 0 &&
|
|
665
679
|
form?.get("grant_type") !== "refresh_token") {
|
|
666
680
|
return Response.json({
|
|
667
681
|
error: "invalid_request",
|
|
@@ -693,7 +707,7 @@ export async function createFakeTenant(options) {
|
|
|
693
707
|
: undefined;
|
|
694
708
|
if (session)
|
|
695
709
|
session.lastActiveAt = new Date();
|
|
696
|
-
const issued = await response.clone().json();
|
|
710
|
+
const issued = (await response.clone().json());
|
|
697
711
|
const credential = credentials.get(issued.access_token);
|
|
698
712
|
if (credential) {
|
|
699
713
|
credential.signIn = signIn;
|
|
@@ -710,7 +724,10 @@ export async function createFakeTenant(options) {
|
|
|
710
724
|
});
|
|
711
725
|
};
|
|
712
726
|
const revocation = async (request) => {
|
|
713
|
-
const form = await request
|
|
727
|
+
const form = await request
|
|
728
|
+
.clone()
|
|
729
|
+
.formData()
|
|
730
|
+
.catch(() => null);
|
|
714
731
|
const value = form?.get("token");
|
|
715
732
|
const presented = typeof value === "string"
|
|
716
733
|
? await liveCredentials((credential, accessToken) => accessToken === value || credential.refreshToken === value)
|
|
@@ -851,7 +868,8 @@ export async function createFakeTenant(options) {
|
|
|
851
868
|
const now = new Date();
|
|
852
869
|
for (const invitation of invitations.values()) {
|
|
853
870
|
if (invitation.organizationId === organization.id &&
|
|
854
|
-
invitation.email === email &&
|
|
871
|
+
invitation.email === email &&
|
|
872
|
+
!invitation.acceptedAt &&
|
|
855
873
|
!invitation.deletedAt) {
|
|
856
874
|
invitation.deletedAt = now;
|
|
857
875
|
invitation.updatedAt = now;
|
|
@@ -879,9 +897,7 @@ export async function createFakeTenant(options) {
|
|
|
879
897
|
const slugRefusal = slugProblem(body.slug);
|
|
880
898
|
if (slugRefusal)
|
|
881
899
|
return invalid("slug", slugRefusal);
|
|
882
|
-
const metadataRefusal = body.metadata === undefined
|
|
883
|
-
? undefined
|
|
884
|
-
: metadataProblem(body.metadata);
|
|
900
|
+
const metadataRefusal = body.metadata === undefined ? undefined : metadataProblem(body.metadata);
|
|
885
901
|
if (metadataRefusal)
|
|
886
902
|
return invalid("metadata", metadataRefusal);
|
|
887
903
|
const slug = body.slug;
|
|
@@ -957,7 +973,10 @@ export async function createFakeTenant(options) {
|
|
|
957
973
|
}
|
|
958
974
|
const held = holdsGrantOnResource(organization.id);
|
|
959
975
|
if (held.length > 0) {
|
|
960
|
-
const named = held
|
|
976
|
+
const named = held
|
|
977
|
+
.slice(0, 3)
|
|
978
|
+
.map((grant) => `${grant.resource.type} ${grant.resource.id}`)
|
|
979
|
+
.join(", ");
|
|
961
980
|
return problem(409, `This organization still holds a role on ${named}. Revoke what it holds before deleting it.`);
|
|
962
981
|
}
|
|
963
982
|
organizations.delete(organization.id);
|
|
@@ -994,7 +1013,8 @@ export async function createFakeTenant(options) {
|
|
|
994
1013
|
if (refusal)
|
|
995
1014
|
return refusal;
|
|
996
1015
|
const grant = grantOf(organization, userId, role);
|
|
997
|
-
if (role === OWNER &&
|
|
1016
|
+
if (role === OWNER &&
|
|
1017
|
+
grant?.acceptedAt &&
|
|
998
1018
|
organization.grants.filter((g) => g.role === OWNER && g.acceptedAt)
|
|
999
1019
|
.length <= 1) {
|
|
1000
1020
|
return problem(409, "This is the organization's last owner — make someone else an owner before revoking this membership.");
|
|
@@ -1034,7 +1054,8 @@ export async function createFakeTenant(options) {
|
|
|
1034
1054
|
if (organization instanceof Response)
|
|
1035
1055
|
return organization;
|
|
1036
1056
|
const body = await jsonBody(request);
|
|
1037
|
-
if (!isRecord(body) ||
|
|
1057
|
+
if (!isRecord(body) ||
|
|
1058
|
+
typeof body.roleId !== "string" ||
|
|
1038
1059
|
body.roleId.length === 0) {
|
|
1039
1060
|
return invalid("roleId", "A role id is required");
|
|
1040
1061
|
}
|
|
@@ -1069,9 +1090,7 @@ export async function createFakeTenant(options) {
|
|
|
1069
1090
|
if (organization instanceof Response)
|
|
1070
1091
|
return organization;
|
|
1071
1092
|
if (BUILT_IN_APP_ROLES.includes(roleId)) {
|
|
1072
|
-
const who = roleId === OWNER
|
|
1073
|
-
? "Only an owner"
|
|
1074
|
-
: "Only an admin or owner";
|
|
1093
|
+
const who = roleId === OWNER ? "Only an owner" : "Only an admin or owner";
|
|
1075
1094
|
return problem(403, `${who} can grant or revoke ${roleId} access.`);
|
|
1076
1095
|
}
|
|
1077
1096
|
const held = appRolesHeld(organization.id, userId).find((assignment) => assignment.roleId === roleId);
|
|
@@ -1109,8 +1128,10 @@ export async function createFakeTenant(options) {
|
|
|
1109
1128
|
return invalid("return_to", "return_to must be a path on this tenant's host or an absolute URL on the origin of a redirect URI registered on one of its live applications");
|
|
1110
1129
|
}
|
|
1111
1130
|
const offered = [...invitations.values()].find((invitation) => invitation.organizationId === organization.id &&
|
|
1112
|
-
invitation.email === email &&
|
|
1113
|
-
|
|
1131
|
+
invitation.email === email &&
|
|
1132
|
+
invitation.role === role &&
|
|
1133
|
+
!invitation.acceptedAt &&
|
|
1134
|
+
!invitation.deletedAt);
|
|
1114
1135
|
if (offered) {
|
|
1115
1136
|
return problem(409, `${email} was already invited to the ${role} role. Withdraw that invitation before sending another.`);
|
|
1116
1137
|
}
|
|
@@ -1166,7 +1187,8 @@ export async function createFakeTenant(options) {
|
|
|
1166
1187
|
.filter((invitation) => invitation.organizationId === organization.id &&
|
|
1167
1188
|
!invitation.deletedAt &&
|
|
1168
1189
|
(status === "" || !invitation.acceptedAt) &&
|
|
1169
|
-
(status !== "outstanding" ||
|
|
1190
|
+
(status !== "outstanding" ||
|
|
1191
|
+
invitation.expiresAt.getTime() > now))
|
|
1170
1192
|
.map(invitationJson), request));
|
|
1171
1193
|
});
|
|
1172
1194
|
const withdrawInvitation = organizationApi((caller, _request, [id, invitationId]) => {
|
|
@@ -1174,8 +1196,10 @@ export async function createFakeTenant(options) {
|
|
|
1174
1196
|
if (organization instanceof Response)
|
|
1175
1197
|
return organization;
|
|
1176
1198
|
const invitation = invitations.get(invitationId);
|
|
1177
|
-
if (!invitation ||
|
|
1178
|
-
invitation.
|
|
1199
|
+
if (!invitation ||
|
|
1200
|
+
invitation.organizationId !== organization.id ||
|
|
1201
|
+
invitation.acceptedAt ||
|
|
1202
|
+
invitation.deletedAt) {
|
|
1179
1203
|
return problem(404, "Organization invitation not found");
|
|
1180
1204
|
}
|
|
1181
1205
|
invitation.deletedAt = new Date();
|
|
@@ -1189,18 +1213,22 @@ export async function createFakeTenant(options) {
|
|
|
1189
1213
|
const address = email?.toLowerCase();
|
|
1190
1214
|
const now = Date.now();
|
|
1191
1215
|
const invited = address && !unverifiedEmail
|
|
1192
|
-
? [...invitations.values()].filter((invitation) => invitation.email === address &&
|
|
1193
|
-
!invitation.
|
|
1216
|
+
? [...invitations.values()].filter((invitation) => invitation.email === address &&
|
|
1217
|
+
!invitation.acceptedAt &&
|
|
1218
|
+
!invitation.deletedAt &&
|
|
1219
|
+
invitation.expiresAt.getTime() > now)
|
|
1194
1220
|
: [];
|
|
1195
1221
|
return Response.json({
|
|
1196
1222
|
offers: [...pendingGrantsOf(caller.user.id), ...invited].flatMap((offer) => {
|
|
1197
1223
|
const organization = organizations.get(offer.organizationId);
|
|
1198
1224
|
return organization
|
|
1199
|
-
? [
|
|
1225
|
+
? [
|
|
1226
|
+
{
|
|
1200
1227
|
id: offer.id,
|
|
1201
1228
|
organizationName: organization.name,
|
|
1202
1229
|
roleName: roleName(offer.role),
|
|
1203
|
-
}
|
|
1230
|
+
},
|
|
1231
|
+
]
|
|
1204
1232
|
: [];
|
|
1205
1233
|
}),
|
|
1206
1234
|
unverifiedEmail,
|
|
@@ -1210,7 +1238,8 @@ export async function createFakeTenant(options) {
|
|
|
1210
1238
|
const invitation = invitations.get(offerId);
|
|
1211
1239
|
if (invitation && !invitation.acceptedAt && !invitation.deletedAt) {
|
|
1212
1240
|
const email = caller.user.email?.toLowerCase();
|
|
1213
|
-
if (!email ||
|
|
1241
|
+
if (!email ||
|
|
1242
|
+
caller.user.emailVerified === false ||
|
|
1214
1243
|
email !== invitation.email) {
|
|
1215
1244
|
return Response.json({ status: "wrong-account" });
|
|
1216
1245
|
}
|
|
@@ -1260,14 +1289,15 @@ export async function createFakeTenant(options) {
|
|
|
1260
1289
|
}));
|
|
1261
1290
|
const patchAccount = accountApi(async (caller, request) => {
|
|
1262
1291
|
const body = await jsonBody(request);
|
|
1263
|
-
if (!isRecord(body) ||
|
|
1292
|
+
if (!isRecord(body) ||
|
|
1293
|
+
Object.keys(body).length !== 1 ||
|
|
1264
1294
|
!("userMetadata" in body)) {
|
|
1265
1295
|
return problem(400, "Only userMetadata may be written here");
|
|
1266
1296
|
}
|
|
1267
1297
|
const incoming = metadataProblem(body.userMetadata);
|
|
1268
1298
|
if (incoming)
|
|
1269
1299
|
return invalid("userMetadata", incoming);
|
|
1270
|
-
const merged = { ...userMetadata.get(caller.user.id) ?? {} };
|
|
1300
|
+
const merged = { ...(userMetadata.get(caller.user.id) ?? {}) };
|
|
1271
1301
|
for (const [key, value] of Object.entries(body.userMetadata)) {
|
|
1272
1302
|
if (value === null)
|
|
1273
1303
|
delete merged[key];
|
|
@@ -1302,7 +1332,7 @@ export async function createFakeTenant(options) {
|
|
|
1302
1332
|
});
|
|
1303
1333
|
});
|
|
1304
1334
|
const revokeSession = accountApi(async (caller, request, [sessionId]) => {
|
|
1305
|
-
if (!await isEmptyBody(request)) {
|
|
1335
|
+
if (!(await isEmptyBody(request))) {
|
|
1306
1336
|
return problem(400, "The request body must be empty");
|
|
1307
1337
|
}
|
|
1308
1338
|
if (sessionId === currentSessionOf(caller)) {
|
|
@@ -1317,7 +1347,7 @@ export async function createFakeTenant(options) {
|
|
|
1317
1347
|
return new Response(null, { status: 204 });
|
|
1318
1348
|
});
|
|
1319
1349
|
const revokeOtherSessions = accountApi(async (caller, request) => {
|
|
1320
|
-
if (!await isEmptyBody(request)) {
|
|
1350
|
+
if (!(await isEmptyBody(request))) {
|
|
1321
1351
|
return problem(400, "The request body must be empty");
|
|
1322
1352
|
}
|
|
1323
1353
|
const current = currentSessionOf(caller);
|
|
@@ -1344,7 +1374,7 @@ export async function createFakeTenant(options) {
|
|
|
1344
1374
|
connectable: [],
|
|
1345
1375
|
}));
|
|
1346
1376
|
const unlinkAccount = accountApi(async (caller, request, [identityId]) => {
|
|
1347
|
-
if (!await isEmptyBody(request)) {
|
|
1377
|
+
if (!(await isEmptyBody(request))) {
|
|
1348
1378
|
return problem(400, "Only an empty request body is accepted");
|
|
1349
1379
|
}
|
|
1350
1380
|
const account = linkedAccounts.get(identityId);
|
|
@@ -1375,9 +1405,7 @@ export async function createFakeTenant(options) {
|
|
|
1375
1405
|
};
|
|
1376
1406
|
};
|
|
1377
1407
|
const resourceGrantJson = (grant) => {
|
|
1378
|
-
const holder = grant.subject.type === "user"
|
|
1379
|
-
? users.get(grant.subject.id)
|
|
1380
|
-
: undefined;
|
|
1408
|
+
const holder = grant.subject.type === "user" ? users.get(grant.subject.id) : undefined;
|
|
1381
1409
|
const organization = grant.subject.type === "organization"
|
|
1382
1410
|
? organizations.get(grant.subject.id)
|
|
1383
1411
|
: undefined;
|
|
@@ -1387,8 +1415,8 @@ export async function createFakeTenant(options) {
|
|
|
1387
1415
|
subjectId: grant.subject.id,
|
|
1388
1416
|
subjectRole: null,
|
|
1389
1417
|
subjectName: holder
|
|
1390
|
-
? holder.name ?? holder.username
|
|
1391
|
-
: organization?.name ?? null,
|
|
1418
|
+
? (holder.name ?? holder.username)
|
|
1419
|
+
: (organization?.name ?? null),
|
|
1392
1420
|
subjectActive: holder !== undefined || organization !== undefined,
|
|
1393
1421
|
roleId: grant.roleId,
|
|
1394
1422
|
builtInRole: isBuiltInRole(grant.role) ? grant.role : null,
|
|
@@ -1407,8 +1435,11 @@ export async function createFakeTenant(options) {
|
|
|
1407
1435
|
const params = new URL(request.url).searchParams;
|
|
1408
1436
|
const type = params.get("type");
|
|
1409
1437
|
const id = params.get("id");
|
|
1410
|
-
if (!type ||
|
|
1411
|
-
|
|
1438
|
+
if (!type ||
|
|
1439
|
+
type.length > RESOURCE_TYPE_MAX_LENGTH ||
|
|
1440
|
+
!id ||
|
|
1441
|
+
id.length > RESOURCE_ID_MAX_LENGTH ||
|
|
1442
|
+
ASCII_CONTROL_CHARACTERS.test(id)) {
|
|
1412
1443
|
return noStore(invalid("resource", "Pass the resource as `type` and `id` query parameters."));
|
|
1413
1444
|
}
|
|
1414
1445
|
if (!resourceTypes.has(type)) {
|
|
@@ -1454,11 +1485,7 @@ export async function createFakeTenant(options) {
|
|
|
1454
1485
|
new RegExp(`^/api/organizations/${segment}$`),
|
|
1455
1486
|
deleteOrganization,
|
|
1456
1487
|
],
|
|
1457
|
-
[
|
|
1458
|
-
"GET",
|
|
1459
|
-
new RegExp(`^/api/organizations/${segment}/members$`),
|
|
1460
|
-
listMembers,
|
|
1461
|
-
],
|
|
1488
|
+
["GET", new RegExp(`^/api/organizations/${segment}/members$`), listMembers],
|
|
1462
1489
|
[
|
|
1463
1490
|
"GET",
|
|
1464
1491
|
new RegExp(`^/api/organizations/${segment}/member-roles$`),
|
|
@@ -1484,11 +1511,7 @@ export async function createFakeTenant(options) {
|
|
|
1484
1511
|
new RegExp(`^/api/organizations/${segment}/members/${segment}/${segment}$`),
|
|
1485
1512
|
revokeMembership,
|
|
1486
1513
|
],
|
|
1487
|
-
[
|
|
1488
|
-
"POST",
|
|
1489
|
-
new RegExp(`^/api/organizations/${segment}/invitations$`),
|
|
1490
|
-
invite,
|
|
1491
|
-
],
|
|
1514
|
+
["POST", new RegExp(`^/api/organizations/${segment}/invitations$`), invite],
|
|
1492
1515
|
[
|
|
1493
1516
|
"GET",
|
|
1494
1517
|
new RegExp(`^/api/organizations/${segment}/invitations$`),
|
|
@@ -1502,16 +1525,8 @@ export async function createFakeTenant(options) {
|
|
|
1502
1525
|
["GET", /^\/api\/account$/, readAccount],
|
|
1503
1526
|
["PATCH", /^\/api\/account$/, patchAccount],
|
|
1504
1527
|
["GET", /^\/api\/account\/sessions$/, listSessions],
|
|
1505
|
-
[
|
|
1506
|
-
|
|
1507
|
-
/^\/api\/account\/sessions\/revoke-others$/,
|
|
1508
|
-
revokeOtherSessions,
|
|
1509
|
-
],
|
|
1510
|
-
[
|
|
1511
|
-
"DELETE",
|
|
1512
|
-
new RegExp(`^/api/account/sessions/${segment}$`),
|
|
1513
|
-
revokeSession,
|
|
1514
|
-
],
|
|
1528
|
+
["POST", /^\/api\/account\/sessions\/revoke-others$/, revokeOtherSessions],
|
|
1529
|
+
["DELETE", new RegExp(`^/api/account/sessions/${segment}$`), revokeSession],
|
|
1515
1530
|
["GET", /^\/api\/account\/linked-accounts$/, listLinkedAccounts],
|
|
1516
1531
|
[
|
|
1517
1532
|
"DELETE",
|
|
@@ -1570,7 +1585,7 @@ export async function createFakeTenant(options) {
|
|
|
1570
1585
|
addUser: async (user) => {
|
|
1571
1586
|
await userService.add(user, crypto.randomUUID());
|
|
1572
1587
|
users.set(user.id, user);
|
|
1573
|
-
userMetadata.set(user.id, { ...user.userMetadata ?? {} });
|
|
1588
|
+
userMetadata.set(user.id, { ...(user.userMetadata ?? {}) });
|
|
1574
1589
|
},
|
|
1575
1590
|
addOrganization: (organization) => {
|
|
1576
1591
|
if (organizations.has(organization.id)) {
|
|
@@ -1629,7 +1644,7 @@ export async function createFakeTenant(options) {
|
|
|
1629
1644
|
id: roleId,
|
|
1630
1645
|
slug,
|
|
1631
1646
|
name: name ?? slug,
|
|
1632
|
-
permissions: [...permissions ?? []],
|
|
1647
|
+
permissions: [...(permissions ?? [])],
|
|
1633
1648
|
});
|
|
1634
1649
|
return roleId;
|
|
1635
1650
|
},
|
|
@@ -1640,7 +1655,7 @@ export async function createFakeTenant(options) {
|
|
|
1640
1655
|
const role = grant.role ?? roleSlugOf(grant.permissions);
|
|
1641
1656
|
let roleId = isBuiltInRole(role)
|
|
1642
1657
|
? null
|
|
1643
|
-
: definedRoles.get(role)?.id ?? resourceRoleIds.get(role);
|
|
1658
|
+
: (definedRoles.get(role)?.id ?? resourceRoleIds.get(role));
|
|
1644
1659
|
if (roleId === undefined) {
|
|
1645
1660
|
roleId = crypto.randomUUID();
|
|
1646
1661
|
resourceRoleIds.set(role, roleId);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal constant-time byte comparison for modules that also run in
|
|
3
|
+
* browsers, where `node:crypto` is unavailable.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Compares two byte arrays without an early exit on the first differing byte.
|
|
9
|
+
* Returns `false` immediately when the lengths differ, so the length is not
|
|
10
|
+
* secret.
|
|
11
|
+
*/
|
|
12
|
+
export declare function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
|