@furystack/rest-service 14.0.0 → 14.1.1

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 (143) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/esm/actions/error-action.d.ts +7 -2
  3. package/esm/actions/error-action.d.ts.map +1 -1
  4. package/esm/actions/error-action.js +7 -2
  5. package/esm/actions/error-action.js.map +1 -1
  6. package/esm/actions/get-current-user.d.ts +1 -4
  7. package/esm/actions/get-current-user.d.ts.map +1 -1
  8. package/esm/actions/get-current-user.js +1 -4
  9. package/esm/actions/get-current-user.js.map +1 -1
  10. package/esm/actions/is-authenticated.d.ts +3 -4
  11. package/esm/actions/is-authenticated.d.ts.map +1 -1
  12. package/esm/actions/is-authenticated.js +3 -4
  13. package/esm/actions/is-authenticated.js.map +1 -1
  14. package/esm/actions/logout.d.ts +2 -6
  15. package/esm/actions/logout.d.ts.map +1 -1
  16. package/esm/actions/logout.js +2 -6
  17. package/esm/actions/logout.js.map +1 -1
  18. package/esm/actions/not-found-action.d.ts +1 -3
  19. package/esm/actions/not-found-action.d.ts.map +1 -1
  20. package/esm/actions/not-found-action.js +1 -3
  21. package/esm/actions/not-found-action.js.map +1 -1
  22. package/esm/actions/password-login-action.d.ts +5 -12
  23. package/esm/actions/password-login-action.d.ts.map +1 -1
  24. package/esm/actions/password-login-action.js +5 -12
  25. package/esm/actions/password-login-action.js.map +1 -1
  26. package/esm/authenticate.d.ts +5 -11
  27. package/esm/authenticate.d.ts.map +1 -1
  28. package/esm/authenticate.js +5 -11
  29. package/esm/authenticate.js.map +1 -1
  30. package/esm/authentication-providers/authentication-provider.d.ts +17 -0
  31. package/esm/authentication-providers/authentication-provider.d.ts.map +1 -1
  32. package/esm/authentication-providers/basic-auth-provider.d.ts +7 -0
  33. package/esm/authentication-providers/basic-auth-provider.d.ts.map +1 -1
  34. package/esm/authentication-providers/basic-auth-provider.js +7 -0
  35. package/esm/authentication-providers/basic-auth-provider.js.map +1 -1
  36. package/esm/authentication-providers/cookie-auth-provider.d.ts.map +1 -1
  37. package/esm/authentication-providers/cookie-auth-provider.js +8 -0
  38. package/esm/authentication-providers/cookie-auth-provider.js.map +1 -1
  39. package/esm/authorize.d.ts +5 -17
  40. package/esm/authorize.d.ts.map +1 -1
  41. package/esm/authorize.js +5 -17
  42. package/esm/authorize.js.map +1 -1
  43. package/esm/get-schema-from-api.d.ts.map +1 -1
  44. package/esm/get-schema-from-api.js +1 -0
  45. package/esm/get-schema-from-api.js.map +1 -1
  46. package/esm/helpers.d.ts +6 -6
  47. package/esm/helpers.d.ts.map +1 -1
  48. package/esm/helpers.js +3 -4
  49. package/esm/helpers.js.map +1 -1
  50. package/esm/http-authentication-settings.d.ts +27 -6
  51. package/esm/http-authentication-settings.d.ts.map +1 -1
  52. package/esm/http-authentication-settings.js +6 -0
  53. package/esm/http-authentication-settings.js.map +1 -1
  54. package/esm/http-server-pool.spec.js.map +1 -1
  55. package/esm/http-user-context.d.ts +8 -5
  56. package/esm/http-user-context.d.ts.map +1 -1
  57. package/esm/http-user-context.js +26 -27
  58. package/esm/http-user-context.js.map +1 -1
  59. package/esm/http-user-context.spec.js +48 -6
  60. package/esm/http-user-context.spec.js.map +1 -1
  61. package/esm/identity-cache-keys.d.ts +28 -0
  62. package/esm/identity-cache-keys.d.ts.map +1 -0
  63. package/esm/identity-cache-keys.js +13 -0
  64. package/esm/identity-cache-keys.js.map +1 -0
  65. package/esm/identity-cache-keys.spec.d.ts +2 -0
  66. package/esm/identity-cache-keys.spec.d.ts.map +1 -0
  67. package/esm/identity-cache-keys.spec.js +21 -0
  68. package/esm/identity-cache-keys.spec.js.map +1 -0
  69. package/esm/identity-event-bus.d.ts +102 -0
  70. package/esm/identity-event-bus.d.ts.map +1 -0
  71. package/esm/identity-event-bus.js +151 -0
  72. package/esm/identity-event-bus.js.map +1 -0
  73. package/esm/identity-event-bus.spec.d.ts +2 -0
  74. package/esm/identity-event-bus.spec.d.ts.map +1 -0
  75. package/esm/identity-event-bus.spec.js +392 -0
  76. package/esm/identity-event-bus.spec.js.map +1 -0
  77. package/esm/index.d.ts +3 -0
  78. package/esm/index.d.ts.map +1 -1
  79. package/esm/index.js +3 -0
  80. package/esm/index.js.map +1 -1
  81. package/esm/login-response-strategy.d.ts.map +1 -1
  82. package/esm/login-response-strategy.js.map +1 -1
  83. package/esm/openapi/generate-openapi-document.spec.js.map +1 -1
  84. package/esm/proxy-manager.spec.js.map +1 -1
  85. package/esm/request-action-implementation.d.ts +25 -0
  86. package/esm/request-action-implementation.d.ts.map +1 -1
  87. package/esm/request-action-implementation.js +9 -0
  88. package/esm/request-action-implementation.js.map +1 -1
  89. package/esm/rest-api-runtime.spec.js.map +1 -1
  90. package/esm/server-telemetry.d.ts +6 -14
  91. package/esm/server-telemetry.d.ts.map +1 -1
  92. package/esm/server-telemetry.js +2 -3
  93. package/esm/server-telemetry.js.map +1 -1
  94. package/esm/user-resolution-cache.d.ts +65 -0
  95. package/esm/user-resolution-cache.d.ts.map +1 -0
  96. package/esm/user-resolution-cache.js +101 -0
  97. package/esm/user-resolution-cache.js.map +1 -0
  98. package/esm/user-resolution-cache.spec.d.ts +2 -0
  99. package/esm/user-resolution-cache.spec.d.ts.map +1 -0
  100. package/esm/user-resolution-cache.spec.js +220 -0
  101. package/esm/user-resolution-cache.spec.js.map +1 -0
  102. package/esm/user-store.d.ts.map +1 -1
  103. package/esm/user-store.js.map +1 -1
  104. package/esm/validate.d.ts +13 -12
  105. package/esm/validate.d.ts.map +1 -1
  106. package/esm/validate.integration.spec.js +1 -0
  107. package/esm/validate.integration.spec.js.map +1 -1
  108. package/esm/validate.js +12 -0
  109. package/esm/validate.js.map +1 -1
  110. package/package.json +15 -13
  111. package/src/actions/error-action.ts +7 -3
  112. package/src/actions/get-current-user.ts +1 -4
  113. package/src/actions/is-authenticated.ts +3 -4
  114. package/src/actions/logout.ts +2 -6
  115. package/src/actions/not-found-action.ts +1 -3
  116. package/src/actions/password-login-action.ts +5 -12
  117. package/src/authenticate.ts +5 -11
  118. package/src/authentication-providers/authentication-provider.ts +17 -0
  119. package/src/authentication-providers/basic-auth-provider.ts +7 -0
  120. package/src/authentication-providers/cookie-auth-provider.ts +8 -0
  121. package/src/authorize.ts +6 -17
  122. package/src/get-schema-from-api.ts +1 -0
  123. package/src/helpers.ts +11 -11
  124. package/src/http-authentication-settings.ts +34 -6
  125. package/src/http-server-pool.spec.ts +1 -1
  126. package/src/http-user-context.spec.ts +59 -11
  127. package/src/http-user-context.ts +38 -31
  128. package/src/identity-cache-keys.spec.ts +24 -0
  129. package/src/identity-cache-keys.ts +31 -0
  130. package/src/identity-event-bus.spec.ts +285 -0
  131. package/src/identity-event-bus.ts +230 -0
  132. package/src/index.ts +3 -0
  133. package/src/login-response-strategy.ts +1 -0
  134. package/src/openapi/generate-openapi-document.spec.ts +2 -2
  135. package/src/proxy-manager.spec.ts +6 -6
  136. package/src/request-action-implementation.ts +25 -0
  137. package/src/rest-api-runtime.spec.ts +1 -1
  138. package/src/server-telemetry.ts +6 -14
  139. package/src/user-resolution-cache.spec.ts +241 -0
  140. package/src/user-resolution-cache.ts +164 -0
  141. package/src/user-store.ts +1 -0
  142. package/src/validate.integration.spec.ts +1 -0
  143. package/src/validate.ts +14 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,123 @@
