@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,707 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2 client that talks to the authorization server directly and holds the
|
|
3
|
+
* resulting tokens (RFC 6749 + RFC 7636 PKCE + RFC 7009 revocation +
|
|
4
|
+
* RFC 7662 introspection + RFC 8414 metadata + RFC 8628 device authorization).
|
|
5
|
+
*
|
|
6
|
+
* Works against any RFC-compliant authorization server. Use it for SPAs
|
|
7
|
+
* without a Backend-for-Frontend, CLIs, mobile shells, and server-side apps —
|
|
8
|
+
* anywhere this process is the one that keeps the access and refresh tokens.
|
|
9
|
+
* When a BFF holds them instead, use `BffClient`.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { AuthorizationServerMetadata, DeviceAuthorizationResponse } from "../models/responses.js";
|
|
14
|
+
import type { IntrospectionResponse, TokenResponse } from "../models/token.js";
|
|
15
|
+
import { type BaseLoginOptions, type BaseOptions, type LogoutOptions, type LogoutRedirect, OAuth2ClientBase, type SessionState, type UserInfoClaims } from "./base.js";
|
|
16
|
+
import { type DiscoveryCache } from "./discovery-cache.js";
|
|
17
|
+
import { type AuthRequestStorage, type RefreshTokenStorage, type TokenBundle, type TokenStorage } from "./storage.js";
|
|
18
|
+
/** Endpoints resolved via discovery or provided explicitly by the caller. */
|
|
19
|
+
export interface AuthorizationServerEndpoints {
|
|
20
|
+
/** URL of the authorization endpoint (required for the auth-code flow). */
|
|
21
|
+
authorization?: string;
|
|
22
|
+
/**
|
|
23
|
+
* URL of the token endpoint. Required by every grant; methods that need it
|
|
24
|
+
* throw a clear error if it is missing both here and from the server's
|
|
25
|
+
* discovery metadata.
|
|
26
|
+
*/
|
|
27
|
+
token?: string;
|
|
28
|
+
/** URL of the revocation endpoint (RFC 7009). */
|
|
29
|
+
revocation?: string;
|
|
30
|
+
/** URL of the introspection endpoint (RFC 7662). */
|
|
31
|
+
introspection?: string;
|
|
32
|
+
/** URL of the device authorization endpoint (RFC 8628). */
|
|
33
|
+
deviceAuthorization?: string;
|
|
34
|
+
/** URL of the OIDC userinfo endpoint. */
|
|
35
|
+
userInfo?: string;
|
|
36
|
+
/** URL of the OIDC end-session endpoint (for logout redirect). */
|
|
37
|
+
endSession?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Options accepted by {@link DirectClient}. */
|
|
40
|
+
export interface DirectClientOptions extends BaseOptions {
|
|
41
|
+
/** OAuth2 client identifier. */
|
|
42
|
+
clientId: string;
|
|
43
|
+
/**
|
|
44
|
+
* The client secret, sent via HTTP Basic auth on token-endpoint calls.
|
|
45
|
+
*
|
|
46
|
+
* Supplying one makes this a **confidential** client, which unlocks
|
|
47
|
+
* {@link DirectClient.getClientCredentialsToken} and
|
|
48
|
+
* {@link DirectClient.introspect}. **Omit the property** for a **public**
|
|
49
|
+
* client (a browser or a native app, where a secret cannot be kept) — those
|
|
50
|
+
* require refresh-token rotation with reuse detection on the server side.
|
|
51
|
+
*
|
|
52
|
+
* `clientSecret: undefined` reads as omitted, so a wrapper can forward an
|
|
53
|
+
* optional secret. An **empty string** is a construction error rather than a
|
|
54
|
+
* request for a public client — nobody spells "public" `""`, so it is a
|
|
55
|
+
* variable that resolved to blank. Note that `clientSecret: getEnv("X")`
|
|
56
|
+
* with `X` unset therefore yields a *public* client; require it at the
|
|
57
|
+
* source if the deployment must be confidential.
|
|
58
|
+
*/
|
|
59
|
+
clientSecret?: string;
|
|
60
|
+
/** Registered redirect URI; required for the auth-code flow. */
|
|
61
|
+
redirectUri?: string;
|
|
62
|
+
/** Default scope string to request when none is passed explicitly. */
|
|
63
|
+
scope?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Issuer URL. If provided, endpoints are fetched from
|
|
66
|
+
* `${issuer}/.well-known/oauth-authorization-server` on first use.
|
|
67
|
+
* Mutually exclusive with an explicit `endpoints` record below.
|
|
68
|
+
*/
|
|
69
|
+
issuer?: string;
|
|
70
|
+
/** Explicit endpoint URLs, bypassing discovery. */
|
|
71
|
+
endpoints?: AuthorizationServerEndpoints;
|
|
72
|
+
/**
|
|
73
|
+
* Cache the discovery result is read from and written to, shared with any
|
|
74
|
+
* other client given the same instance. Without one, metadata is cached on
|
|
75
|
+
* this client only — a server that builds a client per request then
|
|
76
|
+
* re-fetches discovery on every request. `MemoryDiscoveryCache` is the
|
|
77
|
+
* ready-made implementation.
|
|
78
|
+
*/
|
|
79
|
+
discoveryCache?: DiscoveryCache;
|
|
80
|
+
/** Access-token storage. Defaults to {@link MemoryTokenStorage}. */
|
|
81
|
+
tokenStorage?: TokenStorage;
|
|
82
|
+
/** Refresh-token storage. Defaults to {@link MemoryRefreshTokenStorage}. */
|
|
83
|
+
refreshTokenStorage?: RefreshTokenStorage;
|
|
84
|
+
/**
|
|
85
|
+
* Auth-request storage.
|
|
86
|
+
*
|
|
87
|
+
* Defaults to {@link SessionStorageAuthRequestStorage} in a browser document
|
|
88
|
+
* and {@link MemoryAuthRequestStorage} everywhere else. The browser default
|
|
89
|
+
* is what makes the authorize redirect work at all: an in-memory record does
|
|
90
|
+
* not survive the navigation away and back, so the callback rejects every
|
|
91
|
+
* `state` as unknown. Servers keep the in-memory default so a per-request
|
|
92
|
+
* client does not write pending logins into one process-wide store.
|
|
93
|
+
*
|
|
94
|
+
* Override it when the record must live somewhere else — a per-request
|
|
95
|
+
* cookie store on a server, or a store scoped to something other than the
|
|
96
|
+
* tab.
|
|
97
|
+
*/
|
|
98
|
+
authRequestStorage?: AuthRequestStorage;
|
|
99
|
+
/**
|
|
100
|
+
* Max age of a pending auth-request (`state`) record before
|
|
101
|
+
* {@link DirectClient.exchangeAuthorizationCode} rejects it as stale, in ms.
|
|
102
|
+
* Bounds the callback replay window and unbounded growth of the in-memory
|
|
103
|
+
* store. Defaults to 10 minutes; set `Infinity` to disable the check.
|
|
104
|
+
*/
|
|
105
|
+
authRequestTtlMs?: number;
|
|
106
|
+
}
|
|
107
|
+
/** Options accepted by {@link DirectClient.login}. */
|
|
108
|
+
export interface LoginOptions extends BaseLoginOptions {
|
|
109
|
+
/** Override the default scope string. */
|
|
110
|
+
scope?: string;
|
|
111
|
+
/** OIDC `prompt` parameter. */
|
|
112
|
+
prompt?: string;
|
|
113
|
+
/** Extra query parameters to append to the authorize URL. */
|
|
114
|
+
extraParams?: Record<string, string>;
|
|
115
|
+
/**
|
|
116
|
+
* Override the origin (scheme + host) of the authorize endpoint and the
|
|
117
|
+
* `redirect_uri` for THIS call, keeping their configured paths. Lets one
|
|
118
|
+
* build-once client serve multiple deployment origins (preview URLs,
|
|
119
|
+
* multi-tenant subdomains) — e.g. the Hono BFF passes the trusted request
|
|
120
|
+
* origin so sign-in works on whichever host the request arrived at. The
|
|
121
|
+
* callback returns to the same origin, so the matching `exchange` call is
|
|
122
|
+
* given the same origin.
|
|
123
|
+
*/
|
|
124
|
+
origin?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Store the pending authorization request here instead of in the client's
|
|
127
|
+
* configured `authRequestStorage`, for THIS call. Lets a server bind the
|
|
128
|
+
* record to the request it belongs to — a per-request cookie store, or a
|
|
129
|
+
* per-tenant table — rather than sharing one process-wide store. The
|
|
130
|
+
* matching `exchangeAuthorizationCode` call MUST be given a storage that
|
|
131
|
+
* can read the record back.
|
|
132
|
+
*/
|
|
133
|
+
authRequestStorage?: AuthRequestStorage;
|
|
134
|
+
/**
|
|
135
|
+
* Override the `redirect_uri` for THIS call (used verbatim, not retargeted
|
|
136
|
+
* by `origin`). Lets a Backend-for-Frontend derive `redirect_uri` from its
|
|
137
|
+
* mounted callback path instead of restating it in the client config. The
|
|
138
|
+
* matching `exchangeAuthorizationCode` call MUST be given the same value
|
|
139
|
+
* (RFC 6749 §4.1.3 — the token-exchange `redirect_uri` must match
|
|
140
|
+
* authorize).
|
|
141
|
+
*/
|
|
142
|
+
redirectUri?: string;
|
|
143
|
+
}
|
|
144
|
+
/** Result of {@link DirectClient.login}. */
|
|
145
|
+
export interface AuthorizationRedirect {
|
|
146
|
+
/** Fully-built authorize URL for the caller to navigate to. */
|
|
147
|
+
url: string;
|
|
148
|
+
/** Opaque `state` value the callback will echo back. */
|
|
149
|
+
state: string;
|
|
150
|
+
}
|
|
151
|
+
/** Result of {@link DirectClient.handleAuthorizationCallback}. */
|
|
152
|
+
export interface AuthorizationCallbackResult {
|
|
153
|
+
/** Token bundle saved on the client. */
|
|
154
|
+
tokens: TokenBundle;
|
|
155
|
+
/** `returnTo` that was recorded when `login` ran. */
|
|
156
|
+
returnTo?: string;
|
|
157
|
+
}
|
|
158
|
+
/** Options accepted by {@link DirectClient.startDeviceAuthorization}. */
|
|
159
|
+
export interface DeviceAuthorizationOptions {
|
|
160
|
+
/** Override the default scope string requested for the device grant. */
|
|
161
|
+
scope?: string;
|
|
162
|
+
}
|
|
163
|
+
/** Options accepted by {@link DirectClient.pollDeviceToken}. */
|
|
164
|
+
export interface PollDeviceTokenOptions {
|
|
165
|
+
/** Minimum seconds between polls. Defaults to the RFC 8628 fallback of 5. */
|
|
166
|
+
interval?: number;
|
|
167
|
+
/** Unix ms at which polling should give up. */
|
|
168
|
+
expiresAt?: number;
|
|
169
|
+
/** Abort signal to cancel polling. */
|
|
170
|
+
signal?: AbortSignal;
|
|
171
|
+
}
|
|
172
|
+
/** Generic token introspection response. */
|
|
173
|
+
export type IntrospectionResult = IntrospectionResponse;
|
|
174
|
+
/** Options accepted by the low-level exchange primitives. */
|
|
175
|
+
export interface ExchangeOptions {
|
|
176
|
+
/** Retarget the configured `redirect_uri` at this origin. */
|
|
177
|
+
origin?: string;
|
|
178
|
+
/** Use this `redirect_uri` verbatim. */
|
|
179
|
+
redirectUri?: string;
|
|
180
|
+
/** Read the pending auth-request record from here. */
|
|
181
|
+
authRequestStorage?: AuthRequestStorage;
|
|
182
|
+
}
|
|
183
|
+
/** Result of the low-level exchange primitives. */
|
|
184
|
+
export interface ExchangeResult {
|
|
185
|
+
/** The token bundle, not persisted to this client's storage. */
|
|
186
|
+
tokens: TokenBundle;
|
|
187
|
+
/** The rotated refresh token, when the server issued one. */
|
|
188
|
+
refreshToken?: string;
|
|
189
|
+
/** `returnTo` recorded at authorize time, for the code exchange. */
|
|
190
|
+
returnTo?: string;
|
|
191
|
+
/** The raw token-endpoint response. */
|
|
192
|
+
raw: TokenResponse;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* OAuth2 client that drives the standard grants against an authorization
|
|
196
|
+
* server and keeps the tokens in its own storage.
|
|
197
|
+
*
|
|
198
|
+
* Public vs confidential is decided by whether `clientSecret` was supplied:
|
|
199
|
+
* with a secret the client authenticates via HTTP Basic (RFC 6749 §2.3.1) and
|
|
200
|
+
* may use `client_credentials` and introspection; without one it sends
|
|
201
|
+
* `client_id` in the body and is limited to the grants a public client may
|
|
202
|
+
* run.
|
|
203
|
+
*
|
|
204
|
+
* **Advanced — multi-session servers.**
|
|
205
|
+
* {@link exchangeAuthorizationCode} and {@link exchangeRefreshToken} are
|
|
206
|
+
* lower-level variants that bypass this client's token storage and event bus.
|
|
207
|
+
* They exist so server-side adapters (e.g. the Hono BFF) can drive many
|
|
208
|
+
* concurrent sessions through a single shared client without their tokens
|
|
209
|
+
* clobbering each other. SPA / mobile / CLI consumers should never reach for
|
|
210
|
+
* them — call {@link handleAuthorizationCallback} and {@link refresh}
|
|
211
|
+
* instead.
|
|
212
|
+
*
|
|
213
|
+
* @example SPA with PKCE against a discovered issuer
|
|
214
|
+
* ```ts
|
|
215
|
+
* import {
|
|
216
|
+
* DirectClient,
|
|
217
|
+
* IndexedDBRefreshTokenStorage,
|
|
218
|
+
* } from "@udibo/oauth2/client";
|
|
219
|
+
*
|
|
220
|
+
* const client = new DirectClient({
|
|
221
|
+
* clientId: "spa",
|
|
222
|
+
* issuer: "https://auth.example.com",
|
|
223
|
+
* redirectUri: "https://app.example.com/callback",
|
|
224
|
+
* scope: "openid profile",
|
|
225
|
+
* refreshTokenStorage: new IndexedDBRefreshTokenStorage({ clientId: "spa" }),
|
|
226
|
+
* });
|
|
227
|
+
*
|
|
228
|
+
* const { url } = await client.login({ returnTo: "/dashboard" });
|
|
229
|
+
* location.assign(url);
|
|
230
|
+
* ```
|
|
231
|
+
*
|
|
232
|
+
* @example Confidential client issuing a machine token
|
|
233
|
+
* ```ts
|
|
234
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
235
|
+
*
|
|
236
|
+
* const client = new DirectClient({
|
|
237
|
+
* clientId: "worker",
|
|
238
|
+
* clientSecret: Deno.env.get("CLIENT_SECRET")!,
|
|
239
|
+
* endpoints: { token: "https://auth.example.com/token" },
|
|
240
|
+
* });
|
|
241
|
+
*
|
|
242
|
+
* const tokens = await client.getClientCredentialsToken({ scope: "jobs:run" });
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
export declare class DirectClient extends OAuth2ClientBase {
|
|
246
|
+
#private;
|
|
247
|
+
/**
|
|
248
|
+
* Builds a client for one OAuth2 client registration.
|
|
249
|
+
*
|
|
250
|
+
* @param options Must carry `clientId`, plus either `issuer` (endpoints come
|
|
251
|
+
* from discovery) or `endpoints` (discovery is skipped). Supply
|
|
252
|
+
* `clientSecret` for a confidential client; **omit the property entirely**
|
|
253
|
+
* for a public one.
|
|
254
|
+
* @throws {TypeError} when both `endpoints` and `issuer` are supplied (they
|
|
255
|
+
* are mutually exclusive), or when `clientSecret` is an empty string.
|
|
256
|
+
* @throws {Error} when a secret is supplied in a document context, where it
|
|
257
|
+
* would ship to every visitor in the bundle.
|
|
258
|
+
*/
|
|
259
|
+
constructor(options: DirectClientOptions);
|
|
260
|
+
/** True when the client was given a secret and authenticates via Basic. */
|
|
261
|
+
get isConfidential(): boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Fetches authorization-server metadata and caches it on the instance.
|
|
264
|
+
*
|
|
265
|
+
* Tries `.well-known/oauth-authorization-server` (RFC 8414) first, then
|
|
266
|
+
* falls back to `.well-known/openid-configuration` (OIDC Discovery) — the
|
|
267
|
+
* two use the same field names, and real IdPs serve one or the other, so
|
|
268
|
+
* this works against any compliant server. **Lazy + cached**: it only runs
|
|
269
|
+
* on the first endpoint use of an `issuer`-configured client (never at
|
|
270
|
+
* construction), so there is no startup cost and no per-request fetch.
|
|
271
|
+
* Concurrent calls share one in-flight request; a failure clears the cache
|
|
272
|
+
* so a later call can retry.
|
|
273
|
+
*
|
|
274
|
+
* **Re-resolution contract.** The document this client holds is never
|
|
275
|
+
* permanent: every endpoint use re-resolves once the copy in hand is past
|
|
276
|
+
* its expiry, so a long-lived client picks up a rotated `jwks_uri` or a
|
|
277
|
+
* moved endpoint without a restart. Without a `discoveryCache` the copy
|
|
278
|
+
* expires {@link DEFAULT_DISCOVERY_TTL_MS} after it was fetched. With one,
|
|
279
|
+
* the cache owns freshness: the copy expires when the cache says its entry
|
|
280
|
+
* does (the `expiresAt` it reports), so the client reads the cache once
|
|
281
|
+
* per entry lifetime rather than once per endpoint lookup — a Redis- or
|
|
282
|
+
* file-backed cache costs one round trip per TTL window, not one per call.
|
|
283
|
+
* Either way expiry only schedules the *next* resolve; nothing re-fetches on
|
|
284
|
+
* a timer, and a client that is idle past its expiry makes no requests.
|
|
285
|
+
*
|
|
286
|
+
* A server that builds a client per request should supply a
|
|
287
|
+
* `discoveryCache`, or it re-fetches discovery on every request.
|
|
288
|
+
*
|
|
289
|
+
* @returns The server's metadata document.
|
|
290
|
+
* @throws {Error} when the client was configured with explicit `endpoints`
|
|
291
|
+
* rather than an `issuer` — there is nothing to discover.
|
|
292
|
+
* @throws {ServerError} when both well-known paths fail.
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```ts
|
|
296
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
297
|
+
*
|
|
298
|
+
* const client = new DirectClient({
|
|
299
|
+
* clientId: "cli",
|
|
300
|
+
* issuer: "https://auth.example.com",
|
|
301
|
+
* });
|
|
302
|
+
* const meta = await client.discover();
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
discover(): Promise<AuthorizationServerMetadata>;
|
|
306
|
+
/** Last-seen authorization server metadata, if discovery has run. */
|
|
307
|
+
get metadata(): AuthorizationServerMetadata | undefined;
|
|
308
|
+
/**
|
|
309
|
+
* The configured authorization endpoint URL, if known **synchronously** —
|
|
310
|
+
* set explicitly via `endpoints.authorization`, or learned from a previous
|
|
311
|
+
* {@link discover}. `undefined` for an issuer-only client that has not
|
|
312
|
+
* discovered yet.
|
|
313
|
+
*
|
|
314
|
+
* Exposed so a Backend-for-Frontend can recognize an in-flight authorize URL
|
|
315
|
+
* (e.g. to decide whether to *resume* it or *start* a fresh login) without
|
|
316
|
+
* re-running discovery. See `HonoBff.loginContinuation`.
|
|
317
|
+
*/
|
|
318
|
+
get authorizationEndpoint(): string | undefined;
|
|
319
|
+
/**
|
|
320
|
+
* The configured OIDC end-session endpoint URL, if known **synchronously** —
|
|
321
|
+
* set explicitly via `endpoints.endSession`, or learned from a previous
|
|
322
|
+
* {@link discover}. `undefined` when the server advertises none.
|
|
323
|
+
*
|
|
324
|
+
* Exposed so a Backend-for-Frontend can drive RP-Initiated Logout (redirect
|
|
325
|
+
* to the OP's end-session endpoint) without re-running discovery. See
|
|
326
|
+
* `HonoBffOptions.rpInitiatedLogout`.
|
|
327
|
+
*/
|
|
328
|
+
get endSessionEndpoint(): string | undefined;
|
|
329
|
+
/**
|
|
330
|
+
* This client's `client_id`.
|
|
331
|
+
*
|
|
332
|
+
* Exposed for the requests that identify the client without authenticating
|
|
333
|
+
* it — RP-Initiated Logout's `client_id`, which is how the OP knows whose
|
|
334
|
+
* registered `post_logout_redirect_uris` to check when no `id_token_hint` is
|
|
335
|
+
* available. It is a public identifier, never a credential.
|
|
336
|
+
*/
|
|
337
|
+
get clientId(): string;
|
|
338
|
+
/**
|
|
339
|
+
* Builds the PKCE-protected authorize URL and persists the verifier under
|
|
340
|
+
* the returned `state`. Navigate the browser to `url`; the callback route
|
|
341
|
+
* then calls {@link handleAuthorizationCallback}. Non-browser callers can
|
|
342
|
+
* use the URL for out-of-band flows.
|
|
343
|
+
*
|
|
344
|
+
* @param options `returnTo` is stored with the pending request and handed
|
|
345
|
+
* back by the callback. The remaining fields let a server retarget the
|
|
346
|
+
* authorize URL, override `redirect_uri`, or bind the pending request to a
|
|
347
|
+
* per-request store.
|
|
348
|
+
* @returns The authorize URL and the `state` the callback will echo back.
|
|
349
|
+
* @throws {Error} when no authorization endpoint is configured or
|
|
350
|
+
* discoverable.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
355
|
+
*
|
|
356
|
+
* const client = new DirectClient({
|
|
357
|
+
* clientId: "spa",
|
|
358
|
+
* endpoints: { authorization: "https://auth.example.com/authorize" },
|
|
359
|
+
* redirectUri: "https://app.example.com/callback",
|
|
360
|
+
* });
|
|
361
|
+
* const { url, state } = await client.login({ returnTo: "/dashboard" });
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
login(options?: LoginOptions): Promise<AuthorizationRedirect>;
|
|
365
|
+
/**
|
|
366
|
+
* Completes the auth-code flow: reads `code` + `state` from the callback
|
|
367
|
+
* URL, exchanges the code at the token endpoint, persists the token bundle,
|
|
368
|
+
* and emits `authenticated`.
|
|
369
|
+
*
|
|
370
|
+
* Idempotent on duplicate calls with the same `code` — React strict-mode
|
|
371
|
+
* double-mounts would otherwise double-redeem a single-use code and fail the
|
|
372
|
+
* second call, so the first in-flight promise is returned again.
|
|
373
|
+
*
|
|
374
|
+
* @param input The callback URL, its query string, or the parsed params.
|
|
375
|
+
* @returns The persisted tokens and the `returnTo` recorded at login.
|
|
376
|
+
* @throws {OAuth2Error} when the callback carries `?error=…`, or the token
|
|
377
|
+
* exchange fails.
|
|
378
|
+
* @throws {TypeError} when `code` or `state` is missing.
|
|
379
|
+
* @throws {InvalidGrantError} when `state` is unknown or expired.
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```ts
|
|
383
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
384
|
+
*
|
|
385
|
+
* declare const client: DirectClient;
|
|
386
|
+
* const { returnTo } = await client.handleAuthorizationCallback(
|
|
387
|
+
* location.href,
|
|
388
|
+
* );
|
|
389
|
+
* location.replace(returnTo ?? "/");
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
handleAuthorizationCallback(input: URL | URLSearchParams | string): Promise<AuthorizationCallbackResult>;
|
|
393
|
+
/**
|
|
394
|
+
* **Advanced (BFF / multi-session servers).** SPA, mobile, and CLI consumers
|
|
395
|
+
* should call {@link handleAuthorizationCallback} instead — that variant
|
|
396
|
+
* persists the token bundle to this client's storage and emits the
|
|
397
|
+
* `authenticated` event the React adapter listens for.
|
|
398
|
+
*
|
|
399
|
+
* Exchanges an authorization code for tokens **without persisting them**.
|
|
400
|
+
* Used by server-side adapters that manage their own per-session storage
|
|
401
|
+
* (e.g. the Hono BFF, which keeps tokens in a `SessionStore` keyed by an
|
|
402
|
+
* HttpOnly cookie). Calling this from a single-session client splits the
|
|
403
|
+
* token state across this client's storage and the caller's external store,
|
|
404
|
+
* so refresh, revocation, and logout all stop working as expected.
|
|
405
|
+
*
|
|
406
|
+
* The verifier and `returnTo` are looked up under `state`; on success the
|
|
407
|
+
* record is consumed. Emits nothing — the caller owns session semantics.
|
|
408
|
+
*
|
|
409
|
+
* @param code The authorization code from the callback.
|
|
410
|
+
* @param state The `state` the callback echoed back.
|
|
411
|
+
* @param options Per-call `redirect_uri` and auth-request storage overrides,
|
|
412
|
+
* which must match what {@link login} was given.
|
|
413
|
+
* @returns The tokens, the rotated refresh token, and the recorded
|
|
414
|
+
* `returnTo`, plus the raw response.
|
|
415
|
+
* @throws {InvalidGrantError} when `state` is unknown or older than
|
|
416
|
+
* `authRequestTtlMs`.
|
|
417
|
+
*/
|
|
418
|
+
exchangeAuthorizationCode(code: string, state: string, options?: ExchangeOptions): Promise<ExchangeResult>;
|
|
419
|
+
/**
|
|
420
|
+
* **Advanced (BFF / multi-session servers).** SPA, mobile, and CLI consumers
|
|
421
|
+
* should call {@link refresh} instead — that variant persists the rotated
|
|
422
|
+
* bundle, dedupes concurrent calls, and emits `token_refreshed`.
|
|
423
|
+
*
|
|
424
|
+
* Exchanges a refresh token for a fresh bundle **without persisting** it.
|
|
425
|
+
* Used by server-side adapters that manage their own per-session storage
|
|
426
|
+
* (the Hono BFF's `attachToken` middleware calls this when refreshing on
|
|
427
|
+
* expiry). Mixing this with this client's own token storage in a
|
|
428
|
+
* single-session deployment leaves the two out of sync.
|
|
429
|
+
*
|
|
430
|
+
* Does not share the single-flight promise with {@link refresh} and emits
|
|
431
|
+
* nothing — the caller owns concurrency control across its sessions.
|
|
432
|
+
*
|
|
433
|
+
* @param refreshToken The refresh token to redeem.
|
|
434
|
+
* @returns The new tokens, the rotated refresh token, and the raw response.
|
|
435
|
+
* @throws {OAuth2Error} when the token endpoint rejects the grant.
|
|
436
|
+
*/
|
|
437
|
+
exchangeRefreshToken(refreshToken: string): Promise<ExchangeResult>;
|
|
438
|
+
/**
|
|
439
|
+
* Issues a token via the `client_credentials` grant, persists it, and emits
|
|
440
|
+
* `authenticated`. For machine-to-machine calls where there is no user.
|
|
441
|
+
*
|
|
442
|
+
* @param options `scope` overrides the client's default scope string.
|
|
443
|
+
* @returns The persisted token bundle.
|
|
444
|
+
* @throws {Error} when the client has no `clientSecret` — RFC 6749 §4.4
|
|
445
|
+
* restricts this grant to confidential clients.
|
|
446
|
+
* @throws {OAuth2Error} when the token endpoint rejects the grant.
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```ts
|
|
450
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
451
|
+
*
|
|
452
|
+
* declare const client: DirectClient;
|
|
453
|
+
* const { accessToken } = await client.getClientCredentialsToken({
|
|
454
|
+
* scope: "jobs:run",
|
|
455
|
+
* });
|
|
456
|
+
* ```
|
|
457
|
+
*/
|
|
458
|
+
getClientCredentialsToken(options?: {
|
|
459
|
+
scope?: string;
|
|
460
|
+
}): Promise<TokenBundle>;
|
|
461
|
+
/**
|
|
462
|
+
* Refreshes the access token using the stored refresh token, persists the
|
|
463
|
+
* result, and emits `token_refreshed`. Single-flight: concurrent callers
|
|
464
|
+
* share one in-flight promise.
|
|
465
|
+
*
|
|
466
|
+
* If the refresh fails with `invalid_grant` (the token was revoked or, under
|
|
467
|
+
* rotation, already consumed), all local token state is cleared and
|
|
468
|
+
* `logged_out` is emitted so the framework adapter can redirect to login.
|
|
469
|
+
*
|
|
470
|
+
* Single-flight has one consequence worth knowing: a call that **joins** a
|
|
471
|
+
* refresh already started by {@link renewSession} inherits that call's quiet
|
|
472
|
+
* policy, so it will not emit `error` on a transport failure even though it
|
|
473
|
+
* still rejects. The rejection, not the event, is the contract for a caller
|
|
474
|
+
* that asked directly.
|
|
475
|
+
*
|
|
476
|
+
* A refresh-token storage that fails to read refuses the refresh: the
|
|
477
|
+
* rejection carries the storage's own error, `error` is emitted, and the
|
|
478
|
+
* stored session is left alone — a broken store is not evidence the grant
|
|
479
|
+
* died.
|
|
480
|
+
*
|
|
481
|
+
* @returns The new access token.
|
|
482
|
+
* @throws {InvalidGrantError} when no refresh token is stored, or the stored
|
|
483
|
+
* one is dead.
|
|
484
|
+
* @throws {Error} whatever the configured `RefreshTokenStorage` threw when
|
|
485
|
+
* the read itself failed.
|
|
486
|
+
*/
|
|
487
|
+
refresh(): Promise<string>;
|
|
488
|
+
/**
|
|
489
|
+
* Initiates the device authorization flow (RFC 8628 §3.1) for an input-
|
|
490
|
+
* constrained device. Show the returned `user_code` and
|
|
491
|
+
* `verification_uri`, then hand `device_code` to {@link pollDeviceToken}.
|
|
492
|
+
*
|
|
493
|
+
* @param options `scope` overrides the client's default scope string.
|
|
494
|
+
* @returns The server's device authorization response.
|
|
495
|
+
* @throws {Error} when no device authorization endpoint is configured or
|
|
496
|
+
* discoverable.
|
|
497
|
+
* @throws {OAuth2Error} when the server rejects the request.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
502
|
+
*
|
|
503
|
+
* declare const client: DirectClient;
|
|
504
|
+
* const grant = await client.startDeviceAuthorization();
|
|
505
|
+
* console.log(`go to ${grant.verification_uri}, enter ${grant.user_code}`);
|
|
506
|
+
* const tokens = await client.pollDeviceToken(grant.device_code, {
|
|
507
|
+
* interval: grant.interval,
|
|
508
|
+
* expiresAt: Date.now() + grant.expires_in * 1000,
|
|
509
|
+
* });
|
|
510
|
+
* ```
|
|
511
|
+
*/
|
|
512
|
+
startDeviceAuthorization(options?: DeviceAuthorizationOptions): Promise<DeviceAuthorizationResponse>;
|
|
513
|
+
/**
|
|
514
|
+
* Polls the token endpoint for a device-code exchange (RFC 8628 §3.4),
|
|
515
|
+
* persists the tokens, and emits `authenticated`. Handles
|
|
516
|
+
* `authorization_pending` (keep polling) and `slow_down` (add 5s to the
|
|
517
|
+
* interval) automatically.
|
|
518
|
+
*
|
|
519
|
+
* @param deviceCode The `device_code` from
|
|
520
|
+
* {@link startDeviceAuthorization}.
|
|
521
|
+
* @param options Pass `interval` and `expiresAt` from the device
|
|
522
|
+
* authorization response so polling respects the server's advertised
|
|
523
|
+
* minimum interval and deadline, and `signal` to cancel.
|
|
524
|
+
* @returns The persisted token bundle.
|
|
525
|
+
* @throws {AccessDeniedError} when `signal` aborts, or the user declines.
|
|
526
|
+
* @throws {InvalidGrantError} when the device code expires before approval.
|
|
527
|
+
* @throws {OAuth2Error} on any other terminal error from the server.
|
|
528
|
+
*/
|
|
529
|
+
pollDeviceToken(deviceCode: string, options?: PollDeviceTokenOptions): Promise<TokenBundle>;
|
|
530
|
+
/**
|
|
531
|
+
* Calls the introspection endpoint (RFC 7662) to ask the server whether a
|
|
532
|
+
* token is still active and what it carries.
|
|
533
|
+
*
|
|
534
|
+
* @param token The token to introspect.
|
|
535
|
+
* @param options `tokenTypeHint` tells the server which kind to look up
|
|
536
|
+
* first.
|
|
537
|
+
* @returns The introspection response; check `active`.
|
|
538
|
+
* @throws {Error} when the client has no `clientSecret` — RFC 7662 requires
|
|
539
|
+
* an authenticated caller.
|
|
540
|
+
* @throws {OAuth2Error} when the endpoint rejects the request.
|
|
541
|
+
*/
|
|
542
|
+
introspect(token: string, options?: {
|
|
543
|
+
tokenTypeHint?: "access_token" | "refresh_token";
|
|
544
|
+
}): Promise<IntrospectionResult>;
|
|
545
|
+
/**
|
|
546
|
+
* Calls the revocation endpoint (RFC 7009) to kill a token server-side.
|
|
547
|
+
* Does not touch this client's storage — {@link logout} does both.
|
|
548
|
+
*
|
|
549
|
+
* @param token The token to revoke.
|
|
550
|
+
* @param options `tokenTypeHint` tells the server which kind to look up
|
|
551
|
+
* first.
|
|
552
|
+
* @throws {Error} when no revocation endpoint is configured or discoverable.
|
|
553
|
+
* @throws {OAuth2Error} when the endpoint rejects the request.
|
|
554
|
+
*/
|
|
555
|
+
revoke(token: string, options?: {
|
|
556
|
+
tokenTypeHint?: "access_token" | "refresh_token";
|
|
557
|
+
}): Promise<void>;
|
|
558
|
+
/**
|
|
559
|
+
* Fetches the OIDC userinfo endpoint with the current access token,
|
|
560
|
+
* refreshing it first if it is close to expiry.
|
|
561
|
+
*
|
|
562
|
+
* @returns The claims the server returns.
|
|
563
|
+
* @throws {Error} when no userinfo endpoint is configured or discoverable.
|
|
564
|
+
* @throws {AccessDeniedError} when no token is stored and none can be
|
|
565
|
+
* refreshed.
|
|
566
|
+
*/
|
|
567
|
+
getUserInfo(): Promise<UserInfoClaims>;
|
|
568
|
+
/**
|
|
569
|
+
* Base64url-decodes an OIDC `id_token` payload and returns the claims.
|
|
570
|
+
*
|
|
571
|
+
* **Does not verify the signature** — the token arrives from the token
|
|
572
|
+
* endpoint over TLS, so verification would only defend against a compromised
|
|
573
|
+
* TLS chain and is not a normal client concern. If you need verification
|
|
574
|
+
* (e.g. the token was obtained out-of-band), use a dedicated JWT library
|
|
575
|
+
* with your server's JWKS.
|
|
576
|
+
*
|
|
577
|
+
* @param idToken A compact-serialized JWS.
|
|
578
|
+
* @returns The decoded payload claims.
|
|
579
|
+
* @throws {TypeError | RangeError | SyntaxError} on a token that isn't a
|
|
580
|
+
* decodable JWS with a JSON payload. Callers that surface an authorization
|
|
581
|
+
* server's response to a user should catch, since a broken or hostile
|
|
582
|
+
* server controls this input.
|
|
583
|
+
*/
|
|
584
|
+
decodeIdToken(idToken: string): UserInfoClaims;
|
|
585
|
+
/**
|
|
586
|
+
* Fetches with `Authorization: Bearer <access token>` attached, retrying
|
|
587
|
+
* once after a silent {@link refresh} if the response is a `401` carrying
|
|
588
|
+
* `WWW-Authenticate: … error="invalid_token"`.
|
|
589
|
+
*
|
|
590
|
+
* A `401` without that challenge is returned as-is — it means "not allowed",
|
|
591
|
+
* not "token stale", and re-minting would not help.
|
|
592
|
+
*
|
|
593
|
+
* @param input Request target, as for `fetch`. A `Request` keeps the headers
|
|
594
|
+
* and body it was built with; it is cloned up front so the retry has an
|
|
595
|
+
* unread body.
|
|
596
|
+
* @param init Request options. A caller-set `Authorization` header wins, and
|
|
597
|
+
* `init.headers` override same-named headers on a `Request` input.
|
|
598
|
+
* @returns The response; the original `401` when no new token could be
|
|
599
|
+
* minted. A transport failure on the retry itself rejects rather than
|
|
600
|
+
* masquerading as the original `401`.
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* ```ts
|
|
604
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
605
|
+
*
|
|
606
|
+
* declare const client: DirectClient;
|
|
607
|
+
* const res = await client.fetch("https://api.example.com/items");
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
611
|
+
/**
|
|
612
|
+
* Revokes the stored refresh token (best effort), clears all local token
|
|
613
|
+
* state, emits `logged_out`, and returns the OIDC end-session URL when the
|
|
614
|
+
* server advertises one.
|
|
615
|
+
*
|
|
616
|
+
* Best effort covers the read too: a refresh-token store that cannot be read
|
|
617
|
+
* skips the revocation rather than failing the sign-out, because a client
|
|
618
|
+
* that stays signed in locally because its store broke is the worse outcome.
|
|
619
|
+
*
|
|
620
|
+
* @param options `returnTo` becomes `post_logout_redirect_uri` on the
|
|
621
|
+
* end-session URL.
|
|
622
|
+
* @returns `{ url }` when the browser should visit the OP's end-session
|
|
623
|
+
* endpoint to drop the upstream SSO session, `{}` when sign-out completed
|
|
624
|
+
* locally.
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```ts
|
|
628
|
+
* import { DirectClient } from "@udibo/oauth2/client";
|
|
629
|
+
*
|
|
630
|
+
* declare const client: DirectClient;
|
|
631
|
+
* const { url } = await client.logout({ returnTo: "https://app.test/" });
|
|
632
|
+
* if (url) location.assign(url);
|
|
633
|
+
* ```
|
|
634
|
+
*/
|
|
635
|
+
logout(options?: LogoutOptions): Promise<LogoutRedirect>;
|
|
636
|
+
/**
|
|
637
|
+
* The signed-in user's claims: the decoded `id_token` when one was issued,
|
|
638
|
+
* otherwise the userinfo endpoint when one is configured, otherwise `null`.
|
|
639
|
+
*
|
|
640
|
+
* @returns The claims, or `null` when no token is stored or neither source
|
|
641
|
+
* is available.
|
|
642
|
+
*/
|
|
643
|
+
getUser(): Promise<UserInfoClaims | null>;
|
|
644
|
+
/**
|
|
645
|
+
* The session as this client sees it, derived from stored tokens — no
|
|
646
|
+
* network call unless the claims come from a userinfo endpoint.
|
|
647
|
+
*
|
|
648
|
+
* `isAuthenticated` requires a usable access token: a stored bundle whose
|
|
649
|
+
* `accessToken` is non-empty and not past its expiry. A holder of a valid
|
|
650
|
+
* token therefore still reads as signed in when the server issues neither an
|
|
651
|
+
* `id_token` nor a userinfo endpoint, while an expired one reads as signed
|
|
652
|
+
* out rather than leaving the UI authenticated over a dead token. An expired
|
|
653
|
+
* bundle that a stored refresh token could still revive also reads as signed
|
|
654
|
+
* in, with `sessionExpiresIn: 0` — call {@link renewSession} to revive it.
|
|
655
|
+
*
|
|
656
|
+
* `logoutUrl` is always `null`: sign-out runs through {@link logout}, which
|
|
657
|
+
* revokes and clears locally rather than sending the browser somewhere.
|
|
658
|
+
*
|
|
659
|
+
* Never rejects — a failure to resolve claims reports a signed-out session
|
|
660
|
+
* and emits `error`, per {@link OAuth2ClientBase.getSession}.
|
|
661
|
+
*
|
|
662
|
+
* @returns The current {@link SessionState}.
|
|
663
|
+
*/
|
|
664
|
+
getSession(): Promise<SessionState>;
|
|
665
|
+
/**
|
|
666
|
+
* Refreshes the access token, then reports the resulting session.
|
|
667
|
+
*
|
|
668
|
+
* Never rejects: a dead grant has already cleared the session and emitted
|
|
669
|
+
* `logged_out`, so the reported state is signed out, while a transient
|
|
670
|
+
* network failure leaves the stored tokens — and the reported state — as
|
|
671
|
+
* they were. Unlike {@link refresh}, nothing on this path emits `error`:
|
|
672
|
+
* it runs on a background timer, and a blip the client will simply retry
|
|
673
|
+
* must not paint a user-visible error over a session that is still perfectly
|
|
674
|
+
* good. A dead grant still emits `logged_out`, because the UI does have to
|
|
675
|
+
* react to that.
|
|
676
|
+
*
|
|
677
|
+
* "Transient" here means only "not `invalid_grant`", so a durable failure —
|
|
678
|
+
* a rotated client secret answering `invalid_client`, or a token endpoint
|
|
679
|
+
* returning `500` indefinitely — is retried silently for as long as the
|
|
680
|
+
* timer runs. The session ends when the access token finally expires and a
|
|
681
|
+
* request 401s, not when renewal starts failing. Subscribe to
|
|
682
|
+
* `token_refreshed` if you need to notice renewals that stopped landing.
|
|
683
|
+
*
|
|
684
|
+
* **A client with no refresh token cannot renew.** That includes the
|
|
685
|
+
* `client_credentials` case, where re-running the grant would be possible in
|
|
686
|
+
* principle: doing it automatically is refused because a confidential client
|
|
687
|
+
* that had signed a *user* in would silently swap that user's session for a
|
|
688
|
+
* machine identity. Re-run {@link getClientCredentialsToken} yourself.
|
|
689
|
+
*
|
|
690
|
+
* @returns The {@link SessionState} after the refresh attempt.
|
|
691
|
+
*/
|
|
692
|
+
renewSession(): Promise<SessionState>;
|
|
693
|
+
/**
|
|
694
|
+
* Returns a usable access token, refreshing first when the stored one is
|
|
695
|
+
* within 30 seconds of expiry.
|
|
696
|
+
*
|
|
697
|
+
* Reach for this when you must attach `Authorization: Bearer <token>` to a
|
|
698
|
+
* request {@link fetch} can't carry (a custom transport, a third-party SDK).
|
|
699
|
+
* For ordinary HTTP calls prefer {@link fetch}, which attaches the token and
|
|
700
|
+
* handles the 401/refresh dance for you.
|
|
701
|
+
*
|
|
702
|
+
* @returns A non-expired access token.
|
|
703
|
+
* @throws {InvalidGrantError} when nothing is stored and no refresh token is
|
|
704
|
+
* available.
|
|
705
|
+
*/
|
|
706
|
+
getAccessToken(): Promise<string>;
|
|
707
|
+
}
|