@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,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client authentication shared by the authorization server's endpoints and by
|
|
3
|
+
* the grants, so every endpoint answers an unauthenticated or wrongly
|
|
4
|
+
* authenticated request the same way (RFC 6749 Section 2.3).
|
|
5
|
+
*
|
|
6
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { ClientCredentials, ClientInterface } from "../models/client.js";
|
|
10
|
+
import type { ClientServiceInterface } from "./services/client.js";
|
|
11
|
+
/**
|
|
12
|
+
* Reads the credentials a request presents: HTTP Basic first (RFC 6749 Section
|
|
13
|
+
* 2.3.1), then `client_id`/`client_secret` in `body`, the form body the
|
|
14
|
+
* endpoint already parsed. An `Authorization` header that is not usable Basic
|
|
15
|
+
* falls through to the body, so a request that also carries body credentials
|
|
16
|
+
* authenticates by the method it did present.
|
|
17
|
+
*
|
|
18
|
+
* @throws {InvalidClientError} If the request presents no usable credentials.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractClientCredentials(request: Request, body: FormData): ClientCredentials;
|
|
21
|
+
/**
|
|
22
|
+
* Looks up the client the credentials authenticate. Pair it with
|
|
23
|
+
* {@linkcode extractClientCredentials} unless the caller applies its own
|
|
24
|
+
* credential rules (as the authorization-code grant does for PKCE).
|
|
25
|
+
*
|
|
26
|
+
* @throws {InvalidClientError} If no client matches the credentials.
|
|
27
|
+
*/
|
|
28
|
+
export declare function authenticateClientCredentials<Client extends ClientInterface, User>(credentials: ClientCredentials, clientService: ClientServiceInterface<Client, User>): Promise<Client>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client authentication shared by the authorization server's endpoints and by
|
|
3
|
+
* the grants, so every endpoint answers an unauthenticated or wrongly
|
|
4
|
+
* authenticated request the same way (RFC 6749 Section 2.3).
|
|
5
|
+
*
|
|
6
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { InvalidClientError } from "../errors.js";
|
|
10
|
+
import { parseBasicAuth, tryParseBasicAuth } from "../utils/basic-auth.js";
|
|
11
|
+
/**
|
|
12
|
+
* Reads the credentials a request presents: HTTP Basic first (RFC 6749 Section
|
|
13
|
+
* 2.3.1), then `client_id`/`client_secret` in `body`, the form body the
|
|
14
|
+
* endpoint already parsed. An `Authorization` header that is not usable Basic
|
|
15
|
+
* falls through to the body, so a request that also carries body credentials
|
|
16
|
+
* authenticates by the method it did present.
|
|
17
|
+
*
|
|
18
|
+
* @throws {InvalidClientError} If the request presents no usable credentials.
|
|
19
|
+
*/
|
|
20
|
+
export function extractClientCredentials(request, body) {
|
|
21
|
+
const authorization = request.headers.get("authorization");
|
|
22
|
+
const basicAuth = tryParseBasicAuth(authorization);
|
|
23
|
+
if (basicAuth) {
|
|
24
|
+
return { clientId: basicAuth.name, clientSecret: basicAuth.pass };
|
|
25
|
+
}
|
|
26
|
+
const clientId = body.get("client_id");
|
|
27
|
+
if (typeof clientId === "string") {
|
|
28
|
+
const credentials = { clientId };
|
|
29
|
+
const clientSecret = body.get("client_secret");
|
|
30
|
+
if (typeof clientSecret === "string") {
|
|
31
|
+
credentials.clientSecret = clientSecret;
|
|
32
|
+
}
|
|
33
|
+
return credentials;
|
|
34
|
+
}
|
|
35
|
+
if (authorization)
|
|
36
|
+
throw parseBasicAuth(authorization);
|
|
37
|
+
throw new InvalidClientError("client authentication required");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Looks up the client the credentials authenticate. Pair it with
|
|
41
|
+
* {@linkcode extractClientCredentials} unless the caller applies its own
|
|
42
|
+
* credential rules (as the authorization-code grant does for PKCE).
|
|
43
|
+
*
|
|
44
|
+
* @throws {InvalidClientError} If no client matches the credentials.
|
|
45
|
+
*/
|
|
46
|
+
export async function authenticateClientCredentials(credentials, clientService) {
|
|
47
|
+
const client = await clientService.getAuthenticated(credentials.clientId, credentials.clientSecret);
|
|
48
|
+
if (!client)
|
|
49
|
+
throw new InvalidClientError("client authentication failed");
|
|
50
|
+
return client;
|
|
51
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorization Code grant type implementation with PKCE support.
|
|
3
|
+
* RFC 6749 Section 4.1 and RFC 7636.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthorizationCode } from "../../models/authorization-code.js";
|
|
7
|
+
import type { AuthenticationContext } from "../../models/authentication.js";
|
|
8
|
+
import type { ClientCredentials, ClientInterface } from "../../models/client.js";
|
|
9
|
+
import type { Token } from "../../models/token.js";
|
|
10
|
+
import type { AbstractScope, BasicScope } from "../../models/scope.js";
|
|
11
|
+
import type { ChallengeMethod, ChallengeMethods } from "../../utils/pkce.js";
|
|
12
|
+
import type { ClientServiceInterface } from "../services/client.js";
|
|
13
|
+
import type { AuthorizationCodeServiceInterface } from "../services/authorization-code.js";
|
|
14
|
+
import { AbstractGrant, type GrantOptions, type GrantServices } from "./grant.js";
|
|
15
|
+
/** Services required by the authorization code grant. */
|
|
16
|
+
export interface AuthorizationCodeGrantServices<Client extends ClientInterface, User, S extends AbstractScope> extends GrantServices<Client, User, S> {
|
|
17
|
+
/** Generates, stores, and looks up authorization codes for the exchange. */
|
|
18
|
+
authorizationCodeService: AuthorizationCodeServiceInterface<Client, User, S>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Options for configuring the authorization code grant.
|
|
22
|
+
*
|
|
23
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
|
|
24
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636
|
|
25
|
+
*/
|
|
26
|
+
export interface AuthorizationCodeGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> extends GrantOptions<Client, User, S, AuthorizationCodeGrantServices<Client, User, S>> {
|
|
27
|
+
/** Allow optional refresh token. Defaults to true. */
|
|
28
|
+
allowRefreshToken?: boolean;
|
|
29
|
+
/** Custom PKCE challenge methods. Defaults to S256 only. */
|
|
30
|
+
challengeMethods?: ChallengeMethods;
|
|
31
|
+
/**
|
|
32
|
+
* Whether to require PKCE for all authorization requests.
|
|
33
|
+
*
|
|
34
|
+
* Per OAuth 2.1 and security best practices, PKCE is required for all OAuth
|
|
35
|
+
* clients, including confidential clients. Defaults to `true`; set it to
|
|
36
|
+
* `false` only to interoperate with legacy clients that cannot send a
|
|
37
|
+
* `code_challenge`.
|
|
38
|
+
*
|
|
39
|
+
* @default true
|
|
40
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-7.5.2
|
|
41
|
+
*/
|
|
42
|
+
requirePKCE?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a client that presents a `code_verifier` must still authenticate
|
|
45
|
+
* with its client credentials.
|
|
46
|
+
*
|
|
47
|
+
* PKCE proves the token request comes from the same party that started the
|
|
48
|
+
* authorization request; it says nothing about *which client* that party is.
|
|
49
|
+
* OAuth 2.1 and RFC 7636 both treat PKCE as additional to client
|
|
50
|
+
* authentication, never a replacement for it, so a confidential client's
|
|
51
|
+
* secret is still required on this grant.
|
|
52
|
+
*
|
|
53
|
+
* Defaults to `true`: confidential clients authenticate alongside PKCE.
|
|
54
|
+
* Public clients, which have no secret, are unaffected. Setting `false`
|
|
55
|
+
* explicitly enables legacy behavior where PKCE replaces client credentials;
|
|
56
|
+
* use that opt-out only for an integration that requires it.
|
|
57
|
+
*
|
|
58
|
+
* @default true
|
|
59
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-4.1.3
|
|
60
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
|
|
61
|
+
* @see https://datatracker.ietf.org/doc/html/rfc9700#section-2.1.1
|
|
62
|
+
*/
|
|
63
|
+
requireClientAuthentication?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** Client credentials extended with optional PKCE code_verifier. */
|
|
66
|
+
export interface PKCEClientCredentials extends ClientCredentials {
|
|
67
|
+
/** The PKCE `code_verifier`, present when the client used PKCE (RFC 7636). */
|
|
68
|
+
codeVerifier?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Options for generating an authorization code. */
|
|
71
|
+
export interface GenerateAuthorizationCodeOptions<Client extends ClientInterface, User, S extends AbstractScope> {
|
|
72
|
+
/** Verified event to capture on this code, supplied by authentication policy. */
|
|
73
|
+
authenticationContext?: AuthenticationContext;
|
|
74
|
+
/** The client the code is issued to. */
|
|
75
|
+
client: Client;
|
|
76
|
+
/** The resource owner the code authorizes. */
|
|
77
|
+
user: User;
|
|
78
|
+
/** The scope to bind to the code. */
|
|
79
|
+
scope?: S | null;
|
|
80
|
+
/** Redirect URI to bind to the code; must match again at the token exchange. */
|
|
81
|
+
redirectUri?: string | null;
|
|
82
|
+
/** PKCE `code_challenge` to bind to the code (RFC 7636). */
|
|
83
|
+
challenge?: string | null;
|
|
84
|
+
/** PKCE `code_challenge_method` (e.g. `S256`). Defaults to `S256`. */
|
|
85
|
+
challengeMethod?: string | null;
|
|
86
|
+
/** OIDC nonce to bind to the code (echoed into the id_token). */
|
|
87
|
+
nonce?: string | null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The authorization code grant type.
|
|
91
|
+
*
|
|
92
|
+
* Implements RFC 6749 Section 4.1 with PKCE support (RFC 7636).
|
|
93
|
+
*
|
|
94
|
+
* PKCE (Proof Key for Code Exchange) is **required by default** for all
|
|
95
|
+
* clients, including confidential ones, per OAuth 2.1. Set `requirePKCE: false`
|
|
96
|
+
* only to interoperate with legacy clients that cannot send a `code_challenge`.
|
|
97
|
+
*
|
|
98
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
|
|
99
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636
|
|
100
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-2.1.1
|
|
101
|
+
*/
|
|
102
|
+
export declare class AuthorizationCodeGrant<Client extends ClientInterface, User, S extends AbstractScope = BasicScope> extends AbstractGrant<Client, User, S, AuthorizationCodeGrantServices<Client, User, S>> {
|
|
103
|
+
/** The OAuth2 grant type: `authorization_code`. */
|
|
104
|
+
readonly grantType = "authorization_code";
|
|
105
|
+
/** The PKCE challenge methods this grant accepts. Defaults to `S256` only. */
|
|
106
|
+
challengeMethods: ChallengeMethods;
|
|
107
|
+
/**
|
|
108
|
+
* Whether PKCE is required for all authorization requests.
|
|
109
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-7.5.2
|
|
110
|
+
*/
|
|
111
|
+
requirePKCE: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Whether a client presenting a `code_verifier` must still authenticate with
|
|
114
|
+
* its client credentials. Defaults to `true`.
|
|
115
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
|
|
116
|
+
*/
|
|
117
|
+
requireClientAuthentication: boolean;
|
|
118
|
+
/** Creates the grant from its {@linkcode AuthorizationCodeGrantOptions}. */
|
|
119
|
+
constructor(options: AuthorizationCodeGrantOptions<Client, User, S>);
|
|
120
|
+
/**
|
|
121
|
+
* Extracts client credentials from a request, carrying the PKCE
|
|
122
|
+
* `code_verifier` alongside them when the client sent one.
|
|
123
|
+
*
|
|
124
|
+
* When {@linkcode requireClientAuthentication} is explicitly `false`, a
|
|
125
|
+
* `code_verifier` drops the `client_secret` from the returned credentials,
|
|
126
|
+
* so the client is looked up rather than authenticated.
|
|
127
|
+
*/
|
|
128
|
+
protected getClientCredentials(request: Request, body: FormData): PKCEClientCredentials;
|
|
129
|
+
/** Gets a client by ID without authentication. */
|
|
130
|
+
getClient(clientId: string, clientService: ClientServiceInterface<Client, User>): Promise<Client>;
|
|
131
|
+
/**
|
|
132
|
+
* Authenticates a client from a request.
|
|
133
|
+
*
|
|
134
|
+
* Confidential clients authenticate with their registered secret by default,
|
|
135
|
+
* including when they send a `code_verifier`. Setting
|
|
136
|
+
* {@linkcode requireClientAuthentication} to `false` makes a request carrying
|
|
137
|
+
* a `code_verifier` look up the client by id without authenticating it.
|
|
138
|
+
*
|
|
139
|
+
* @throws {InvalidClientError} When the client is unknown or its credentials do not authenticate it.
|
|
140
|
+
*/
|
|
141
|
+
getAuthenticatedClient(request: Request, body: FormData): Promise<Client>;
|
|
142
|
+
/**
|
|
143
|
+
* Resolves an allowed challenge method by name, defaulting to `S256`.
|
|
144
|
+
*
|
|
145
|
+
* Only names {@linkcode challengeMethods} holds as its own callable
|
|
146
|
+
* properties resolve, so a client cannot reach an inherited
|
|
147
|
+
* `Object.prototype` member such as `toString` by naming it as its
|
|
148
|
+
* `code_challenge_method`.
|
|
149
|
+
*/
|
|
150
|
+
getChallengeMethod(challengeMethod?: string | null): ChallengeMethod | undefined;
|
|
151
|
+
/** Validates that the challenge method is allowed. */
|
|
152
|
+
validateChallengeMethod(challengeMethod?: string | null): boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Verifies that a code verifier matches the authorization code's challenge.
|
|
155
|
+
* https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
|
|
156
|
+
*/
|
|
157
|
+
verifyCode(code: AuthorizationCode<Client, User, S>, verifier: string): Promise<boolean>;
|
|
158
|
+
/**
|
|
159
|
+
* Generates and saves an authorization code. Resolves its own
|
|
160
|
+
* `authorizationCodeService` from the request.
|
|
161
|
+
*/
|
|
162
|
+
generateAuthorizationCode(options: GenerateAuthorizationCodeOptions<Client, User, S>, request: Request): Promise<AuthorizationCode<Client, User, S>>;
|
|
163
|
+
/**
|
|
164
|
+
* Handles a token request for the authorization code grant.
|
|
165
|
+
*
|
|
166
|
+
* Exchanges an authorization code for an access token. If PKCE was used
|
|
167
|
+
* during authorization, the code_verifier must be provided.
|
|
168
|
+
*
|
|
169
|
+
* The code is claimed by revoking it: only the caller whose revoke wins
|
|
170
|
+
* receives a token, so concurrent exchanges of one code cannot both succeed.
|
|
171
|
+
*
|
|
172
|
+
* @throws {InvalidRequestError} When `code` is missing, a required `code_verifier` is absent, or the verifier is malformed.
|
|
173
|
+
* @throws {InvalidGrantError} When the code is unknown, expired, already claimed, or its verifier or `redirect_uri` does not match.
|
|
174
|
+
* @throws {InvalidClientError} When the code was issued to another client.
|
|
175
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
|
|
176
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
|
|
177
|
+
*/
|
|
178
|
+
token(request: Request, client: Client, body: FormData): Promise<Token<Client, User, S>>;
|
|
179
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authorization Code grant type implementation with PKCE support.
|
|
3
|
+
* RFC 6749 Section 4.1 and RFC 7636.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { snapshotAuthenticationContext } from "../../utils/authentication-context.js";
|
|
7
|
+
import { InvalidClientError, InvalidGrantError, InvalidRequestError, ServerError, } from "../../errors.js";
|
|
8
|
+
import { challengeMethods as defaultChallengeMethods, getChallengeMethod, validateCodeVerifier, } from "../../utils/pkce.js";
|
|
9
|
+
import { authenticateClientCredentials } from "../client-authentication.js";
|
|
10
|
+
import { AbstractGrant, } from "./grant.js";
|
|
11
|
+
/**
|
|
12
|
+
* The authorization code grant type.
|
|
13
|
+
*
|
|
14
|
+
* Implements RFC 6749 Section 4.1 with PKCE support (RFC 7636).
|
|
15
|
+
*
|
|
16
|
+
* PKCE (Proof Key for Code Exchange) is **required by default** for all
|
|
17
|
+
* clients, including confidential ones, per OAuth 2.1. Set `requirePKCE: false`
|
|
18
|
+
* only to interoperate with legacy clients that cannot send a `code_challenge`.
|
|
19
|
+
*
|
|
20
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
|
|
21
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636
|
|
22
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-2.1.1
|
|
23
|
+
*/
|
|
24
|
+
export class AuthorizationCodeGrant extends AbstractGrant {
|
|
25
|
+
/** The OAuth2 grant type: `authorization_code`. */
|
|
26
|
+
grantType = "authorization_code";
|
|
27
|
+
/** The PKCE challenge methods this grant accepts. Defaults to `S256` only. */
|
|
28
|
+
challengeMethods;
|
|
29
|
+
/**
|
|
30
|
+
* Whether PKCE is required for all authorization requests.
|
|
31
|
+
* @see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-7.5.2
|
|
32
|
+
*/
|
|
33
|
+
requirePKCE;
|
|
34
|
+
/**
|
|
35
|
+
* Whether a client presenting a `code_verifier` must still authenticate with
|
|
36
|
+
* its client credentials. Defaults to `true`.
|
|
37
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
|
|
38
|
+
*/
|
|
39
|
+
requireClientAuthentication;
|
|
40
|
+
/** Creates the grant from its {@linkcode AuthorizationCodeGrantOptions}. */
|
|
41
|
+
constructor(options) {
|
|
42
|
+
super({
|
|
43
|
+
...options,
|
|
44
|
+
allowRefreshToken: options.allowRefreshToken ?? true,
|
|
45
|
+
});
|
|
46
|
+
this.challengeMethods = options.challengeMethods ?? defaultChallengeMethods;
|
|
47
|
+
this.requirePKCE = options.requirePKCE ?? true;
|
|
48
|
+
this.requireClientAuthentication = options.requireClientAuthentication ??
|
|
49
|
+
true;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extracts client credentials from a request, carrying the PKCE
|
|
53
|
+
* `code_verifier` alongside them when the client sent one.
|
|
54
|
+
*
|
|
55
|
+
* When {@linkcode requireClientAuthentication} is explicitly `false`, a
|
|
56
|
+
* `code_verifier` drops the `client_secret` from the returned credentials,
|
|
57
|
+
* so the client is looked up rather than authenticated.
|
|
58
|
+
*/
|
|
59
|
+
getClientCredentials(request, body) {
|
|
60
|
+
const credentials = super.getClientCredentials(request, body);
|
|
61
|
+
const codeVerifier = body.get("code_verifier");
|
|
62
|
+
if (typeof codeVerifier === "string") {
|
|
63
|
+
return this.requireClientAuthentication
|
|
64
|
+
? { ...credentials, codeVerifier }
|
|
65
|
+
: { ...credentials, codeVerifier, clientSecret: undefined };
|
|
66
|
+
}
|
|
67
|
+
return credentials;
|
|
68
|
+
}
|
|
69
|
+
/** Gets a client by ID without authentication. */
|
|
70
|
+
async getClient(clientId, clientService) {
|
|
71
|
+
const client = await clientService.get(clientId);
|
|
72
|
+
if (!client) {
|
|
73
|
+
throw new InvalidClientError("client not found");
|
|
74
|
+
}
|
|
75
|
+
return client;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Authenticates a client from a request.
|
|
79
|
+
*
|
|
80
|
+
* Confidential clients authenticate with their registered secret by default,
|
|
81
|
+
* including when they send a `code_verifier`. Setting
|
|
82
|
+
* {@linkcode requireClientAuthentication} to `false` makes a request carrying
|
|
83
|
+
* a `code_verifier` look up the client by id without authenticating it.
|
|
84
|
+
*
|
|
85
|
+
* @throws {InvalidClientError} When the client is unknown or its credentials do not authenticate it.
|
|
86
|
+
*/
|
|
87
|
+
async getAuthenticatedClient(request, body) {
|
|
88
|
+
const { clientService } = await this.resolveServices(request);
|
|
89
|
+
const { clientId, clientSecret, codeVerifier } = this.getClientCredentials(request, body);
|
|
90
|
+
if (codeVerifier && !this.requireClientAuthentication) {
|
|
91
|
+
const client = await clientService.get(clientId);
|
|
92
|
+
if (!client) {
|
|
93
|
+
throw new InvalidClientError("client authentication failed");
|
|
94
|
+
}
|
|
95
|
+
return client;
|
|
96
|
+
}
|
|
97
|
+
return await authenticateClientCredentials({ clientId, clientSecret }, clientService);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Resolves an allowed challenge method by name, defaulting to `S256`.
|
|
101
|
+
*
|
|
102
|
+
* Only names {@linkcode challengeMethods} holds as its own callable
|
|
103
|
+
* properties resolve, so a client cannot reach an inherited
|
|
104
|
+
* `Object.prototype` member such as `toString` by naming it as its
|
|
105
|
+
* `code_challenge_method`.
|
|
106
|
+
*/
|
|
107
|
+
getChallengeMethod(challengeMethod) {
|
|
108
|
+
return getChallengeMethod(this.challengeMethods, challengeMethod);
|
|
109
|
+
}
|
|
110
|
+
/** Validates that the challenge method is allowed. */
|
|
111
|
+
validateChallengeMethod(challengeMethod) {
|
|
112
|
+
return !!this.getChallengeMethod(challengeMethod);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Verifies that a code verifier matches the authorization code's challenge.
|
|
116
|
+
* https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
|
|
117
|
+
*/
|
|
118
|
+
async verifyCode(code, verifier) {
|
|
119
|
+
if (!code.challenge)
|
|
120
|
+
return false;
|
|
121
|
+
const challengeMethod = this.getChallengeMethod(code.challengeMethod);
|
|
122
|
+
if (challengeMethod) {
|
|
123
|
+
const challenge = await challengeMethod(verifier);
|
|
124
|
+
if (challenge !== code.challenge)
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
throw new ServerError("code_challenge_method not implemented");
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generates and saves an authorization code. Resolves its own
|
|
134
|
+
* `authorizationCodeService` from the request.
|
|
135
|
+
*/
|
|
136
|
+
async generateAuthorizationCode(options, request) {
|
|
137
|
+
const authenticationContext = snapshotAuthenticationContext(options.authenticationContext);
|
|
138
|
+
const { authorizationCodeService } = await this.resolveServices(request);
|
|
139
|
+
const { client, user, scope, redirectUri, challenge, challengeMethod, nonce, } = options;
|
|
140
|
+
const authorizationCode = {
|
|
141
|
+
code: await authorizationCodeService.generateCode(client, user, scope),
|
|
142
|
+
expiresAt: await authorizationCodeService.expiresAt(client, user, scope),
|
|
143
|
+
client,
|
|
144
|
+
user,
|
|
145
|
+
};
|
|
146
|
+
if (scope)
|
|
147
|
+
authorizationCode.scope = scope;
|
|
148
|
+
if (redirectUri)
|
|
149
|
+
authorizationCode.redirectUri = redirectUri;
|
|
150
|
+
if (challenge)
|
|
151
|
+
authorizationCode.challenge = challenge;
|
|
152
|
+
if (challengeMethod)
|
|
153
|
+
authorizationCode.challengeMethod = challengeMethod;
|
|
154
|
+
if (nonce)
|
|
155
|
+
authorizationCode.nonce = nonce;
|
|
156
|
+
if (authenticationContext) {
|
|
157
|
+
authorizationCode.authenticationContext = authenticationContext;
|
|
158
|
+
}
|
|
159
|
+
return await authorizationCodeService.save(authorizationCode);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Handles a token request for the authorization code grant.
|
|
163
|
+
*
|
|
164
|
+
* Exchanges an authorization code for an access token. If PKCE was used
|
|
165
|
+
* during authorization, the code_verifier must be provided.
|
|
166
|
+
*
|
|
167
|
+
* The code is claimed by revoking it: only the caller whose revoke wins
|
|
168
|
+
* receives a token, so concurrent exchanges of one code cannot both succeed.
|
|
169
|
+
*
|
|
170
|
+
* @throws {InvalidRequestError} When `code` is missing, a required `code_verifier` is absent, or the verifier is malformed.
|
|
171
|
+
* @throws {InvalidGrantError} When the code is unknown, expired, already claimed, or its verifier or `redirect_uri` does not match.
|
|
172
|
+
* @throws {InvalidClientError} When the code was issued to another client.
|
|
173
|
+
* @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
|
|
174
|
+
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.5
|
|
175
|
+
*/
|
|
176
|
+
async token(request, client, body) {
|
|
177
|
+
const { authorizationCodeService, tokenService } = await this
|
|
178
|
+
.resolveServices(request);
|
|
179
|
+
const code = body.get("code");
|
|
180
|
+
if (typeof code !== "string") {
|
|
181
|
+
throw new InvalidRequestError("code parameter required");
|
|
182
|
+
}
|
|
183
|
+
// RFC 6819 §4.4.1.1: code replay revokes already-issued tokens and rejects.
|
|
184
|
+
if (await tokenService.revokeCode(code)) {
|
|
185
|
+
throw new InvalidGrantError("code already used");
|
|
186
|
+
}
|
|
187
|
+
const authorizationCode = await authorizationCodeService.get(code);
|
|
188
|
+
if (!authorizationCode || authorizationCode.expiresAt < new Date()) {
|
|
189
|
+
throw new InvalidGrantError("invalid code");
|
|
190
|
+
}
|
|
191
|
+
if (!await authorizationCodeService.revoke(authorizationCode)) {
|
|
192
|
+
throw new InvalidGrantError("code already used");
|
|
193
|
+
}
|
|
194
|
+
const codeVerifier = body.get("code_verifier");
|
|
195
|
+
if (typeof codeVerifier === "string") {
|
|
196
|
+
if (!validateCodeVerifier(codeVerifier)) {
|
|
197
|
+
throw new InvalidRequestError("code_verifier must be 43-128 characters using [A-Z] / [a-z] / [0-9] / - / . / _ / ~");
|
|
198
|
+
}
|
|
199
|
+
if (!await this.verifyCode(authorizationCode, codeVerifier)) {
|
|
200
|
+
throw new InvalidGrantError("code_verifier verification failed");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else if (authorizationCode.challenge) {
|
|
204
|
+
throw new InvalidRequestError("code_verifier required");
|
|
205
|
+
}
|
|
206
|
+
else if (this.requirePKCE) {
|
|
207
|
+
throw new InvalidRequestError("PKCE is required for this grant");
|
|
208
|
+
}
|
|
209
|
+
const { client: codeClient, user, scope, redirectUri: expectedRedirectUri, } = authorizationCode;
|
|
210
|
+
if (client.id.toString() !== codeClient.id.toString()) {
|
|
211
|
+
throw new InvalidClientError("code was issued to another client");
|
|
212
|
+
}
|
|
213
|
+
const redirectUri = body.get("redirect_uri");
|
|
214
|
+
if (expectedRedirectUri) {
|
|
215
|
+
if (typeof redirectUri !== "string") {
|
|
216
|
+
throw new InvalidGrantError("redirect_uri parameter required");
|
|
217
|
+
}
|
|
218
|
+
else if (redirectUri !== expectedRedirectUri) {
|
|
219
|
+
throw new InvalidGrantError("incorrect redirect_uri");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else if (redirectUri) {
|
|
223
|
+
throw new InvalidGrantError("did not expect redirect_uri parameter");
|
|
224
|
+
}
|
|
225
|
+
const token = await this.generateToken(client, user, scope, tokenService, authorizationCode.authenticationContext);
|
|
226
|
+
token.code = code;
|
|
227
|
+
const saved = await tokenService.save(token);
|
|
228
|
+
if (authorizationCode.nonce)
|
|
229
|
+
saved.nonce = authorizationCode.nonce;
|
|
230
|
+
return saved;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client Credentials grant type implementation.
|
|
3
|
+
* RFC 6749 Section 4.4.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { ClientInterface } from "../../models/client.js";
|
|
7
|
+
import type { Token } from "../../models/token.js";
|
|
8
|
+
import type { AbstractScope, BasicScope } from "../../models/scope.js";
|
|
9
|
+
import { AbstractGrant, type GrantOptions, type GrantServices } from "./grant.js";
|
|
10
|
+
/** Services required by the client credentials grant. */
|
|
11
|
+
export type ClientCredentialsGrantServices<Client extends ClientInterface, User, S extends AbstractScope> = GrantServices<Client, User, S>;
|
|
12
|
+
/**
|
|
13
|
+
* Options for configuring the client credentials grant. This grant never issues
|
|
14
|
+
* a refresh token (RFC 6749 §4.4.3), so `allowRefreshToken` is not offered.
|
|
15
|
+
*/
|
|
16
|
+
export type ClientCredentialsGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> = Omit<GrantOptions<Client, User, S, ClientCredentialsGrantServices<Client, User, S>>, "allowRefreshToken">;
|
|
17
|
+
/**
|
|
18
|
+
* The client credentials grant type.
|
|
19
|
+
* https://datatracker.ietf.org/doc/html/rfc6749.html#section-4.4
|
|
20
|
+
*
|
|
21
|
+
* Used for machine-to-machine authentication where the client itself
|
|
22
|
+
* is the resource owner. Does not support refresh tokens.
|
|
23
|
+
*
|
|
24
|
+
* **The token need not carry a user.** RFC 6749 §4.4 has no resource owner, so
|
|
25
|
+
* a `ClientServiceInterface.getUser` that resolves nothing is the conformant
|
|
26
|
+
* case, not an error: the grant issues a token whose {@linkcode Token.user} is
|
|
27
|
+
* unset, and `createJwtAccessTokenGenerator` names the client as its subject
|
|
28
|
+
* (RFC 9068 §2.2). Resolving a human owner instead makes the machine
|
|
29
|
+
* indistinguishable from that person — the _Client Impersonating Resource
|
|
30
|
+
* Owner_ attack of RFC 9700 §4.15 — so map a client to a user only when that
|
|
31
|
+
* user is a principal of its own, such as a per-application service account.
|
|
32
|
+
*/
|
|
33
|
+
export declare class ClientCredentialsGrant<Client extends ClientInterface, User, S extends AbstractScope = BasicScope> extends AbstractGrant<Client, User, S, ClientCredentialsGrantServices<Client, User, S>> {
|
|
34
|
+
/** The OAuth2 grant type: `client_credentials`. */
|
|
35
|
+
readonly grantType = "client_credentials";
|
|
36
|
+
/** Machine flow — no end-user authentication event, so no id_token. */
|
|
37
|
+
readonly issuesIdToken = false;
|
|
38
|
+
/** Creates the grant from its {@linkcode ClientCredentialsGrantOptions}. */
|
|
39
|
+
constructor(options: ClientCredentialsGrantOptions<Client, User, S>);
|
|
40
|
+
/**
|
|
41
|
+
* Handles a token request for the client credentials grant. Issues an access
|
|
42
|
+
* token (never a refresh token) to the client acting as its own resource
|
|
43
|
+
* owner.
|
|
44
|
+
*
|
|
45
|
+
* The token's {@linkcode Token.user} is whatever
|
|
46
|
+
* `ClientServiceInterface.getUser` resolves for the client, and is left
|
|
47
|
+
* unset when it resolves nothing — a user-less machine token, not a failure.
|
|
48
|
+
*
|
|
49
|
+
* @throws {InvalidScopeError} If the requested scope is rejected.
|
|
50
|
+
*/
|
|
51
|
+
token(request: Request, client: Client, body: FormData): Promise<Token<Client, User, S>>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client Credentials grant type implementation.
|
|
3
|
+
* RFC 6749 Section 4.4.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { AbstractGrant, } from "./grant.js";
|
|
7
|
+
/**
|
|
8
|
+
* The client credentials grant type.
|
|
9
|
+
* https://datatracker.ietf.org/doc/html/rfc6749.html#section-4.4
|
|
10
|
+
*
|
|
11
|
+
* Used for machine-to-machine authentication where the client itself
|
|
12
|
+
* is the resource owner. Does not support refresh tokens.
|
|
13
|
+
*
|
|
14
|
+
* **The token need not carry a user.** RFC 6749 §4.4 has no resource owner, so
|
|
15
|
+
* a `ClientServiceInterface.getUser` that resolves nothing is the conformant
|
|
16
|
+
* case, not an error: the grant issues a token whose {@linkcode Token.user} is
|
|
17
|
+
* unset, and `createJwtAccessTokenGenerator` names the client as its subject
|
|
18
|
+
* (RFC 9068 §2.2). Resolving a human owner instead makes the machine
|
|
19
|
+
* indistinguishable from that person — the _Client Impersonating Resource
|
|
20
|
+
* Owner_ attack of RFC 9700 §4.15 — so map a client to a user only when that
|
|
21
|
+
* user is a principal of its own, such as a per-application service account.
|
|
22
|
+
*/
|
|
23
|
+
export class ClientCredentialsGrant extends AbstractGrant {
|
|
24
|
+
/** The OAuth2 grant type: `client_credentials`. */
|
|
25
|
+
grantType = "client_credentials";
|
|
26
|
+
/** Machine flow — no end-user authentication event, so no id_token. */
|
|
27
|
+
issuesIdToken = false;
|
|
28
|
+
/** Creates the grant from its {@linkcode ClientCredentialsGrantOptions}. */
|
|
29
|
+
constructor(options) {
|
|
30
|
+
super({ ...options, allowRefreshToken: false });
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Handles a token request for the client credentials grant. Issues an access
|
|
34
|
+
* token (never a refresh token) to the client acting as its own resource
|
|
35
|
+
* owner.
|
|
36
|
+
*
|
|
37
|
+
* The token's {@linkcode Token.user} is whatever
|
|
38
|
+
* `ClientServiceInterface.getUser` resolves for the client, and is left
|
|
39
|
+
* unset when it resolves nothing — a user-less machine token, not a failure.
|
|
40
|
+
*
|
|
41
|
+
* @throws {InvalidScopeError} If the requested scope is rejected.
|
|
42
|
+
*/
|
|
43
|
+
async token(request, client, body) {
|
|
44
|
+
const { tokenService, clientService } = await this.resolveServices(request);
|
|
45
|
+
const scopeText = body.get("scope");
|
|
46
|
+
let scope = this.parseScope(typeof scopeText === "string" ? scopeText : undefined);
|
|
47
|
+
const user = await clientService.getUser(client);
|
|
48
|
+
const acceptedScope = await this.acceptedScope(client, user, scope, tokenService);
|
|
49
|
+
scope = acceptedScope ?? undefined;
|
|
50
|
+
const token = await this.generateToken(client, user, scope, tokenService);
|
|
51
|
+
return await tokenService.save(token);
|
|
52
|
+
}
|
|
53
|
+
}
|