@udibo/oauth2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +174 -0
- package/SECURITY.md +77 -0
- package/esm/_dnt.polyfills.d.ts +17 -0
- package/esm/_dnt.polyfills.js +15 -0
- package/esm/adapters/hono/_common.d.ts +66 -0
- package/esm/adapters/hono/_common.js +108 -0
- package/esm/adapters/hono/authorization-server.d.ts +211 -0
- package/esm/adapters/hono/authorization-server.js +214 -0
- package/esm/adapters/hono/bff/auth-request-store.d.ts +164 -0
- package/esm/adapters/hono/bff/auth-request-store.js +154 -0
- package/esm/adapters/hono/bff/bff.d.ts +852 -0
- package/esm/adapters/hono/bff/bff.js +1194 -0
- package/esm/adapters/hono/bff/cookie-prefix.d.ts +72 -0
- package/esm/adapters/hono/bff/cookie-prefix.js +85 -0
- package/esm/adapters/hono/bff/mod.d.ts +19 -0
- package/esm/adapters/hono/bff/mod.js +19 -0
- package/esm/adapters/hono/bff/proxy.d.ts +108 -0
- package/esm/adapters/hono/bff/proxy.js +314 -0
- package/esm/adapters/hono/bff/session-store.d.ts +250 -0
- package/esm/adapters/hono/bff/session-store.js +216 -0
- package/esm/adapters/hono/bff/testing.d.ts +165 -0
- package/esm/adapters/hono/bff/testing.js +254 -0
- package/esm/adapters/hono/identity.d.ts +117 -0
- package/esm/adapters/hono/identity.js +215 -0
- package/esm/adapters/hono/log.d.ts +49 -0
- package/esm/adapters/hono/log.js +66 -0
- package/esm/adapters/hono/resource-server.d.ts +101 -0
- package/esm/adapters/hono/resource-server.js +108 -0
- package/esm/client/_http.d.ts +91 -0
- package/esm/client/_http.js +303 -0
- package/esm/client/base.d.ts +184 -0
- package/esm/client/base.js +93 -0
- package/esm/client/bff-client.d.ts +235 -0
- package/esm/client/bff-client.js +280 -0
- package/esm/client/browser-storage.d.ts +152 -0
- package/esm/client/browser-storage.js +246 -0
- package/esm/client/check.d.ts +114 -0
- package/esm/client/check.js +97 -0
- package/esm/client/direct-client.d.ts +707 -0
- package/esm/client/direct-client.js +1237 -0
- package/esm/client/discovery-cache.d.ts +138 -0
- package/esm/client/discovery-cache.js +124 -0
- package/esm/client/events.d.ts +54 -0
- package/esm/client/events.js +37 -0
- package/esm/client/mod.d.ts +35 -0
- package/esm/client/mod.js +33 -0
- package/esm/client/storage.d.ts +127 -0
- package/esm/client/storage.js +81 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.d.ts +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.js +47 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.d.ts +28 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.js +65 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assert.d.ts +16 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assert.js +23 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.d.ts +25 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.js +30 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equal.d.ts +24 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equal.js +193 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equals.d.ts +36 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/equals.js +61 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/exists.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/exists.js +27 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/fail.d.ts +14 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/fail.js +20 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/false.d.ts +18 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/false.js +23 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.d.ts +22 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.js +52 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.d.ts +24 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.js +53 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less.d.ts +18 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less.js +28 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/match.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/match.js +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/mod.d.ts +43 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/mod.js +45 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.d.ts +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.js +39 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.d.ts +19 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.js +29 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.js +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.d.ts +22 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.js +32 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.d.ts +28 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.js +177 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.d.ts +41 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.js +53 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.d.ts +23 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.js +58 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.d.ts +17 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.js +26 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/throws.d.ts +44 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/throws.js +44 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.d.ts +14 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.js +20 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.d.ts +14 -0
- package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.js +20 -0
- package/esm/deps/jsr.io/@std/async/1.5.0/delay.d.ts +44 -0
- package/esm/deps/jsr.io/@std/async/1.5.0/delay.js +84 -0
- package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.d.ts +39 -0
- package/esm/deps/jsr.io/@std/crypto/1.1.0/timing_safe_equal.js +61 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.d.ts +22 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common16.js +51 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.d.ts +34 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common32.js +192 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.d.ts +34 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common64.js +113 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.d.ts +3 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_common_detach.js +13 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.d.ts +8 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_types.js +2 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.d.ts +1 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/_validate_binary_like.js +26 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.d.ts +60 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/ascii85.js +152 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base32.js +87 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base58.js +131 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64.js +82 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.d.ts +39 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/base64url.js +72 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.d.ts +38 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/hex.js +87 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.d.ts +97 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/mod.js +99 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.d.ts +119 -0
- package/esm/deps/jsr.io/@std/encoding/1.0.11/varint.js +209 -0
- package/esm/deps/jsr.io/@std/http/1.1.3/status.d.ts +343 -0
- package/esm/deps/jsr.io/@std/http/1.1.3/status.js +343 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.d.ts +159 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/assertion_state.js +223 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.d.ts +79 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/build_message.js +116 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff.d.ts +139 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff.js +277 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.d.ts +98 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/diff_str.js +181 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/format.d.ts +29 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/format.js +68 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/styles.d.ts +158 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/styles.js +206 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/types.d.ts +24 -0
- package/esm/deps/jsr.io/@std/internal/1.0.14/types.js +3 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.d.ts +91 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/_test_suite.js +347 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.d.ts +382 -0
- package/esm/deps/jsr.io/@std/testing/1.0.20/bdd.js +1110 -0
- package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.d.ts +363 -0
- package/esm/deps/jsr.io/@udibo/http-error/0.11.1/mod.js +616 -0
- package/esm/errors.d.ts +203 -0
- package/esm/errors.js +287 -0
- package/esm/identity/captcha.d.ts +147 -0
- package/esm/identity/captcha.js +64 -0
- package/esm/identity/delivery.d.ts +96 -0
- package/esm/identity/delivery.js +41 -0
- package/esm/identity/errors.d.ts +39 -0
- package/esm/identity/errors.js +46 -0
- package/esm/identity/events.d.ts +242 -0
- package/esm/identity/events.js +39 -0
- package/esm/identity/external/_shared.d.ts +86 -0
- package/esm/identity/external/_shared.js +107 -0
- package/esm/identity/external/_token-exchange.d.ts +61 -0
- package/esm/identity/external/_token-exchange.js +179 -0
- package/esm/identity/external/apple-client-secret.d.ts +82 -0
- package/esm/identity/external/apple-client-secret.js +133 -0
- package/esm/identity/external/apple.d.ts +64 -0
- package/esm/identity/external/apple.js +248 -0
- package/esm/identity/external/discord.d.ts +38 -0
- package/esm/identity/external/discord.js +62 -0
- package/esm/identity/external/errors.d.ts +48 -0
- package/esm/identity/external/errors.js +36 -0
- package/esm/identity/external/flow.d.ts +107 -0
- package/esm/identity/external/flow.js +158 -0
- package/esm/identity/external/github.d.ts +42 -0
- package/esm/identity/external/github.js +164 -0
- package/esm/identity/external/google.d.ts +52 -0
- package/esm/identity/external/google.js +38 -0
- package/esm/identity/external/mod.d.ts +63 -0
- package/esm/identity/external/mod.js +61 -0
- package/esm/identity/external/oauth2.d.ts +110 -0
- package/esm/identity/external/oauth2.js +158 -0
- package/esm/identity/external/oidc.d.ts +81 -0
- package/esm/identity/external/oidc.js +250 -0
- package/esm/identity/external/provider.d.ts +132 -0
- package/esm/identity/external/provider.js +12 -0
- package/esm/identity/hibp.d.ts +67 -0
- package/esm/identity/hibp.js +87 -0
- package/esm/identity/identifier.d.ts +44 -0
- package/esm/identity/identifier.js +47 -0
- package/esm/identity/lockout.d.ts +155 -0
- package/esm/identity/lockout.js +117 -0
- package/esm/identity/mfa/mod.d.ts +42 -0
- package/esm/identity/mfa/mod.js +42 -0
- package/esm/identity/mfa/recovery-codes.d.ts +96 -0
- package/esm/identity/mfa/recovery-codes.js +80 -0
- package/esm/identity/mfa/service.d.ts +338 -0
- package/esm/identity/mfa/service.js +351 -0
- package/esm/identity/mfa/totp.d.ts +186 -0
- package/esm/identity/mfa/totp.js +194 -0
- package/esm/identity/migration.d.ts +155 -0
- package/esm/identity/migration.js +243 -0
- package/esm/identity/mod.d.ts +65 -0
- package/esm/identity/mod.js +64 -0
- package/esm/identity/otp.d.ts +255 -0
- package/esm/identity/otp.js +245 -0
- package/esm/identity/password-policy.d.ts +45 -0
- package/esm/identity/password-policy.js +60 -0
- package/esm/identity/password.d.ts +171 -0
- package/esm/identity/password.js +160 -0
- package/esm/identity/rate-limit.d.ts +192 -0
- package/esm/identity/rate-limit.js +168 -0
- package/esm/identity/service.d.ts +579 -0
- package/esm/identity/service.js +835 -0
- package/esm/identity/session.d.ts +122 -0
- package/esm/identity/session.js +48 -0
- package/esm/identity/token-flow.d.ts +214 -0
- package/esm/identity/token-flow.js +173 -0
- package/esm/models/authentication.d.ts +24 -0
- package/esm/models/authentication.js +1 -0
- package/esm/models/authorization-code.d.ts +63 -0
- package/esm/models/authorization-code.js +1 -0
- package/esm/models/authorization.d.ts +165 -0
- package/esm/models/authorization.js +160 -0
- package/esm/models/client.d.ts +78 -0
- package/esm/models/client.js +1 -0
- package/esm/models/device-authorization.d.ts +30 -0
- package/esm/models/device-authorization.js +1 -0
- package/esm/models/responses.d.ts +93 -0
- package/esm/models/responses.js +1 -0
- package/esm/models/scope.d.ts +81 -0
- package/esm/models/scope.js +125 -0
- package/esm/models/token.d.ts +135 -0
- package/esm/models/token.js +1 -0
- package/esm/package.json +3 -0
- package/esm/react/callback.d.ts +52 -0
- package/esm/react/callback.js +71 -0
- package/esm/react/components/class-names.d.ts +91 -0
- package/esm/react/components/class-names.js +118 -0
- package/esm/react/components/internal.d.ts +62 -0
- package/esm/react/components/internal.js +102 -0
- package/esm/react/components/mfa-challenge-form.d.ts +66 -0
- package/esm/react/components/mfa-challenge-form.js +46 -0
- package/esm/react/components/mfa-enrollment-form.d.ts +83 -0
- package/esm/react/components/mfa-enrollment-form.js +41 -0
- package/esm/react/components/mod.d.ts +103 -0
- package/esm/react/components/mod.js +93 -0
- package/esm/react/components/request-password-reset-form.d.ts +61 -0
- package/esm/react/components/request-password-reset-form.js +41 -0
- package/esm/react/components/reset-password-form.d.ts +66 -0
- package/esm/react/components/reset-password-form.js +56 -0
- package/esm/react/components/sign-in-form.d.ts +74 -0
- package/esm/react/components/sign-in-form.js +43 -0
- package/esm/react/components/sign-up-form.d.ts +85 -0
- package/esm/react/components/sign-up-form.js +57 -0
- package/esm/react/components/types.d.ts +208 -0
- package/esm/react/components/types.js +14 -0
- package/esm/react/components/use-auth-form.d.ts +118 -0
- package/esm/react/components/use-auth-form.js +172 -0
- package/esm/react/components/user-menu.d.ts +79 -0
- package/esm/react/components/user-menu.js +73 -0
- package/esm/react/context.d.ts +51 -0
- package/esm/react/context.js +16 -0
- package/esm/react/mod.d.ts +26 -0
- package/esm/react/mod.js +26 -0
- package/esm/react/provider.d.ts +40 -0
- package/esm/react/provider.js +189 -0
- package/esm/react/require-auth.d.ts +31 -0
- package/esm/react/require-auth.js +37 -0
- package/esm/react/testing.d.ts +322 -0
- package/esm/react/testing.js +461 -0
- package/esm/react/use-authorization.d.ts +31 -0
- package/esm/react/use-authorization.js +36 -0
- package/esm/react/use-oauth2.d.ts +112 -0
- package/esm/react/use-oauth2.js +127 -0
- package/esm/server/authorization-server.d.ts +736 -0
- package/esm/server/authorization-server.js +1304 -0
- package/esm/server/client-authentication.d.ts +28 -0
- package/esm/server/client-authentication.js +51 -0
- package/esm/server/grants/authorization-code.d.ts +179 -0
- package/esm/server/grants/authorization-code.js +232 -0
- package/esm/server/grants/client-credentials.d.ts +52 -0
- package/esm/server/grants/client-credentials.js +53 -0
- package/esm/server/grants/device-authorization.d.ts +103 -0
- package/esm/server/grants/device-authorization.js +156 -0
- package/esm/server/grants/grant.d.ts +165 -0
- package/esm/server/grants/grant.js +173 -0
- package/esm/server/grants/password.d.ts +80 -0
- package/esm/server/grants/password.js +92 -0
- package/esm/server/grants/refresh-token.d.ts +80 -0
- package/esm/server/grants/refresh-token.js +136 -0
- package/esm/server/introspection-token-reader.d.ts +153 -0
- package/esm/server/introspection-token-reader.js +164 -0
- package/esm/server/jwks-token-reader.d.ts +263 -0
- package/esm/server/jwks-token-reader.js +508 -0
- package/esm/server/mod.d.ts +23 -0
- package/esm/server/mod.js +15 -0
- package/esm/server/public-suffix/mod.d.ts +62 -0
- package/esm/server/public-suffix/mod.js +103 -0
- package/esm/server/public-suffix/rules.d.ts +23 -0
- package/esm/server/public-suffix/rules.js +23 -0
- package/esm/server/redirect-uri.d.ts +187 -0
- package/esm/server/redirect-uri.js +331 -0
- package/esm/server/resource-server.d.ts +286 -0
- package/esm/server/resource-server.js +365 -0
- package/esm/server/services/authorization-code.d.ts +84 -0
- package/esm/server/services/authorization-code.js +35 -0
- package/esm/server/services/client.d.ts +35 -0
- package/esm/server/services/client.js +1 -0
- package/esm/server/services/device-authorization.d.ts +134 -0
- package/esm/server/services/device-authorization.js +7 -0
- package/esm/server/services/token.d.ts +242 -0
- package/esm/server/services/token.js +78 -0
- package/esm/server/services/user.d.ts +29 -0
- package/esm/server/services/user.js +7 -0
- package/esm/server/signing-keys.d.ts +201 -0
- package/esm/server/signing-keys.js +225 -0
- package/esm/server/utils/hash.d.ts +6 -0
- package/esm/server/utils/hash.js +11 -0
- package/esm/testing/contract/authorization-code.d.ts +37 -0
- package/esm/testing/contract/authorization-code.js +93 -0
- package/esm/testing/contract/client.d.ts +85 -0
- package/esm/testing/contract/client.js +126 -0
- package/esm/testing/contract/device-authorization.d.ts +37 -0
- package/esm/testing/contract/device-authorization.js +193 -0
- package/esm/testing/contract/lockout-store.d.ts +47 -0
- package/esm/testing/contract/lockout-store.js +122 -0
- package/esm/testing/contract/mfa-store.d.ts +50 -0
- package/esm/testing/contract/mfa-store.js +226 -0
- package/esm/testing/contract/mod.d.ts +39 -0
- package/esm/testing/contract/mod.js +39 -0
- package/esm/testing/contract/otp-store.d.ts +48 -0
- package/esm/testing/contract/otp-store.js +197 -0
- package/esm/testing/contract/rate-limit-store.d.ts +52 -0
- package/esm/testing/contract/rate-limit-store.js +105 -0
- package/esm/testing/contract/token-flow-store.d.ts +54 -0
- package/esm/testing/contract/token-flow-store.js +179 -0
- package/esm/testing/contract/token-reader.d.ts +77 -0
- package/esm/testing/contract/token-reader.js +78 -0
- package/esm/testing/contract/token.d.ts +61 -0
- package/esm/testing/contract/token.js +287 -0
- package/esm/testing/contract/user.d.ts +63 -0
- package/esm/testing/contract/user.js +75 -0
- package/esm/testing/mod.d.ts +34 -0
- package/esm/testing/mod.js +34 -0
- package/esm/testing/server.d.ts +197 -0
- package/esm/testing/server.js +193 -0
- package/esm/testing/services.d.ts +373 -0
- package/esm/testing/services.js +703 -0
- package/esm/utils/_buffer.d.ts +10 -0
- package/esm/utils/_buffer.js +12 -0
- package/esm/utils/_timing-safe.d.ts +13 -0
- package/esm/utils/_timing-safe.js +22 -0
- package/esm/utils/authentication-context.d.ts +3 -0
- package/esm/utils/authentication-context.js +14 -0
- package/esm/utils/basic-auth.d.ts +50 -0
- package/esm/utils/basic-auth.js +106 -0
- package/esm/utils/crypto.d.ts +97 -0
- package/esm/utils/crypto.js +162 -0
- package/esm/utils/pkce.d.ts +151 -0
- package/esm/utils/pkce.js +179 -0
- package/esm/utils/text.d.ts +24 -0
- package/esm/utils/text.js +28 -0
- package/esm/utils/url.d.ts +66 -0
- package/esm/utils/url.js +90 -0
- package/package.json +92 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract test suite for {@link TokenServiceInterface} implementations.
|
|
3
|
+
*
|
|
4
|
+
* Verifies the methods the framework actually calls during the OAuth2
|
|
5
|
+
* flows: token persistence, refresh-token lookup, revocation by token
|
|
6
|
+
* value or by authorization code, and per-(client, user) lifetime/scope
|
|
7
|
+
* generation.
|
|
8
|
+
*
|
|
9
|
+
* Both a token with a user and a token without one are exercised. The client
|
|
10
|
+
* credentials grant (RFC 6749 §4.4) has no resource owner, so the framework
|
|
11
|
+
* calls `acceptedScope`, `generateAccessToken`, and `accessTokenExpiresAt`
|
|
12
|
+
* with `user: undefined` and persists a token whose `user` is unset — a store
|
|
13
|
+
* that requires a user cannot hold a machine token.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { assert, assertEquals, assertStrictEquals } from "../../deps/jsr.io/@std/assert/1.0.19/mod.js";
|
|
18
|
+
import { beforeEach, describe, it } from "../../deps/jsr.io/@std/testing/1.0.20/bdd.js";
|
|
19
|
+
import { BasicScope } from "../../models/scope.js";
|
|
20
|
+
const defaultMakeUser = (seq) => ({ id: `u${seq}`, username: `user${seq}` });
|
|
21
|
+
const defaultMakeClient = (seq) => ({
|
|
22
|
+
id: `client-${seq}`,
|
|
23
|
+
grants: ["authorization_code", "refresh_token"],
|
|
24
|
+
});
|
|
25
|
+
const defaultMakeScope = (value) => new BasicScope(value);
|
|
26
|
+
/** Runs the {@link TokenServiceInterface} contract suite. */
|
|
27
|
+
export function runTokenServiceContractTests(options) {
|
|
28
|
+
const makeUser = options.makeUser ?? (defaultMakeUser);
|
|
29
|
+
const makeClient = options.makeClient ?? (defaultMakeClient);
|
|
30
|
+
const makeScope = options.makeScope ?? (defaultMakeScope);
|
|
31
|
+
const expectReuseDetection = options.refreshTokenReuseDetection ?? true;
|
|
32
|
+
describe(options.describeName ?? "TokenServiceInterface contract", () => {
|
|
33
|
+
let userService;
|
|
34
|
+
let clientService;
|
|
35
|
+
let tokenService;
|
|
36
|
+
let user;
|
|
37
|
+
let client;
|
|
38
|
+
beforeEach(async () => {
|
|
39
|
+
const services = await options.makeServices();
|
|
40
|
+
userService = services.userService;
|
|
41
|
+
clientService = services.clientService;
|
|
42
|
+
tokenService = services.tokenService;
|
|
43
|
+
user = makeUser(1);
|
|
44
|
+
client = makeClient(1);
|
|
45
|
+
await options.addUser(userService, user, "pw");
|
|
46
|
+
await options.addClient(clientService, client);
|
|
47
|
+
});
|
|
48
|
+
describe("generateAccessToken / generateRefreshToken", () => {
|
|
49
|
+
it("produces unique access tokens", async () => {
|
|
50
|
+
const a = await tokenService.generateAccessToken(client, user);
|
|
51
|
+
const b = await tokenService.generateAccessToken(client, user);
|
|
52
|
+
assertEquals(typeof a, "string");
|
|
53
|
+
assertEquals(typeof b, "string");
|
|
54
|
+
assert(a !== b, "access tokens should not collide");
|
|
55
|
+
});
|
|
56
|
+
it("produces unique refresh tokens (or undefined to opt out)", async () => {
|
|
57
|
+
const a = await tokenService.generateRefreshToken(client, user);
|
|
58
|
+
const b = await tokenService.generateRefreshToken(client, user);
|
|
59
|
+
if (a !== undefined && b !== undefined) {
|
|
60
|
+
assert(a !== b, "refresh tokens should not collide");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("acceptedScope", () => {
|
|
65
|
+
it("returns the requested scope unchanged for the simple case", async () => {
|
|
66
|
+
const requested = makeScope("read");
|
|
67
|
+
const result = await tokenService.acceptedScope(client, user, requested);
|
|
68
|
+
assertEquals(result?.toString(), "read");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("save + getToken", () => {
|
|
72
|
+
it("round-trips an access-only token", async () => {
|
|
73
|
+
const accessToken = await tokenService.generateAccessToken(client, user);
|
|
74
|
+
const expiresAt = new Date(Date.now() + 60_000);
|
|
75
|
+
const token = {
|
|
76
|
+
accessToken,
|
|
77
|
+
accessTokenExpiresAt: expiresAt,
|
|
78
|
+
client,
|
|
79
|
+
user,
|
|
80
|
+
};
|
|
81
|
+
await tokenService.save(token);
|
|
82
|
+
const fetched = await tokenService.getToken(accessToken);
|
|
83
|
+
assertEquals(fetched?.client.id, client.id);
|
|
84
|
+
assertEquals(fetched?.user?.id, user.id);
|
|
85
|
+
assertStrictEquals(fetched?.accessTokenExpiresAt?.getTime(), expiresAt.getTime());
|
|
86
|
+
});
|
|
87
|
+
it("returns undefined for an unknown access token", async () => {
|
|
88
|
+
assertStrictEquals(await tokenService.getToken("nope"), undefined);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("tokens with no user", () => {
|
|
92
|
+
it("generates an access token for a client with no user", async () => {
|
|
93
|
+
const accessToken = await tokenService.generateAccessToken(client, undefined);
|
|
94
|
+
assertEquals(typeof accessToken, "string");
|
|
95
|
+
});
|
|
96
|
+
it("accepts scope for a client with no user", async () => {
|
|
97
|
+
const result = await tokenService.acceptedScope(client, undefined, makeScope("read"));
|
|
98
|
+
assert(result !== false, "scope must not be rejected for lack of a user");
|
|
99
|
+
});
|
|
100
|
+
it("dates the expiry of an access token with no user", async () => {
|
|
101
|
+
const expiresAt = await tokenService.accessTokenExpiresAt(client, undefined);
|
|
102
|
+
if (expiresAt)
|
|
103
|
+
assert(expiresAt.getTime() > Date.now());
|
|
104
|
+
});
|
|
105
|
+
it("round-trips a token with no user", async () => {
|
|
106
|
+
const accessToken = await tokenService.generateAccessToken(client, undefined);
|
|
107
|
+
await tokenService.save({ accessToken, client });
|
|
108
|
+
const fetched = await tokenService.getToken(accessToken);
|
|
109
|
+
assertEquals(fetched?.client.id, client.id);
|
|
110
|
+
assertStrictEquals(fetched?.user, undefined);
|
|
111
|
+
});
|
|
112
|
+
it("revokes a token with no user", async () => {
|
|
113
|
+
const accessToken = await tokenService.generateAccessToken(client, undefined);
|
|
114
|
+
await tokenService.save({ accessToken, client });
|
|
115
|
+
assertStrictEquals(await tokenService.revoke(accessToken), true);
|
|
116
|
+
assertStrictEquals(await tokenService.getToken(accessToken), undefined);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe("save + getRefreshToken", () => {
|
|
120
|
+
it("round-trips a refresh token", async () => {
|
|
121
|
+
const accessToken = await tokenService.generateAccessToken(client, user);
|
|
122
|
+
const refreshToken = (await tokenService.generateRefreshToken(client, user));
|
|
123
|
+
const token = {
|
|
124
|
+
accessToken,
|
|
125
|
+
accessTokenExpiresAt: new Date(Date.now() + 60_000),
|
|
126
|
+
refreshToken,
|
|
127
|
+
refreshTokenExpiresAt: new Date(Date.now() + 600_000),
|
|
128
|
+
client,
|
|
129
|
+
user,
|
|
130
|
+
};
|
|
131
|
+
await tokenService.save(token);
|
|
132
|
+
const fetched = await tokenService.getRefreshToken(refreshToken);
|
|
133
|
+
assertEquals(fetched?.client.id, client.id);
|
|
134
|
+
});
|
|
135
|
+
it("returns undefined for an unknown refresh token", async () => {
|
|
136
|
+
assertStrictEquals(await tokenService.getRefreshToken("nope"), undefined);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe("revoke", () => {
|
|
140
|
+
it("revokes by token object", async () => {
|
|
141
|
+
const accessToken = await tokenService.generateAccessToken(client, user);
|
|
142
|
+
const token = {
|
|
143
|
+
accessToken,
|
|
144
|
+
client,
|
|
145
|
+
user,
|
|
146
|
+
};
|
|
147
|
+
await tokenService.save(token);
|
|
148
|
+
const ok = await tokenService.revoke(token);
|
|
149
|
+
assertStrictEquals(ok, true);
|
|
150
|
+
assertStrictEquals(await tokenService.getToken(accessToken), undefined);
|
|
151
|
+
});
|
|
152
|
+
it("revokes by access token string", async () => {
|
|
153
|
+
const accessToken = await tokenService.generateAccessToken(client, user);
|
|
154
|
+
await tokenService.save({ accessToken, client, user });
|
|
155
|
+
const ok = await tokenService.revoke(accessToken);
|
|
156
|
+
assertStrictEquals(ok, true);
|
|
157
|
+
assertStrictEquals(await tokenService.getToken(accessToken), undefined);
|
|
158
|
+
});
|
|
159
|
+
it("revokes by refresh token string when hint='refresh_token'", async () => {
|
|
160
|
+
const accessToken = await tokenService.generateAccessToken(client, user);
|
|
161
|
+
const refreshToken = (await tokenService.generateRefreshToken(client, user));
|
|
162
|
+
await tokenService.save({
|
|
163
|
+
accessToken,
|
|
164
|
+
refreshToken,
|
|
165
|
+
client,
|
|
166
|
+
user,
|
|
167
|
+
});
|
|
168
|
+
const ok = await tokenService.revoke(refreshToken, "refresh_token");
|
|
169
|
+
assertStrictEquals(ok, true);
|
|
170
|
+
assertStrictEquals(await tokenService.getRefreshToken(refreshToken), undefined);
|
|
171
|
+
assertStrictEquals(await tokenService.getToken(accessToken), undefined);
|
|
172
|
+
});
|
|
173
|
+
it("returns false for an unknown token string", async () => {
|
|
174
|
+
assertStrictEquals(await tokenService.revoke("missing"), false);
|
|
175
|
+
});
|
|
176
|
+
it("answers true only for the call that removed a live token", async () => {
|
|
177
|
+
const accessToken = await tokenService.generateAccessToken(client, user);
|
|
178
|
+
const token = { accessToken, client, user };
|
|
179
|
+
assertStrictEquals(await tokenService.revoke(token), false, "a token that was never saved was not revoked by this call — " +
|
|
180
|
+
"answering true makes a single-use check built on the result inert");
|
|
181
|
+
await tokenService.save(token);
|
|
182
|
+
assertStrictEquals(await tokenService.revoke(token), true);
|
|
183
|
+
assertStrictEquals(await tokenService.revoke(token), false);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe("revokeCode", () => {
|
|
187
|
+
it("revokes every token issued under a given authorization code", async () => {
|
|
188
|
+
const code = "shared-code";
|
|
189
|
+
const accessToken1 = await tokenService.generateAccessToken(client, user);
|
|
190
|
+
const accessToken2 = await tokenService.generateAccessToken(client, user);
|
|
191
|
+
await tokenService.save({
|
|
192
|
+
accessToken: accessToken1,
|
|
193
|
+
code,
|
|
194
|
+
client,
|
|
195
|
+
user,
|
|
196
|
+
});
|
|
197
|
+
await tokenService.save({
|
|
198
|
+
accessToken: accessToken2,
|
|
199
|
+
code,
|
|
200
|
+
client,
|
|
201
|
+
user,
|
|
202
|
+
});
|
|
203
|
+
const ok = await tokenService.revokeCode(code);
|
|
204
|
+
assertStrictEquals(ok, true);
|
|
205
|
+
assertStrictEquals(await tokenService.getToken(accessToken1), undefined);
|
|
206
|
+
assertStrictEquals(await tokenService.getToken(accessToken2), undefined);
|
|
207
|
+
});
|
|
208
|
+
it("returns false when no tokens were issued for the code", async () => {
|
|
209
|
+
assertStrictEquals(await tokenService.revokeCode("never-used"), false);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
if (expectReuseDetection) {
|
|
213
|
+
describe("refresh-token rotation family", () => {
|
|
214
|
+
const FAMILY = "family-1";
|
|
215
|
+
const OTHER_FAMILY = "family-2";
|
|
216
|
+
async function saveFamilyMember(familyId) {
|
|
217
|
+
const accessToken = await tokenService.generateAccessToken(client, user);
|
|
218
|
+
const refreshToken = (await tokenService.generateRefreshToken(client, user));
|
|
219
|
+
const token = {
|
|
220
|
+
accessToken,
|
|
221
|
+
accessTokenExpiresAt: new Date(Date.now() + 60_000),
|
|
222
|
+
refreshToken,
|
|
223
|
+
refreshTokenExpiresAt: new Date(Date.now() + 600_000),
|
|
224
|
+
familyId,
|
|
225
|
+
familyCreatedAt: new Date(Date.now() - 60_000),
|
|
226
|
+
client,
|
|
227
|
+
user,
|
|
228
|
+
};
|
|
229
|
+
await tokenService.save(token);
|
|
230
|
+
return token;
|
|
231
|
+
}
|
|
232
|
+
it("implements both halves of reuse detection", () => {
|
|
233
|
+
assert(typeof tokenService.getRevokedRefreshToken === "function" &&
|
|
234
|
+
typeof tokenService.revokeFamily === "function", "getRevokedRefreshToken and revokeFamily are optional, but " +
|
|
235
|
+
"without both the refresh grant cannot tell a replayed " +
|
|
236
|
+
"rotated-out token from an unknown one and reuse detection is " +
|
|
237
|
+
"silently disabled. Implement them, or pass " +
|
|
238
|
+
"`refreshTokenReuseDetection: false` to record the gap.");
|
|
239
|
+
});
|
|
240
|
+
it("keeps a rotated-out refresh token findable, with its family", async () => {
|
|
241
|
+
const token = await saveFamilyMember(FAMILY);
|
|
242
|
+
await tokenService.revoke(token.refreshToken, "refresh_token");
|
|
243
|
+
assertStrictEquals(await tokenService.getRefreshToken(token.refreshToken), undefined, "a rotated-out token must no longer refresh");
|
|
244
|
+
const revoked = await tokenService.getRevokedRefreshToken(token.refreshToken);
|
|
245
|
+
assert(revoked !== undefined, "a revoked refresh token must stay findable — deleting the row " +
|
|
246
|
+
"is what turns a replay into an ordinary unknown token");
|
|
247
|
+
assertStrictEquals(revoked.familyId, FAMILY);
|
|
248
|
+
});
|
|
249
|
+
it("does not report a live refresh token as revoked", async () => {
|
|
250
|
+
const token = await saveFamilyMember(FAMILY);
|
|
251
|
+
assertStrictEquals(await tokenService.getRevokedRefreshToken(token.refreshToken), undefined, "reporting a live token as revoked turns every legitimate " +
|
|
252
|
+
"refresh into a detected replay");
|
|
253
|
+
});
|
|
254
|
+
it("returns undefined for a refresh token that was never issued", async () => {
|
|
255
|
+
assertStrictEquals(await tokenService.getRevokedRefreshToken("never-issued"), undefined);
|
|
256
|
+
});
|
|
257
|
+
it("revokes every live member of a replayed token's family", async () => {
|
|
258
|
+
const first = await saveFamilyMember(FAMILY);
|
|
259
|
+
const second = await saveFamilyMember(FAMILY);
|
|
260
|
+
assertStrictEquals(await tokenService.revokeFamily(FAMILY), true);
|
|
261
|
+
assertStrictEquals(await tokenService.getRefreshToken(first.refreshToken), undefined);
|
|
262
|
+
assertStrictEquals(await tokenService.getRefreshToken(second.refreshToken), undefined);
|
|
263
|
+
assertStrictEquals(await tokenService.getToken(second.accessToken), undefined, "the family's access tokens die with it — leaving them live " +
|
|
264
|
+
"keeps the thief's session alive for their remaining lifetime");
|
|
265
|
+
});
|
|
266
|
+
it("leaves other families untouched", async () => {
|
|
267
|
+
const mine = await saveFamilyMember(FAMILY);
|
|
268
|
+
const theirs = await saveFamilyMember(OTHER_FAMILY);
|
|
269
|
+
await tokenService.revokeFamily(FAMILY);
|
|
270
|
+
assertStrictEquals(await tokenService.getRefreshToken(mine.refreshToken), undefined);
|
|
271
|
+
assert(await tokenService.getRefreshToken(theirs.refreshToken) !==
|
|
272
|
+
undefined, "one compromised family must not sign every other session out");
|
|
273
|
+
});
|
|
274
|
+
it("returns false when the family has no live tokens", async () => {
|
|
275
|
+
assertStrictEquals(await tokenService.revokeFamily("never-issued"), false);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
it({
|
|
281
|
+
name: "refresh-token reuse detection is not implemented — a replayed rotated-out token cannot be detected (refreshTokenReuseDetection: false)",
|
|
282
|
+
ignore: true,
|
|
283
|
+
fn: () => { },
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract test suite for {@link UserServiceInterface} implementations.
|
|
3
|
+
*
|
|
4
|
+
* Apps that swap a `MemoryUserService` for a DB-backed implementation
|
|
5
|
+
* can run this suite against their factory to verify they satisfy what
|
|
6
|
+
* the framework expects.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { runUserServiceContractTests } from "@udibo/oauth2/testing/contract";
|
|
11
|
+
* import type { UserServiceInterface } from "@udibo/oauth2/server/authorization";
|
|
12
|
+
*
|
|
13
|
+
* interface AppUser {
|
|
14
|
+
* id: string;
|
|
15
|
+
* username: string;
|
|
16
|
+
* }
|
|
17
|
+
* interface DrizzleUserService extends UserServiceInterface<AppUser> {
|
|
18
|
+
* register(user: AppUser, password: string): Promise<void>;
|
|
19
|
+
* }
|
|
20
|
+
* declare function freshUserService(): Promise<DrizzleUserService>;
|
|
21
|
+
*
|
|
22
|
+
* runUserServiceContractTests<AppUser>({
|
|
23
|
+
* makeService: freshUserService,
|
|
24
|
+
* addUser: (svc, user, password) =>
|
|
25
|
+
* (svc as DrizzleUserService).register(user, password),
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
import type { UserServiceInterface } from "../../server/services/user.js";
|
|
32
|
+
import type { MemoryUserShape } from "../services.js";
|
|
33
|
+
/** Options for {@link runUserServiceContractTests}. */
|
|
34
|
+
export interface UserServiceContractOptions<U extends MemoryUserShape> {
|
|
35
|
+
/**
|
|
36
|
+
* Returns a fresh service for each test. Must be isolated — one test
|
|
37
|
+
* leaking state into another defeats the contract guarantees.
|
|
38
|
+
*/
|
|
39
|
+
makeService(): Promise<UserServiceInterface<U>> | UserServiceInterface<U>;
|
|
40
|
+
/**
|
|
41
|
+
* Adds a user with the given password to the service. Not part of the
|
|
42
|
+
* interface, so the consumer supplies it. For the framework's own
|
|
43
|
+
* `MemoryUserService` this is `(svc, user, pw) => svc.add(user, pw)`.
|
|
44
|
+
*/
|
|
45
|
+
addUser(service: UserServiceInterface<U>, user: U, password: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Optional factory for the test user. Defaults to a sequenced
|
|
48
|
+
* `{ id, username }` shape that satisfies {@link MemoryUserShape}.
|
|
49
|
+
* Override when your `User` requires extra fields.
|
|
50
|
+
*/
|
|
51
|
+
makeUser?(seq: number): U;
|
|
52
|
+
/**
|
|
53
|
+
* Optional name for the inner `describe` block. Defaults to
|
|
54
|
+
* `"UserServiceInterface contract"`.
|
|
55
|
+
*/
|
|
56
|
+
describeName?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Runs the {@link UserServiceInterface} contract suite. Call from your
|
|
60
|
+
* own test file — the suite registers `describe` / `it` blocks that the
|
|
61
|
+
* runner picks up.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runUserServiceContractTests<U extends MemoryUserShape>(options: UserServiceContractOptions<U>): void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract test suite for {@link UserServiceInterface} implementations.
|
|
3
|
+
*
|
|
4
|
+
* Apps that swap a `MemoryUserService` for a DB-backed implementation
|
|
5
|
+
* can run this suite against their factory to verify they satisfy what
|
|
6
|
+
* the framework expects.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { runUserServiceContractTests } from "@udibo/oauth2/testing/contract";
|
|
11
|
+
* import type { UserServiceInterface } from "@udibo/oauth2/server/authorization";
|
|
12
|
+
*
|
|
13
|
+
* interface AppUser {
|
|
14
|
+
* id: string;
|
|
15
|
+
* username: string;
|
|
16
|
+
* }
|
|
17
|
+
* interface DrizzleUserService extends UserServiceInterface<AppUser> {
|
|
18
|
+
* register(user: AppUser, password: string): Promise<void>;
|
|
19
|
+
* }
|
|
20
|
+
* declare function freshUserService(): Promise<DrizzleUserService>;
|
|
21
|
+
*
|
|
22
|
+
* runUserServiceContractTests<AppUser>({
|
|
23
|
+
* makeService: freshUserService,
|
|
24
|
+
* addUser: (svc, user, password) =>
|
|
25
|
+
* (svc as DrizzleUserService).register(user, password),
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
import { assertEquals, assertStrictEquals } from "../../deps/jsr.io/@std/assert/1.0.19/mod.js";
|
|
32
|
+
import { beforeEach, describe, it } from "../../deps/jsr.io/@std/testing/1.0.20/bdd.js";
|
|
33
|
+
const defaultMakeUser = (seq) => ({ id: `u${seq}`, username: `user${seq}` });
|
|
34
|
+
/**
|
|
35
|
+
* Runs the {@link UserServiceInterface} contract suite. Call from your
|
|
36
|
+
* own test file — the suite registers `describe` / `it` blocks that the
|
|
37
|
+
* runner picks up.
|
|
38
|
+
*/
|
|
39
|
+
export function runUserServiceContractTests(options) {
|
|
40
|
+
const makeUser = options.makeUser ?? (defaultMakeUser);
|
|
41
|
+
describe(options.describeName ?? "UserServiceInterface contract", () => {
|
|
42
|
+
let service;
|
|
43
|
+
beforeEach(async () => {
|
|
44
|
+
service = await options.makeService();
|
|
45
|
+
});
|
|
46
|
+
describe("get", () => {
|
|
47
|
+
it("returns undefined for an unknown id", async () => {
|
|
48
|
+
assertStrictEquals(await service.get("does-not-exist"), undefined);
|
|
49
|
+
});
|
|
50
|
+
it("returns the user for a known id", async () => {
|
|
51
|
+
const user = makeUser(1);
|
|
52
|
+
await options.addUser(service, user, "pw");
|
|
53
|
+
const fetched = await service.get(user.id);
|
|
54
|
+
assertEquals(fetched?.id, user.id);
|
|
55
|
+
assertEquals(fetched?.username, user.username);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("getAuthenticated", () => {
|
|
59
|
+
it("returns undefined for an unknown username", async () => {
|
|
60
|
+
assertStrictEquals(await service.getAuthenticated("nobody", "pw"), undefined);
|
|
61
|
+
});
|
|
62
|
+
it("returns undefined for a wrong password", async () => {
|
|
63
|
+
const user = makeUser(2);
|
|
64
|
+
await options.addUser(service, user, "correct");
|
|
65
|
+
assertStrictEquals(await service.getAuthenticated(user.username, "wrong"), undefined);
|
|
66
|
+
});
|
|
67
|
+
it("returns the user when username + password match", async () => {
|
|
68
|
+
const user = makeUser(3);
|
|
69
|
+
await options.addUser(service, user, "secret");
|
|
70
|
+
const auth = await service.getAuthenticated(user.username, "secret");
|
|
71
|
+
assertEquals(auth?.id, user.id);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing helpers for apps that use `@udibo/oauth2`.
|
|
3
|
+
*
|
|
4
|
+
* Two layers ship:
|
|
5
|
+
*
|
|
6
|
+
* - **Testing surface for the OAuth2 protocol.** The recommended pattern
|
|
7
|
+
* for testing apps that use the resource server is to expose your
|
|
8
|
+
* `IntrospectionTokenReader` instance from your app's OAuth2 wiring module
|
|
9
|
+
* and stub `tokenReader.getToken` directly with `@std/testing/mock`'s
|
|
10
|
+
* `stub` —
|
|
11
|
+
* one method on one object, no `fetch` indirection, no wire format to
|
|
12
|
+
* fake. For tests that need the OAuth2 protocol end-to-end (BFF flows,
|
|
13
|
+
* SPA integration), build a real in-process authorization server with
|
|
14
|
+
* {@link createMemoryAuthorizationServer} and pass its `fetch` to your
|
|
15
|
+
* `DirectClient` via the constructor's `fetch` option. Both patterns
|
|
16
|
+
* are constructor- or method-scoped — they never touch
|
|
17
|
+
* `globalThis.fetch`, which silently captures every other outbound
|
|
18
|
+
* request the app makes.
|
|
19
|
+
*
|
|
20
|
+
* - **In-memory service implementations.** `MemoryUserService`,
|
|
21
|
+
* `MemoryClientService`, `MemoryTokenService`,
|
|
22
|
+
* `MemoryAuthorizationCodeService`, and `MemoryDeviceAuthorizationService`
|
|
23
|
+
* satisfy every server-side service contract, generic over the consumer's
|
|
24
|
+
* user / client / scope types. Suitable for tests, examples, and
|
|
25
|
+
* prototypes — never for production.
|
|
26
|
+
*
|
|
27
|
+
* Nothing in this module is an OAuth2 primitive — only testing support.
|
|
28
|
+
* Production code should never import from here.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import "../_dnt.polyfills.js";
|
|
33
|
+
export { MemoryAuthorizationCodeService, type MemoryAuthorizationCodeServiceOptions, MemoryClientService, MemoryDeviceAuthorizationService, type MemoryDeviceAuthorizationServiceOptions, MemoryTokenService, type MemoryTokenServiceOptions, MemoryUserService, type MemoryUserServiceOptions, type MemoryUserShape, } from "./services.js";
|
|
34
|
+
export { createMemoryAuthorizationServer, type CreateMemoryAuthorizationServerOptions, type MemoryAuthorizationServerClientSeed, type MemoryAuthorizationServerGrants, type MemoryAuthorizationServerHarness, type MemoryAuthorizationServerUserSeed, } from "./server.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Testing helpers for apps that use `@udibo/oauth2`.
|
|
3
|
+
*
|
|
4
|
+
* Two layers ship:
|
|
5
|
+
*
|
|
6
|
+
* - **Testing surface for the OAuth2 protocol.** The recommended pattern
|
|
7
|
+
* for testing apps that use the resource server is to expose your
|
|
8
|
+
* `IntrospectionTokenReader` instance from your app's OAuth2 wiring module
|
|
9
|
+
* and stub `tokenReader.getToken` directly with `@std/testing/mock`'s
|
|
10
|
+
* `stub` —
|
|
11
|
+
* one method on one object, no `fetch` indirection, no wire format to
|
|
12
|
+
* fake. For tests that need the OAuth2 protocol end-to-end (BFF flows,
|
|
13
|
+
* SPA integration), build a real in-process authorization server with
|
|
14
|
+
* {@link createMemoryAuthorizationServer} and pass its `fetch` to your
|
|
15
|
+
* `DirectClient` via the constructor's `fetch` option. Both patterns
|
|
16
|
+
* are constructor- or method-scoped — they never touch
|
|
17
|
+
* `globalThis.fetch`, which silently captures every other outbound
|
|
18
|
+
* request the app makes.
|
|
19
|
+
*
|
|
20
|
+
* - **In-memory service implementations.** `MemoryUserService`,
|
|
21
|
+
* `MemoryClientService`, `MemoryTokenService`,
|
|
22
|
+
* `MemoryAuthorizationCodeService`, and `MemoryDeviceAuthorizationService`
|
|
23
|
+
* satisfy every server-side service contract, generic over the consumer's
|
|
24
|
+
* user / client / scope types. Suitable for tests, examples, and
|
|
25
|
+
* prototypes — never for production.
|
|
26
|
+
*
|
|
27
|
+
* Nothing in this module is an OAuth2 primitive — only testing support.
|
|
28
|
+
* Production code should never import from here.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import "../_dnt.polyfills.js";
|
|
33
|
+
export { MemoryAuthorizationCodeService, MemoryClientService, MemoryDeviceAuthorizationService, MemoryTokenService, MemoryUserService, } from "./services.js";
|
|
34
|
+
export { createMemoryAuthorizationServer, } from "./server.js";
|