@udibo/oauth2 0.4.1 → 0.5.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.
Files changed (159) hide show
  1. package/README.md +3 -3
  2. package/SECURITY.md +0 -4
  3. package/esm/adapters/hono/_common.d.ts +10 -13
  4. package/esm/adapters/hono/_common.js +11 -20
  5. package/esm/adapters/hono/authorization-server.d.ts +16 -10
  6. package/esm/adapters/hono/authorization-server.js +13 -8
  7. package/esm/adapters/hono/bff/auth-request-store.d.ts +4 -0
  8. package/esm/adapters/hono/bff/auth-request-store.js +4 -0
  9. package/esm/adapters/hono/bff/bff.d.ts +29 -18
  10. package/esm/adapters/hono/bff/bff.js +15 -11
  11. package/esm/adapters/hono/bff/proxy.js +7 -55
  12. package/esm/adapters/hono/bff/session-store.d.ts +9 -9
  13. package/esm/adapters/hono/bff/session-store.js +3 -2
  14. package/esm/adapters/hono/bff/testing.d.ts +14 -3
  15. package/esm/adapters/hono/bff/testing.js +14 -3
  16. package/esm/adapters/hono/identity.d.ts +5 -3
  17. package/esm/adapters/hono/identity.js +1 -17
  18. package/esm/adapters/hono/resource-server.d.ts +3 -2
  19. package/esm/adapters/hono/resource-server.js +3 -2
  20. package/esm/client/_http.d.ts +4 -7
  21. package/esm/client/_http.js +4 -37
  22. package/esm/client/base.d.ts +3 -6
  23. package/esm/client/bff-client.d.ts +7 -6
  24. package/esm/client/bff-client.js +7 -6
  25. package/esm/client/browser-storage.d.ts +33 -34
  26. package/esm/client/browser-storage.js +47 -32
  27. package/esm/client/check.d.ts +10 -3
  28. package/esm/client/check.js +28 -8
  29. package/esm/client/direct-client.d.ts +49 -29
  30. package/esm/client/direct-client.js +59 -59
  31. package/esm/client/discovery-cache.d.ts +12 -5
  32. package/esm/client/discovery-cache.js +11 -4
  33. package/esm/client/events.d.ts +11 -3
  34. package/esm/client/events.js +1 -1
  35. package/esm/client/mod.d.ts +6 -4
  36. package/esm/client/mod.js +5 -3
  37. package/esm/client/storage.d.ts +53 -23
  38. package/esm/client/storage.js +43 -15
  39. package/esm/errors.d.ts +15 -12
  40. package/esm/errors.js +12 -8
  41. package/esm/identity/delivery.d.ts +7 -7
  42. package/esm/identity/delivery.js +1 -2
  43. package/esm/identity/errors.d.ts +10 -4
  44. package/esm/identity/errors.js +4 -1
  45. package/esm/identity/events.d.ts +21 -5
  46. package/esm/identity/events.js +4 -2
  47. package/esm/identity/external/_shared.d.ts +5 -7
  48. package/esm/identity/external/_shared.js +5 -7
  49. package/esm/identity/external/_token-exchange.d.ts +3 -6
  50. package/esm/identity/external/_token-exchange.js +2 -5
  51. package/esm/identity/external/apple-client-secret.d.ts +13 -8
  52. package/esm/identity/external/apple-client-secret.js +9 -6
  53. package/esm/identity/external/apple.d.ts +17 -10
  54. package/esm/identity/external/apple.js +12 -9
  55. package/esm/identity/external/discord.d.ts +5 -1
  56. package/esm/identity/external/errors.d.ts +6 -2
  57. package/esm/identity/external/errors.js +6 -2
  58. package/esm/identity/external/flow.d.ts +9 -6
  59. package/esm/identity/external/flow.js +6 -5
  60. package/esm/identity/external/github.d.ts +7 -3
  61. package/esm/identity/external/github.js +2 -2
  62. package/esm/identity/external/google.d.ts +5 -1
  63. package/esm/identity/external/oauth2.d.ts +13 -2
  64. package/esm/identity/external/oauth2.js +3 -3
  65. package/esm/identity/external/oidc.d.ts +21 -10
  66. package/esm/identity/external/oidc.js +11 -6
  67. package/esm/identity/external/provider.d.ts +4 -2
  68. package/esm/identity/identifier.d.ts +6 -5
  69. package/esm/identity/identifier.js +6 -5
  70. package/esm/identity/lockout.d.ts +4 -3
  71. package/esm/identity/lockout.js +4 -3
  72. package/esm/identity/mfa/mod.d.ts +2 -1
  73. package/esm/identity/mfa/mod.js +2 -1
  74. package/esm/identity/mfa/recovery-codes.d.ts +2 -1
  75. package/esm/identity/mfa/service.d.ts +29 -11
  76. package/esm/identity/mfa/service.js +35 -11
  77. package/esm/identity/mfa/totp.d.ts +4 -2
  78. package/esm/identity/mfa/totp.js +4 -2
  79. package/esm/identity/migration.d.ts +8 -7
  80. package/esm/identity/migration.js +9 -12
  81. package/esm/identity/mod.d.ts +2 -2
  82. package/esm/identity/mod.js +2 -2
  83. package/esm/identity/otp.d.ts +23 -16
  84. package/esm/identity/otp.js +13 -12
  85. package/esm/identity/password-policy.d.ts +3 -3
  86. package/esm/identity/password-policy.js +3 -3
  87. package/esm/identity/password.d.ts +23 -20
  88. package/esm/identity/password.js +15 -16
  89. package/esm/identity/rate-limit.d.ts +1 -7
  90. package/esm/identity/rate-limit.js +1 -2
  91. package/esm/identity/service.d.ts +76 -44
  92. package/esm/identity/service.js +86 -60
  93. package/esm/identity/session.d.ts +5 -4
  94. package/esm/identity/session.js +5 -4
  95. package/esm/identity/token-flow.d.ts +8 -8
  96. package/esm/identity/token-flow.js +5 -6
  97. package/esm/models/client.d.ts +7 -4
  98. package/esm/models/token.d.ts +2 -3
  99. package/esm/react/callback.d.ts +3 -3
  100. package/esm/react/callback.js +3 -3
  101. package/esm/react/components/class-names.d.ts +3 -6
  102. package/esm/react/components/class-names.js +3 -6
  103. package/esm/react/components/internal.d.ts +7 -17
  104. package/esm/react/components/internal.js +7 -17
  105. package/esm/react/components/mod.d.ts +7 -5
  106. package/esm/react/components/mod.js +7 -5
  107. package/esm/react/components/types.d.ts +8 -6
  108. package/esm/react/context.d.ts +3 -3
  109. package/esm/react/provider.d.ts +3 -2
  110. package/esm/react/provider.js +0 -13
  111. package/esm/react/require-auth.d.ts +0 -3
  112. package/esm/react/require-auth.js +0 -3
  113. package/esm/react/testing.d.ts +3 -2
  114. package/esm/react/testing.js +3 -3
  115. package/esm/react/use-oauth2.d.ts +1 -1
  116. package/esm/server/authorization-server.d.ts +81 -54
  117. package/esm/server/authorization-server.js +76 -100
  118. package/esm/server/grants/authorization-code.d.ts +7 -3
  119. package/esm/server/grants/authorization-code.js +6 -2
  120. package/esm/server/grants/device-authorization.d.ts +1 -1
  121. package/esm/server/grants/grant.d.ts +15 -8
  122. package/esm/server/grants/grant.js +26 -18
  123. package/esm/server/grants/password.d.ts +1 -1
  124. package/esm/server/grants/refresh-token.d.ts +6 -3
  125. package/esm/server/grants/refresh-token.js +4 -3
  126. package/esm/server/introspection-token-reader.d.ts +16 -7
  127. package/esm/server/introspection-token-reader.js +9 -3
  128. package/esm/server/jwks-token-reader.d.ts +3 -3
  129. package/esm/server/jwks-token-reader.js +3 -3
  130. package/esm/server/redirect-uri.d.ts +6 -9
  131. package/esm/server/redirect-uri.js +7 -34
  132. package/esm/server/resource-server.d.ts +5 -3
  133. package/esm/server/resource-server.js +5 -3
  134. package/esm/server/services/client.d.ts +5 -1
  135. package/esm/server/services/device-authorization.d.ts +6 -6
  136. package/esm/server/services/token.d.ts +15 -8
  137. package/esm/server/services/token.js +1 -1
  138. package/esm/server/signing-keys.d.ts +24 -9
  139. package/esm/server/signing-keys.js +20 -9
  140. package/esm/testing/contract/mod.d.ts +3 -3
  141. package/esm/testing/contract/mod.js +3 -3
  142. package/esm/testing/mod.d.ts +4 -2
  143. package/esm/testing/mod.js +4 -2
  144. package/esm/testing/services.d.ts +1 -1
  145. package/esm/testing/services.js +1 -1
  146. package/esm/testing/tenant.d.ts +13 -4
  147. package/esm/utils/_buffer.d.ts +3 -2
  148. package/esm/utils/_buffer.js +3 -2
  149. package/esm/utils/_timing-safe.d.ts +8 -6
  150. package/esm/utils/_timing-safe.js +8 -6
  151. package/esm/utils/basic-auth.d.ts +5 -12
  152. package/esm/utils/basic-auth.js +5 -12
  153. package/esm/utils/crypto.d.ts +2 -1
  154. package/esm/utils/crypto.js +2 -1
  155. package/esm/utils/text.d.ts +2 -9
  156. package/esm/utils/text.js +2 -9
  157. package/esm/utils/url.d.ts +2 -2
  158. package/esm/utils/url.js +0 -2
  159. package/package.json +1 -1