1
1
  # Changelog
2
2
 
3
+ ## [14.1.1] - 2026-06-05
4
+
5
+ ### 👷 CI
6
+
7
+ - Raised the minimum supported Node.js to `>=24.0.0` (Node 24 LTS) in `engines`, dropping Node 22.
8
+
9
+ ### ⬆️ Dependencies
10
+
11
+ - Bumped dev `ws` to `^8.21.0` and dev `vitest` to `^4.1.8`. No source changes — dev-tooling bump only.
12
+ - Released alongside the distributed task management work to keep `@furystack/*` workspace dependency ranges current. No functional or API changes to this package.
13
+
14
+ ## [14.1.0] - 2026-05-21
15
+
16
+ ### ⬆️ Dependencies
17
+
18
+ - Bump `ajv` to `^8.20.0`.
19
+ - Bump dev `vitest` to `^4.1.5`.
20
+ - Adds `@furystack/cross-node-bus` as a runtime dependency. Resolves to the in-process default bus unless an adapter is bound.
21
+ - Bumped `@furystack/cache` to its new major version. The removed `obsoleteRange` / `removeRange` APIs are not used by `@furystack/rest-service`, so no source changes were required. See the `@furystack/cache` changelog for the migration if you import the cache directly alongside this package.
22
+ - Added `@furystack/cache` as a runtime dependency (`workspace:^`). Used internally by `UserResolutionCache`; not part of `@furystack/rest-service`'s public API surface.
23
+ - Bumped `ws` from `^8.20.0` to `^8.20.1` (dev dependency used in tests).
24
+ - Bumped `@types/node` to `^25.9.1` and `vitest` to `^4.1.7`. Dev-tooling only.
25
+
26
+ ### 📚 Documentation
27
+
28
+ - Rewrote JSDoc across the public API — built-in actions (`getCurrentUser`, `isAuthenticated`, `logout`, `passwordLoginAction`, `errorAction`, `notFoundAction`), `authenticate`, `authorize`, `validate`, `HttpAuthenticationSettings`, `HttpUserContext`, the login response strategy, and the request-action implementation — to follow the new value-test guidance: dropped restate-the-type narration, kept intent / trade-offs / constraints around session handling, validation, and error mapping.
29
+ - Expanded `UserResolutionCache` JSDoc to cross-link `invalidateByUser` and document the multi-node trade-off.
30
+
31
+ ### ✨ Features
32
+
33
+ ### `IdentityEventBus` — cross-node identity invalidation
34
+
35
+ A typed wrapper over `@furystack/cross-node-bus` that bridges the per-process `UserResolutionCache` to a shared bus, so logouts, role flips, password resets and account deletions invalidate cached identities on every node — not just the one that originated the change. Collapses the staleness window from `userCacheTtlMs` (default 30 s) to bus latency (single-digit ms with Redis pub/sub).
36
+
37
+ ```typescript
38
+ import { IdentityEventBus } from '@furystack/rest-service'
39
+
40
+ const bus = injector.get(IdentityEventBus)
41
+ await bus.publish({ type: 'userRolesChanged', username: 'alice' })
42
+ // → invalidates every cached entry that resolved to alice on this node
43
+ // AND every sibling node, synchronously locally and at bus speed remotely.
44
+ ```
45
+
46
+ Supported event variants (`IdentityEvent`):
47
+
48
+ - `userLoggedOut` / `sessionInvalidated` — drop the cache entry for a single session id.
49
+ - `userRolesChanged` / `userDeleted` / `passwordChanged` — drop every cached entry that resolved to the named user, regardless of which session originally populated it.
50
+
51
+ **Self-delivery contract:** `publish()` applies the matching local invalidation and fires local subscribers synchronously **before** awaiting the bus broadcast. Sibling nodes receive the message via a `subscribeRemoteOnly` bridge — own publishes never round-trip through the transport, so duplicate work is impossible.
52
+
53
+ `HttpUserContext.cookieLogout` now broadcasts `userLoggedOut` on the bus instead of dropping the local cache directly. Apps that have not bound a transport adapter run against the in-process default `CrossNodeBus` and behave exactly as before.
54
+
55
+ ### `UserResolutionCache.invalidateByUser(username)`
56
+
57
+ Drops every cached entry that resolved to `username`, backed by the cache's reverse tag index — no per-call O(n) scan. Use after role / password / account-state changes that must take effect immediately on the next request, instead of waiting for the TTL window to elapse.
58
+
59
+ ```typescript
60
+ const userCache = injector.get(UserResolutionCache)
61
+ userCache.invalidateByUser('alice')
62
+ ```
63
+
64
+ For multi-node deployments, prefer publishing the corresponding `IdentityEvent` on `IdentityEventBus` — it calls `invalidateByUser` locally and on every sibling.
65
+
66
+ ### `identity-cache-keys` exports
67
+
68
+ New module exporting the cache-key / cache-tag shapes shared by the cookie-auth provider, `UserResolutionCache`, and `IdentityEventBus`:
69
+
70
+ - `SessionCacheKey` (type) — `` `cookie:${string}` ``.
71
+ - `UserCacheTag` (type) — `` `user:${string}` ``.
72
+ - `sessionCacheKey(sessionId)` — builds the cookie-auth cache key.
73
+ - `userCacheTag(username)` — builds the user-resolution cache tag.
74
+
75
+ Centralising these strings means the local-vs-replicated invalidation paths cannot drift.
76
+
77
+ ### Short-TTL user-resolution cache for `HttpUserContext`
78
+
79
+ `HttpUserContext.getCurrentUser` now consults a singleton `UserResolutionCache` before walking the configured authentication providers. Successful resolutions are cached per-process under a key produced by the matching provider (e.g. session id for cookie auth) and expire after `HttpAuthenticationSettings.userCacheTtlMs` (default **30 000 ms**, capacity **10 000** entries).
80
+
81
+ This bounds cross-instance staleness for out-of-band identity changes — session invalidation on a sibling node, role mutation in storage, etc. — while sharply cutting the auth-store load on chatty clients (every authenticated request used to walk the provider chain on every call).
82
+
83
+ **New configuration on `HttpAuthenticationSettings`:**
84
+
85
+ | Field | Default | Effect |
86
+ | ------------------- | ------- | ------------------------------------------------------------- |
87
+ | `userCacheTtlMs` | `30000` | TTL for cached `User` entries. `0` disables caching entirely. |
88
+ | `userCacheCapacity` | `10000` | LRU capacity ceiling. |
89
+
90
+ ### Cache-key contract on `AuthenticationProvider`
91
+
92
+ Added an optional `getCacheKey?(request): string | null` method to `AuthenticationProvider`. Providers that opt in produce stable per-identity keys consumed by the user-resolution cache; providers that omit it (or return `null`) cause the request to bypass the cache.
93
+
94
+ Built-in providers:
95
+
96
+ - **`createCookieAuthProvider`** — keys requests by `cookie:${sessionId}`. Cache hits skip both the session-store and user-store lookups.
97
+ - **`createBasicAuthProvider`** — intentionally does **not** opt in. Caching by username would skip password verification on every cache hit.
98
+ - **`createJwtAuthProvider`** (in `@furystack/auth-jwt`) — also intentionally does **not** opt in. See the `@furystack/auth-jwt` changelog for the rationale (token expiry / revocation must be re-checked per request).
99
+
100
+ ### `UserResolutionCache` token
101
+
102
+ Exported a new singleton DI token. Consumers can call `injector.get(UserResolutionCache).invalidate(cacheKey)` to drop a specific entry (e.g. after an out-of-band role mutation), or `invalidateAll()` to flush every entry.
103
+
104
+ `HttpUserContext.cookieLogout` automatically invalidates the local cache entry for the logged-out session, so the very next request on the same node re-authenticates immediately. Sibling nodes still rely on TTL expiry.
105
+
106
+ **Example — invalidate after an admin mutates roles in storage:**
107
+
108
+ ```typescript
109
+ import { UserResolutionCache, UserDataSet } from '@furystack/rest-service'
110
+
111
+ await injector.get(UserDataSet).update(injector, 'alice', { roles: ['admin'] })
112
+ injector.get(UserResolutionCache).invalidate(`cookie:${sessionId}`)
113
+ ```
114
+
115
+ ### ♻️ Refactoring
116
+
117
+ ### `HttpUserContext` no longer maintains a per-request `WeakMap`
118
+
119
+ The previous `WeakMap<headers, Promise<User>>` cache (per-`HttpUserContext`-instance) has been removed in favor of the shared `UserResolutionCache`. With the request-scoped HUC, the WeakMap was effectively single-shot per request anyway. Anonymous-style requests (no provider yields a key) now bypass the cache entirely instead of being dedup-cached for the lifetime of the HUC instance.
120
+
3
121
  ## [14.0.0] - 2026-04-25