@@ -4,8 +4,9 @@
4
4
  * @module
5
5
  */
6
6
  /**
7
- * Copy to a standalone `ArrayBuffer` to satisfy `BufferSource` on runtimes that
8
- * narrow `Uint8Array<ArrayBufferLike>` away from `ArrayBuffer`.
7
+ * Copies the viewed bytes into a standalone `ArrayBuffer`, for passing a
8
+ * `Uint8Array<ArrayBufferLike>` where Web Crypto's `BufferSource` type does not
9
+ * accept it.
9
10
  */
10
11
  export function toArrayBuffer(bytes) {
11
12
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
@@ -1,13 +1,15 @@
1
1
  /**
2
- * Internal constant-time matching shared by the MFA and BFF code paths.
2
+ * Internal timing-safe matching for secret candidates.
3
3
  *
4
4
  * @module
5
5
  */
6
6
  /**
7
- * Constant-time scan for `target` among `candidates`: every candidate is
8
- * compared (no early exit), so timing reveals neither whether nor where a
9
- * match occurred. Returns the index of the *last* matching candidate, or
10
- * `undefined` when none match — order candidates least- to most-preferred so
11
- * a duplicate resolves to the preferred entry.
7
+ * Compares `target` against every candidate with no early exit, so the scan's
8
+ * duration does not reveal where a match occurred. Each comparison is
9
+ * {@link timingSafeEqualString}, which returns early on a byte-length mismatch,
10
+ * so candidates of a different length than `target` are distinguishable by
11
+ * timing. Returns the index of the *last* matching candidate, or `undefined`
12
+ * when none match — order candidates least- to most-preferred so a duplicate
13
+ * resolves to the preferred entry.
12
14
  */
13
15
  export declare function timingSafeMatchIndex(candidates: string[], target: string): number | undefined;
@@ -1,15 +1,17 @@
1
1
  /**
2
- * Internal constant-time matching shared by the MFA and BFF code paths.
2
+ * Internal timing-safe matching for secret candidates.
3
3
  *
4
4
  * @module
5
5
  */
6
6
  import { timingSafeEqualString } from "./crypto.js";
7
7
  /**
8
- * Constant-time scan for `target` among `candidates`: every candidate is
9
- * compared (no early exit), so timing reveals neither whether nor where a
10
- * match occurred. Returns the index of the *last* matching candidate, or
11
- * `undefined` when none match — order candidates least- to most-preferred so
12
- * a duplicate resolves to the preferred entry.
8
+ * Compares `target` against every candidate with no early exit, so the scan's
9
+ * duration does not reveal where a match occurred. Each comparison is
10
+ * {@link timingSafeEqualString}, which returns early on a byte-length mismatch,
11
+ * so candidates of a different length than `target` are distinguishable by
12
+ * timing. Returns the index of the *last* matching candidate, or `undefined`
13
+ * when none match — order candidates least- to most-preferred so a duplicate
14
+ * resolves to the preferred entry.
13
15
  */
14
16
  export function timingSafeMatchIndex(candidates, target) {
15
17
  let index;
@@ -18,9 +18,8 @@ export interface BasicAuth {
18
18
  * trip. A segment that is not valid percent-encoding is taken verbatim, so a
19
19
  * client that sends such characters un-encoded still authenticates.
20
20
  *
21
- * @param authorization The Authorization header value
22
- * @returns The parsed credentials
23
- * @throws {InvalidClientError} If the header is missing or malformed
21
+ * @throws {InvalidClientError} If the header is missing, not `Basic`, not
22
+ * valid base64, or does not decode to `name:pass` with a non-empty name.
24
23
  * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
25
24
  */
26
25
  export declare function parseBasicAuth(authorization: string | null): BasicAuth;
@@ -33,18 +32,12 @@ export declare function parseBasicAuth(authorization: string | null): BasicAuth;
33
32
  * only of `A-Z`, `a-z`, `0-9`, `-`, `_`, `.` and `*` encode byte-identically to
34
33
  * their raw form; every other character, `~` included, is percent-encoded.
35
34
  *
36
- * @param clientId The client ID
37
- * @param clientSecret The client secret
38
- * @returns The encoded Authorization header value
35
+ * @returns The `Basic …` Authorization header value.
39
36
  * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
40
37
  */
41
38
  export declare function encodeBasicAuth(clientId: string, clientSecret: string): string;
42
39
  /**
43
- * Tries to parse basic authentication credentials from an Authorization header.
44
- * Returns undefined if the header is missing or not Basic auth. Decodes both
45
- * segments the same way {@link parseBasicAuth} does.
46
- *
47
- * @param authorization The Authorization header value
48
- * @returns The parsed credentials or undefined
40
+ * Non-throwing {@link parseBasicAuth}: returns `undefined` wherever that would
41
+ * throw — a missing, non-`Basic`, badly encoded or malformed header.
49
42
  */
50
43
  export declare function tryParseBasicAuth(authorization: string | null): BasicAuth | undefined;
@@ -30,9 +30,8 @@ function formUrlDecode(value) {
30
30
  * trip. A segment that is not valid percent-encoding is taken verbatim, so a
31
31
  * client that sends such characters un-encoded still authenticates.
32
32
  *
33
- * @param authorization The Authorization header value
34
- * @returns The parsed credentials
35
- * @throws {InvalidClientError} If the header is missing or malformed
33
+ * @throws {InvalidClientError} If the header is missing, not `Basic`, not
34
+ * valid base64, or does not decode to `name:pass` with a non-empty name.
36
35
  * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
37
36
  */
38
37
  export function parseBasicAuth(authorization) {
@@ -68,21 +67,15 @@ export function parseBasicAuth(authorization) {
68
67
  * only of `A-Z`, `a-z`, `0-9`, `-`, `_`, `.` and `*` encode byte-identically to
69
68
  * their raw form; every other character, `~` included, is percent-encoded.
70
69
  *
71
- * @param clientId The client ID
72
- * @param clientSecret The client secret
73
- * @returns The encoded Authorization header value
70
+ * @returns The `Basic …` Authorization header value.
74
71
  * @see https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
75
72
  */
76
73
  export function encodeBasicAuth(clientId, clientSecret) {
77
74
  return `Basic ${btoa(`${formUrlEncode(clientId)}:${formUrlEncode(clientSecret)}`)}`;
78
75
  }
79
76
  /**
80
- * Tries to parse basic authentication credentials from an Authorization header.
81
- * Returns undefined if the header is missing or not Basic auth. Decodes both
82
- * segments the same way {@link parseBasicAuth} does.
83
- *
84
- * @param authorization The Authorization header value
85
- * @returns The parsed credentials or undefined
77
+ * Non-throwing {@link parseBasicAuth}: returns `undefined` wherever that would
78
+ * throw — a missing, non-`Basic`, badly encoded or malformed header.
86
79
  */
87
80
  export function tryParseBasicAuth(authorization) {
88
81
  if (!authorization)
@@ -70,7 +70,8 @@ export declare function randomToken(bytes?: number): string;
70
70
  * secret is persisted (e.g. as a lookup key) — use {@link deriveSealKey} so the
71
71
  * sealing key is independent of that stored hash.
72
72
  *
73
- * @throws if the secret has fewer than 16 bytes of entropy (32+ recommended).
73
+ * @throws {Error} if the secret is shorter than 16 bytes. Length is all that is
74
+ * checked; supply 32+ bytes of real entropy.
74
75
  */
75
76
  export declare function deriveAesKey(secret: string | Uint8Array): Promise<CryptoKey>;
76
77
  /**
@@ -88,7 +88,8 @@ export function randomToken(bytes = 32) {
88
88
  * secret is persisted (e.g. as a lookup key) — use {@link deriveSealKey} so the
89
89
  * sealing key is independent of that stored hash.
90
90
  *
91
- * @throws if the secret has fewer than 16 bytes of entropy (32+ recommended).
91
+ * @throws {Error} if the secret is shorter than 16 bytes. Length is all that is
92
+ * checked; supply 32+ bytes of real entropy.
92
93
  */
93
94
  export async function deriveAesKey(secret) {
94
95
  const raw = toBytes(secret);
@@ -1,24 +1,17 @@
1
1
  /**
2
2
  * Bounding and flattening for text a remote server chose.
3
3
  *
4
- * Lives in `utils/` rather than beside either caller because both ladders that
5
- * turn a server's bytes into an error message — the client's `_http.ts` and
6
- * the connectors' `_token-exchange.ts` — must sanitize identically, and the
7
- * client cannot reach up into `identity/external/`.
8
- *
9
4
  * @module
10
5
  */
11
6
  /** Characters of remote-supplied text kept in an error message. */
12
7
  export declare const PROVIDER_TEXT_MAX_LENGTH = 200;
13
8
  /**
14
9
  * Bounds and flattens a remote-supplied string for inclusion in an error
15
- * message. Control characters become spaces so a hostile `error_description`
10
+ * message. Each run of C0/C1 control characters becomes one space (and the
11
+ * result is trimmed) so a hostile `error_description`
16
12
  * cannot forge a log line or inject an ANSI escape in a consumer's console,
17
13
  * the result is capped at {@link PROVIDER_TEXT_MAX_LENGTH}, and a surrogate
18
14
  * pair split by that cap is repaired rather than left lone. Use this on
19
15
  * every byte a remote server chose that reaches a message.
20
- *
21
- * @param value The remote-supplied text.
22
- * @returns The flattened, capped, well-formed text.
23
16
  */
24
17
  export declare function sanitizeProviderText(value: string): string;
package/esm/utils/text.js CHANGED
@@ -1,25 +1,18 @@
1
1
  /**
2
2
  * Bounding and flattening for text a remote server chose.
3
3
  *
4
- * Lives in `utils/` rather than beside either caller because both ladders that
5
- * turn a server's bytes into an error message — the client's `_http.ts` and
6
- * the connectors' `_token-exchange.ts` — must sanitize identically, and the
7
- * client cannot reach up into `identity/external/`.
8
- *
9
4
  * @module
10
5
  */
11
6
  /** Characters of remote-supplied text kept in an error message. */
12
7
  export const PROVIDER_TEXT_MAX_LENGTH = 200;
13
8
  /**
14
9
  * Bounds and flattens a remote-supplied string for inclusion in an error
15
- * message. Control characters become spaces so a hostile `error_description`
10
+ * message. Each run of C0/C1 control characters becomes one space (and the
11
+ * result is trimmed) so a hostile `error_description`
16
12
  * cannot forge a log line or inject an ANSI escape in a consumer's console,
17
13
  * the result is capped at {@link PROVIDER_TEXT_MAX_LENGTH}, and a surrogate
18
14
  * pair split by that cap is repaired rather than left lone. Use this on
19
15
  * every byte a remote server chose that reaches a message.
20
- *
21
- * @param value The remote-supplied text.
22
- * @returns The flattened, capped, well-formed text.
23
16
  */
24
17
  export function sanitizeProviderText(value) {
25
18
  // deno-lint-ignore no-control-regex
@@ -38,8 +38,8 @@ export declare function safeReturnTo(value: string | null | undefined, fallback?
38
38
  export interface LoginContinuationOptions {
39
39
  /**
40
40
  * The authorization endpoint (absolute URL or path). When `returnTo` points at
41
- * it, the flow is **resumed** (the in-flight authorize request already has a
42
- * PKCE `state`). Omit it and every `returnTo` starts a fresh login.
41
+ * it, the flow is **resumed** (the in-flight authorize request already carries
42
+ * its `state`). Omit it and every `returnTo` starts a fresh login.
43
43
  */
44
44
  authorizeEndpoint?: string;
45
45
  /** The login path to start a fresh login. Defaults to `"/auth/login"`. */
package/esm/utils/url.js CHANGED
@@ -79,8 +79,6 @@ export function safeReturnTo(value, fallback = "/") {
79
79
  export function loginContinuation(returnTo, options = {}) {
80
80
  const safe = safeReturnTo(returnTo, options.defaultReturnTo ?? "/");
81
81
  if (options.authorizeEndpoint) {
82
- // Compare path only so the in-flight authorize query (client_id, state, …)
83
- // is preserved; the base lets absolute and relative inputs both resolve.
84
82
  const base = "http://login.continuation.local";
85
83
  const authorizePath = new URL(options.authorizeEndpoint, base).pathname;
86
84
  if (new URL(safe, base).pathname === authorizePath)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@udibo/oauth2",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "A flexible OAuth2 toolkit: framework-agnostic authorization & resource servers, a typed client, Hono adapters (including a backend-for-frontend), and React bindings.",
5
5
  "repository": {
6
6
  "type": "git",