4
122
 
5
123
  ### 💥 Breaking Changes
@@ -1,7 +1,12 @@
1
1
  import type { RequestAction } from '../request-action-implementation.js';
2
2
  /**
3
- * Action for unhandled (500) errors
4
- * Returns a serialized error instance in JSON format.
3
+ * Default error handler. Maps thrown errors to HTTP responses:
4
+ *
5
+ * - {@link SchemaValidationError} → 400 with `{ message, errors }`
6
+ * - {@link RequestError} → its `responseCode` with `{ message }`
7
+ * - {@link AuthorizationError} → 403 with `{ message }`
8
+ * - other `Error` → 500 with `{ message }`
9
+ * - non-Error throwables → 500 with a generic message
5
10
  */
6
11
  export declare const ErrorAction: RequestAction<{
7
12
  body: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"error-action.d.ts","sourceRoot":"","sources":["../../src/actions/error-action.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAIxE;;;GAGG;AAEH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IACtC,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5B,CAoBA,CAAA"}
1
+ {"version":3,"file":"error-action.d.ts","sourceRoot":"","sources":["../../src/actions/error-action.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAIxE;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IACtC,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5B,CAoBA,CAAA"}
@@ -3,8 +3,13 @@ import { AuthorizationError } from '@furystack/core';
3
3
  import { JsonResult } from '../request-action-implementation.js';
4
4
  import { SchemaValidationError } from '../schema-validator/schema-validation-error.js';
5
5
  /**
6
- * Action for unhandled (500) errors
7
- * Returns a serialized error instance in JSON format.
6
+ * Default error handler. Maps thrown errors to HTTP responses:
7
+ *
8
+ * - {@link SchemaValidationError} → 400 with `{ message, errors }`
9
+ * - {@link RequestError} → its `responseCode` with `{ message }`
10
+ * - {@link AuthorizationError} → 403 with `{ message }`
11
+ * - other `Error` → 500 with `{ message }`
12
+ * - non-Error throwables → 500 with a generic message
8
13
  */
9
14
  export const ErrorAction = async ({ getBody }) => {
10
15
  const body = await getBody();
@@ -1 +1 @@
1
- {"version":3,"file":"error-action.js","sourceRoot":"","sources":["../../src/actions/error-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAA;AAEtF;;;GAGG;AAEH,MAAM,CAAC,MAAM,WAAW,GAGnB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IACzB,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;IAE5B,IAAI,IAAI,YAAY,qBAAqB,EAAE,CAAC;QAC1C,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAA;IACxE,CAAC;IAED,IAAI,IAAI,YAAY,YAAY,EAAE,CAAC;QACjC,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IACjE,CAAC;IAED,IAAI,IAAI,YAAY,kBAAkB,EAAE,CAAC;QACvC,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;IACnD,CAAC;IAED,IAAI,IAAI,YAAY,KAAK,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;IACnD,CAAC;IAED,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAA;AACrE,CAAC,CAAA"}
1
+ {"version":3,"file":"error-action.js","sourceRoot":"","sources":["../../src/actions/error-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAA;AAEtF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAGnB,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IACzB,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;IAE5B,IAAI,IAAI,YAAY,qBAAqB,EAAE,CAAC;QAC1C,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAA;IACxE,CAAC;IAED,IAAI,IAAI,YAAY,YAAY,EAAE,CAAC;QACjC,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IACjE,CAAC;IAED,IAAI,IAAI,YAAY,kBAAkB,EAAE,CAAC;QACvC,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;IACnD,CAAC;IAED,IAAI,IAAI,YAAY,KAAK,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;IACnD,CAAC;IAED,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAA;AACrE,CAAC,CAAA"}
@@ -1,9 +1,6 @@
1
1
  import type { User } from '@furystack/core';
2
2
  import type { RequestAction } from '../request-action-implementation.js';
3
- /**
4
- * Action that returns the current authenticated user
5
- * @param injector The injector from the current stack
6
- */
3
+ /** Returns the current authenticated user; 401 if no user is bound. */
7
4
  export declare const GetCurrentUser: RequestAction<{
8
5
  result: User;
9
6
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"get-current-user.d.ts","sourceRoot":"","sources":["../../src/actions/get-current-user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAG3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC;IACzC,MAAM,EAAE,IAAI,CAAA;CACb,CAGC,CAAA"}
1
+ {"version":3,"file":"get-current-user.d.ts","sourceRoot":"","sources":["../../src/actions/get-current-user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAG3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE,uEAAuE;AACvE,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC;IACzC,MAAM,EAAE,IAAI,CAAA;CACb,CAGC,CAAA"}
@@ -1,10 +1,7 @@
1
1
  import { getCurrentUser } from '@furystack/core';
2
2
  import { Authenticate } from '../authenticate.js';
3
3
  import { JsonResult } from '../request-action-implementation.js';
4
- /**
5
- * Action that returns the current authenticated user
6
- * @param injector The injector from the current stack
7
- */
4
+ /** Returns the current authenticated user; 401 if no user is bound. */
8
5
  export const GetCurrentUser = Authenticate()(async ({ injector }) => {
9
6
  const user = await getCurrentUser(injector);
10
7
  return JsonResult(user);
@@ -1 +1 @@
1
- {"version":3,"file":"get-current-user.js","sourceRoot":"","sources":["../../src/actions/get-current-user.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAEtB,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;IACzC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAA;IAC3C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"get-current-user.js","sourceRoot":"","sources":["../../src/actions/get-current-user.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE,uEAAuE;AACvE,MAAM,CAAC,MAAM,cAAc,GAEtB,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;IACzC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,QAAQ,CAAC,CAAA;IAC3C,OAAO,UAAU,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC,CAAC,CAAA"}
@@ -1,9 +1,8 @@
1
1
  import type { RequestAction } from '../request-action-implementation.js';
2
2
  /**
3
- * Action that returns if the current user is authenticated
4
- * @param options The options for the Custom Action
5
- * @param options.injector The Injector from the current context
6
- * @returns A standard authentication result
3
+ * Returns `{ isAuthenticated: boolean }`. Unauthenticated callers receive
4
+ * a 200 with `false` rather than a 401 — use this when the client needs to
5
+ * branch on auth state without triggering a re-login flow.
7
6
  */
8
7
  export declare const IsAuthenticated: RequestAction<{
9
8
  result: {
@@ -1 +1 @@
1
- {"version":3,"file":"is-authenticated.d.ts","sourceRoot":"","sources":["../../src/actions/is-authenticated.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE;QAAE,eAAe,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAGnF,CAAA"}
1
+ {"version":3,"file":"is-authenticated.d.ts","sourceRoot":"","sources":["../../src/actions/is-authenticated.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE;QAAE,eAAe,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAGnF,CAAA"}
@@ -1,10 +1,9 @@
1
1
  import { isAuthenticated } from '@furystack/core';
2
2
  import { JsonResult } from '../request-action-implementation.js';
3
3
  /**
4
- * Action that returns if the current user is authenticated
5
- * @param options The options for the Custom Action
6
- * @param options.injector The Injector from the current context
7
- * @returns A standard authentication result
4
+ * Returns `{ isAuthenticated: boolean }`. Unauthenticated callers receive
5
+ * a 200 with `false` rather than a 401 — use this when the client needs to
6
+ * branch on auth state without triggering a re-login flow.
8
7
  */
9
8
  export const IsAuthenticated = async ({ injector }) => {
10
9
  const isAuthenticatedResult = await isAuthenticated(injector);
@@ -1 +1 @@
1
- {"version":3,"file":"is-authenticated.js","sourceRoot":"","sources":["../../src/actions/is-authenticated.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAA4D,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC7G,MAAM,qBAAqB,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC7D,OAAO,UAAU,CAAC,EAAE,eAAe,EAAE,qBAAqB,EAAE,CAAC,CAAA;AAC/D,CAAC,CAAA"}
1
+ {"version":3,"file":"is-authenticated.js","sourceRoot":"","sources":["../../src/actions/is-authenticated.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAA4D,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC7G,MAAM,qBAAqB,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC7D,OAAO,UAAU,CAAC,EAAE,eAAe,EAAE,qBAAqB,EAAE,CAAC,CAAA;AAC/D,CAAC,CAAA"}
@@ -1,11 +1,7 @@
1
1
  import type { RequestAction } from '../request-action-implementation.js';
2
2
  /**
3
- * Action that logs out the current user
4
- * @param root0 The Options object
5
- * @param root0.injector The injector from the context
6
- * @param root0.request The current Request object
7
- * @param root0.response The Response object
8
- * @returns An empty result that indicates the success
3
+ * Logs out via {@link HttpUserContext.cookieLogout} — clears the session
4
+ * cookie and removes the session row from the session DataSet.
9
5
  */
10
6
  export declare const LogoutAction: RequestAction<{
11
7
  result: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/actions/logout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAG3D,CAAA"}
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/actions/logout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAG3D,CAAA"}
@@ -1,12 +1,8 @@
1
1
  import { HttpUserContext } from '../http-user-context.js';
2
2
  import { EmptyResult } from '../request-action-implementation.js';
3
3
  /**
4
- * Action that logs out the current user
5
- * @param root0 The Options object
6
- * @param root0.injector The injector from the context
7
- * @param root0.request The current Request object
8
- * @param root0.response The Response object
9
- * @returns An empty result that indicates the success
4
+ * Logs out via {@link HttpUserContext.cookieLogout} — clears the session
5
+ * cookie and removes the session row from the session DataSet.
10
6
  */
11
7
  export const LogoutAction = async ({ injector, request, response }) => {
12
8
  await injector.get(HttpUserContext).cookieLogout(request, response);
@@ -1 +1 @@
1
- {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/actions/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AAEjE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAuC,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IACxG,MAAM,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACnE,OAAO,WAAW,EAAE,CAAA;AACtB,CAAC,CAAA"}
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/actions/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AAEjE;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAuC,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IACxG,MAAM,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IACnE,OAAO,WAAW,EAAE,CAAA;AACtB,CAAC,CAAA"}
@@ -1,7 +1,5 @@
1
1
  import type { RequestAction } from '../request-action-implementation.js';
2
- /**
3
- * @returns The standard Not Found action result
4
- */
2
+ /** Default `404` action. Returns `{ error: 'Content not found' }`. */
5
3
  export declare const NotFoundAction: RequestAction<{
6
4
  result: {
7
5
  error: string;
@@ -1 +1 @@
1
- {"version":3,"file":"not-found-action.d.ts","sourceRoot":"","sources":["../../src/actions/not-found-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAOvE,CAAA"}
1
+ {"version":3,"file":"not-found-action.d.ts","sourceRoot":"","sources":["../../src/actions/not-found-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE,sEAAsE;AACtE,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAOvE,CAAA"}
@@ -1,7 +1,5 @@
1
1
  import { JsonResult } from '../request-action-implementation.js';
2
- /**
3
- * @returns The standard Not Found action result
4
- */
2
+ /** Default `404` action. Returns `{ error: 'Content not found' }`. */
5
3
  export const NotFoundAction = async () => {
6
4
  return JsonResult({
7
5
  error: 'Content not found',
@@ -1 +1 @@
1
- {"version":3,"file":"not-found-action.js","sourceRoot":"","sources":["../../src/actions/not-found-action.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAiD,KAAK,IAAI,EAAE;IACrF,OAAO,UAAU,CACf;QACE,KAAK,EAAE,mBAAmB;KAC3B,EACD,GAAG,CACJ,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"not-found-action.js","sourceRoot":"","sources":["../../src/actions/not-found-action.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE,sEAAsE;AACtE,MAAM,CAAC,MAAM,cAAc,GAAiD,KAAK,IAAI,EAAE;IACrF,OAAO,UAAU,CACf;QACE,KAAK,EAAE,mBAAmB;KAC3B,EACD,GAAG,CACJ,CAAA;AACH,CAAC,CAAA"}
@@ -1,18 +1,11 @@
1
1
  import type { LoginResponseStrategy } from '../login-response-strategy.js';
2
2
  import type { RequestAction } from '../request-action-implementation.js';
3
3
  /**
4
- * Creates a login {@link RequestAction} that authenticates a user by
5
- * username + password, then delegates session/token creation to the
6
- * provided {@link LoginResponseStrategy}.
7
- *
8
- * The return type is inferred from the strategy:
9
- * - Cookie strategy -> `ActionResult<User>`
10
- * - JWT strategy -> `ActionResult<{ accessToken: string; refreshToken: string }>`
11
- *
12
- * A random delay (0-1 s) is added on failure to mitigate timing attacks.
13
- *
14
- * @param strategy The login response strategy that produces the session/token result
15
- * @returns A `RequestAction` that can be wired into a REST API route
4
+ * Username + password login action. Authenticates via {@link HttpUserContext},
5
+ * then delegates session/token creation to `strategy`. The result type is
6
+ * inferred from the strategy (e.g. `User` for cookies, `{ accessToken,
7
+ * refreshToken }` for JWT). Adds a 0–1 s random delay on failure to mitigate
8
+ * timing attacks; rethrows as `RequestError(400)`.
16
9
  */
17
10
  export declare const createPasswordLoginAction: <TResult>(strategy: LoginResponseStrategy<TResult>) => RequestAction<{
18
11
  result: TResult;
@@ -1 +1 @@
1
- {"version":3,"file":"password-login-action.d.ts","sourceRoot":"","sources":["../../src/actions/password-login-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAExE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,GAAI,OAAO,EAC/C,UAAU,qBAAqB,CAAC,OAAO,CAAC,KACvC,aAAa,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAWjF,CAAA"}
1
+ {"version":3,"file":"password-login-action.d.ts","sourceRoot":"","sources":["../../src/actions/password-login-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAExE;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GAAI,OAAO,EAC/C,UAAU,qBAAqB,CAAC,OAAO,CAAC,KACvC,aAAa,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAWjF,CAAA"}
@@ -2,18 +2,11 @@ import { RequestError } from '@furystack/rest';
2
2
  import { sleepAsync } from '@furystack/utils';
3
3
  import { HttpUserContext } from '../http-user-context.js';
4
4
  /**
5
- * Creates a login {@link RequestAction} that authenticates a user by
6
- * username + password, then delegates session/token creation to the
7
- * provided {@link LoginResponseStrategy}.
8
- *
9
- * The return type is inferred from the strategy:
10
- * - Cookie strategy -> `ActionResult<User>`
11
- * - JWT strategy -> `ActionResult<{ accessToken: string; refreshToken: string }>`
12
- *
13
- * A random delay (0-1 s) is added on failure to mitigate timing attacks.
14
- *
15
- * @param strategy The login response strategy that produces the session/token result
16
- * @returns A `RequestAction` that can be wired into a REST API route
5
+ * Username + password login action. Authenticates via {@link HttpUserContext},
6
+ * then delegates session/token creation to `strategy`. The result type is
7
+ * inferred from the strategy (e.g. `User` for cookies, `{ accessToken,
8
+ * refreshToken }` for JWT). Adds a 0–1 s random delay on failure to mitigate
9
+ * timing attacks; rethrows as `RequestError(400)`.
17
10
  */
18
11
  export const createPasswordLoginAction = (strategy) => {
19
12
  return async ({ injector, getBody }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"password-login-action.js","sourceRoot":"","sources":["../../src/actions/password-login-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAIzD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,QAAwC,EAC0C,EAAE;IACpF,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;QAC5B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC/F,OAAO,QAAQ,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;YACtC,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QAC7C,CAAC;IACH,CAAC,CAAA;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"password-login-action.js","sourceRoot":"","sources":["../../src/actions/password-login-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAIzD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,QAAwC,EAC0C,EAAE;IACpF,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;QAC5B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC/F,OAAO,QAAQ,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;YACtC,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QAC7C,CAAC;IACH,CAAC,CAAA;AACH,CAAC,CAAA"}
@@ -1,20 +1,14 @@
1
1
  import type { RequestAction } from './request-action-implementation.js';
2
2
  /**
3
- * Higher-order function that wraps a request action to require authentication.
4
- * If the user is not authenticated, returns a 401 Unauthorized response.
5
- * Includes a random delay on unauthorized requests to mitigate timing attacks.
3
+ * Wraps a {@link RequestAction} to require authentication. Unauthenticated
4
+ * requests return `401 Unauthorized` (with `WWW-Authenticate: Basic` when
5
+ * the chain includes Basic Auth). Adds a 0–1 s random delay on rejection to
6
+ * mitigate timing attacks.
6
7
  *
7
- * @returns A function that wraps the provided action with authentication check
8
8
  * @example
9
9
  * ```ts
10
- * // Wrap an endpoint to require authentication
11
- * const protectedEndpoint = Authenticate()(myEndpoint)
12
- *
13
- * // Or use directly in API definition
14
10
  * api: {
15
- * GET: {
16
- * '/users': Authenticate()(getUsersEndpoint)
17
- * }
11
+ * GET: { '/users': Authenticate()(getUsersEndpoint) },
18
12
  * }
19
13
  * ```
20
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"authenticate.d.ts","sourceRoot":"","sources":["../src/authenticate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,aAAa,EAAwB,MAAM,oCAAoC,CAAA;AAG3G;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,YAAY,SAEtB,CAAC,SAAS;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAAE,QAAQ,aAAa,CAAC,CAAC,CAAC,KAAG,aAAa,CAAC,CAAC,CAqBzE,CAAA"}
1
+ {"version":3,"file":"authenticate.d.ts","sourceRoot":"","sources":["../src/authenticate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,aAAa,EAAwB,MAAM,oCAAoC,CAAA;AAG3G;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,SAEtB,CAAC,SAAS;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAAE,QAAQ,aAAa,CAAC,CAAC,CAAC,KAAG,aAAa,CAAC,CAAC,CAqBzE,CAAA"}
@@ -3,21 +3,15 @@ import { sleepAsync } from '@furystack/utils';
3
3
  import { HttpUserContext } from './http-user-context.js';
4
4
  import { JsonResult } from './request-action-implementation.js';
5
5
  /**
6
- * Higher-order function that wraps a request action to require authentication.
7
- * If the user is not authenticated, returns a 401 Unauthorized response.
8
- * Includes a random delay on unauthorized requests to mitigate timing attacks.
6
+ * Wraps a {@link RequestAction} to require authentication. Unauthenticated
7
+ * requests return `401 Unauthorized` (with `WWW-Authenticate: Basic` when
8
+ * the chain includes Basic Auth). Adds a 0–1 s random delay on rejection to
9
+ * mitigate timing attacks.
9
10
  *
10
- * @returns A function that wraps the provided action with authentication check
11
11
  * @example
12
12
  * ```ts
13
- * // Wrap an endpoint to require authentication
14
- * const protectedEndpoint = Authenticate()(myEndpoint)
15
- *
16
- * // Or use directly in API definition
17
13
  * api: {
18
- * GET: {
19
- * '/users': Authenticate()(getUsersEndpoint)
20
- * }
14
+ * GET: { '/users': Authenticate()(getUsersEndpoint) },
21
15
  * }
22
16
  * ```
23
17
  */
@@ -1 +1 @@
1
- {"version":3,"file":"authenticate.js","sourceRoot":"","sources":["../src/authenticate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAE/D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,GAAG,EAAE,CACL,CAAgC,MAAwB,EAAoB,EAAE;IAC5E,MAAM,OAAO,GAAG,KAAK,EAAE,IAA6B,EAA4B,EAAE;QAChF,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;QACzB,MAAM,aAAa,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAA;QACrD,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;YACtC,MAAM,YAAY,GAAG,QAAQ;iBAC1B,GAAG,CAAC,eAAe,CAAC;iBACpB,cAAc,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAA;YAC9E,OAAO,UAAU,CACf,EAAE,KAAK,EAAE,cAAc,EAAE,EACzB,GAAG,EACH,YAAY,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CACtB,CAAA;QACjC,CAAC;QACD,OAAO,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAoB,CAAA;IAChD,CAAC,CAAA;IAED,OAAO,CAAC,eAAe,GAAG,IAAI,CAAA;IAE9B,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
1
+ {"version":3,"file":"authenticate.js","sourceRoot":"","sources":["../src/authenticate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAE/D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,GAAG,EAAE,CACL,CAAgC,MAAwB,EAAoB,EAAE;IAC5E,MAAM,OAAO,GAAG,KAAK,EAAE,IAA6B,EAA4B,EAAE;QAChF,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAA;QACzB,MAAM,aAAa,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAA;QACrD,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;YACtC,MAAM,YAAY,GAAG,QAAQ;iBAC1B,GAAG,CAAC,eAAe,CAAC;iBACpB,cAAc,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAA;YAC9E,OAAO,UAAU,CACf,EAAE,KAAK,EAAE,cAAc,EAAE,EACzB,GAAG,EACH,YAAY,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CACtB,CAAA;QACjC,CAAC;QACD,OAAO,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,CAAoB,CAAA;IAChD,CAAC,CAAA;IAED,OAAO,CAAC,eAAe,GAAG,IAAI,CAAA;IAE9B,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
@@ -21,5 +21,22 @@ export type AuthenticationProvider = {
21
21
  */
22
22
  readonly name: string;
23
23
  authenticate: (request: Pick<IncomingMessage, 'headers'>) => Promise<User | null>;
24
+ /**
25
+ * Optional cache-key resolver consulted by the user-resolution cache before
26
+ * the provider chain is walked.
27
+ *
28
+ * Returns a stable, opaque string identifying the authenticated principal
29
+ * for the given request, or `null` when this provider does not apply to
30
+ * the request (or refuses to participate in caching — e.g. Basic Auth,
31
+ * which must verify the password on every request).
32
+ *
33
+ * The first non-null key returned by the configured providers is used as
34
+ * the cache key. Keys returned by different providers must not collide;
35
+ * built-in providers prefix their keys (`cookie:`, `jwt:`).
36
+ *
37
+ * If no provider returns a key, the request bypasses the cache entirely
38
+ * and the provider chain is walked on every call.
39
+ */
40
+ readonly getCacheKey?: (request: Pick<IncomingMessage, 'headers'>) => string | null;
24
41
  };
25
42
  //# sourceMappingURL=authentication-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authentication-provider.d.ts","sourceRoot":"","sources":["../../src/authentication-providers/authentication-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAE3C;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;CAClF,CAAA"}
1
+ {"version":3,"file":"authentication-provider.d.ts","sourceRoot":"","sources":["../../src/authentication-providers/authentication-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAE3C;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IACjF;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;CACpF,CAAA"}
@@ -2,6 +2,13 @@ import type { User } from '@furystack/core';
2
2
  import type { AuthenticationProvider } from './authentication-provider.js';
3
3
  /**
4
4
  * Creates an authentication provider for HTTP Basic Authentication.
5
+ *
6
+ * **Caching note:** this provider intentionally does **not** implement
7
+ * `getCacheKey`. Caching authenticated users by username would bypass
8
+ * password verification on every cache hit — the cache cannot revalidate the
9
+ * password without re-running the authenticator. Basic Auth requests
10
+ * therefore always walk the provider chain, which is also where the password
11
+ * is verified.
5
12
  * @param authenticateUser Callback that verifies credentials and returns a User or throws
6
13
  */
7
14
  export declare const createBasicAuthProvider: (authenticateUser: (username: string, password: string) => Promise<User>) => AuthenticationProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"basic-auth-provider.d.ts","sourceRoot":"","sources":["../../src/authentication-providers/basic-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAE1E;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KACtE,sBAWD,CAAA"}
1
+ {"version":3,"file":"basic-auth-provider.d.ts","sourceRoot":"","sources":["../../src/authentication-providers/basic-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAE1E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,GAClC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KACtE,sBAWD,CAAA"}
@@ -1,5 +1,12 @@
1
1
  /**
2
2
  * Creates an authentication provider for HTTP Basic Authentication.
3
+ *
4
+ * **Caching note:** this provider intentionally does **not** implement
5
+ * `getCacheKey`. Caching authenticated users by username would bypass
6
+ * password verification on every cache hit — the cache cannot revalidate the
7
+ * password without re-running the authenticator. Basic Auth requests
8
+ * therefore always walk the provider chain, which is also where the password
9
+ * is verified.
3
10
  * @param authenticateUser Callback that verifies credentials and returns a User or throws
4
11
  */
5
12
  export const createBasicAuthProvider = (authenticateUser) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"basic-auth-provider.js","sourceRoot":"","sources":["../../src/authentication-providers/basic-auth-provider.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,gBAAuE,EAC/C,EAAE,CAAC,CAAC;IAC5B,IAAI,EAAE,YAAY;IAClB,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAA;QAChD,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAA;QAClD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC1E,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;QAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAA;QAC9C,OAAO,MAAM,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACnD,CAAC;CACF,CAAC,CAAA"}
1
+ {"version":3,"file":"basic-auth-provider.js","sourceRoot":"","sources":["../../src/authentication-providers/basic-auth-provider.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,gBAAuE,EAC/C,EAAE,CAAC,CAAC;IAC5B,IAAI,EAAE,YAAY;IAClB,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAA;QAChD,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAA;QAClD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC1E,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAA;QAC7C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAA;QAC9C,OAAO,MAAM,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACnD,CAAC;CACF,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-auth-provider.d.ts","sourceRoot":"","sources":["../../src/authentication-providers/cookie-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAG1E;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,YAAY,MAAM,EAClB,gBAAgB,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,EACrE,eAAe,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KACjD,sBASD,CAAA"}
1
+ {"version":3,"file":"cookie-auth-provider.d.ts","sourceRoot":"","sources":["../../src/authentication-providers/cookie-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAG3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAG1E;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,YAAY,MAAM,EAClB,gBAAgB,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,EACrE,eAAe,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KACjD,sBAgBD,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { UnauthenticatedError } from '@furystack/security';
2
+ import { sessionCacheKey } from '../identity-cache-keys.js';
2
3
  import { extractSessionIdFromCookies } from './helpers.js';
3
4
  /**
4
5
  * Creates an authentication provider for cookie-based session authentication.
@@ -17,5 +18,12 @@ export const createCookieAuthProvider = (cookieName, getSessionById, getUserByNa
17
18
  throw new UnauthenticatedError();
18
19
  return await getUserByName(session.username);
19
20
  },
21
+ // The session id alone uniquely identifies the principal — caching by it is
22
+ // safe because session invalidation (logout, expiry) drops the session row,
23
+ // and the TTL on the user-resolution cache bounds staleness across nodes.
24
+ getCacheKey: (request) => {
25
+ const sessionId = extractSessionIdFromCookies(request, cookieName);
26
+ return sessionId ? sessionCacheKey(sessionId) : null;
27
+ },
20
28
  });
21
29
  //# sourceMappingURL=cookie-auth-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cookie-auth-provider.js","sourceRoot":"","sources":["../../src/authentication-providers/cookie-auth-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAG1D,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AAE1D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,UAAkB,EAClB,cAAqE,EACrE,aAAkD,EAC1B,EAAE,CAAC,CAAC;IAC5B,IAAI,EAAE,aAAa;IACnB,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9B,MAAM,SAAS,GAAG,2BAA2B,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAClE,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAA;QAC3B,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAA;QAC/C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,oBAAoB,EAAE,CAAA;QAC9C,OAAO,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9C,CAAC;CACF,CAAC,CAAA"}
1
+ {"version":3,"file":"cookie-auth-provider.js","sourceRoot":"","sources":["../../src/authentication-providers/cookie-auth-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAwB,MAAM,2BAA2B,CAAA;AAGjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AAE1D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,UAAkB,EAClB,cAAqE,EACrE,aAAkD,EAC1B,EAAE,CAAC,CAAC;IAC5B,IAAI,EAAE,aAAa;IACnB,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9B,MAAM,SAAS,GAAG,2BAA2B,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAClE,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAA;QAC3B,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAA;QAC/C,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,oBAAoB,EAAE,CAAA;QAC9C,OAAO,MAAM,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC9C,CAAC;IACD,4EAA4E;IAC5E,4EAA4E;IAC5E,0EAA0E;IAC1E,WAAW,EAAE,CAAC,OAAO,EAA0B,EAAE;QAC/C,MAAM,SAAS,GAAG,2BAA2B,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAClE,OAAO,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACtD,CAAC;CACF,CAAC,CAAA"}
@@ -1,22 +1,10 @@
1
1
  import type { RequestAction } from './request-action-implementation.js';
2
2
  /**
3
- * Higher-order function that wraps a request action to require specific roles.
4
- * If the user does not have the required roles, throws an AuthorizationError.
5
- * Includes a random delay on unauthorized requests to mitigate timing attacks.
6
- *
7
- * @param roles - The roles required to access the endpoint
8
- * @returns A function that wraps the provided action with authorization check
9
- * @example
10
- * ```ts
11
- * // Require 'admin' role
12
- * const adminEndpoint = Authorize('admin')(myEndpoint)
13
- *
14
- * // Require multiple roles (user must have ALL specified roles)
15
- * const superAdminEndpoint = Authorize('admin', 'superuser')(myEndpoint)
16
- *
17
- * // Chain with Authenticate for authentication + authorization
18
- * const protectedEndpoint = Authenticate()(Authorize('admin')(myEndpoint))
19
- * ```
3
+ * Wraps a {@link RequestAction} to require **all** specified roles. Throws
4
+ * {@link AuthorizationError} (translated to 403 by `ErrorAction`) when any
5
+ * role is missing. Adds a 0–1 s random delay on rejection to mitigate
6
+ * timing attacks. Compose with {@link Authenticate} to require both:
7
+ * `Authenticate()(Authorize('admin')(myEndpoint))`.
20
8
  */
21
9
  export declare const Authorize: (...roles: string[]) => <T extends {
22
10
  result: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../src/authorize.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAgB,aAAa,EAAwB,MAAM,oCAAoC,CAAA;AAE3G;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,SAAS,GACnB,GAAG,OAAO,MAAM,EAAE,MAClB,CAAC,SAAS;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAAE,QAAQ,aAAa,CAAC,CAAC,CAAC,KAAG,aAAa,CAAC,CAAC,CAazE,CAAA"}
1
+ {"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../src/authorize.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,aAAa,EAAwB,MAAM,oCAAoC,CAAA;AAE3G;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GACnB,GAAG,OAAO,MAAM,EAAE,MAClB,CAAC,SAAS;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAAE,QAAQ,aAAa,CAAC,CAAC,CAAC,KAAG,aAAa,CAAC,CAAC,CAazE,CAAA"}