@furystack/rest-service 11.0.7 → 12.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/README.md +3 -2
  3. package/esm/actions/index.d.ts +1 -0
  4. package/esm/actions/index.d.ts.map +1 -1
  5. package/esm/actions/index.js +1 -0
  6. package/esm/actions/index.js.map +1 -1
  7. package/esm/actions/login.d.ts +7 -3
  8. package/esm/actions/login.d.ts.map +1 -1
  9. package/esm/actions/login.js +11 -5
  10. package/esm/actions/login.js.map +1 -1
  11. package/esm/actions/password-login-action.d.ts +24 -0
  12. package/esm/actions/password-login-action.d.ts.map +1 -0
  13. package/esm/actions/password-login-action.js +31 -0
  14. package/esm/actions/password-login-action.js.map +1 -0
  15. package/esm/actions/password-login-action.spec.d.ts +2 -0
  16. package/esm/actions/password-login-action.spec.d.ts.map +1 -0
  17. package/esm/actions/password-login-action.spec.js +105 -0
  18. package/esm/actions/password-login-action.spec.js.map +1 -0
  19. package/esm/authenticate.d.ts.map +1 -1
  20. package/esm/authenticate.js +4 -1
  21. package/esm/authenticate.js.map +1 -1
  22. package/esm/authenticate.spec.js +6 -4
  23. package/esm/authenticate.spec.js.map +1 -1
  24. package/esm/authentication-providers/authentication-provider.d.ts +25 -0
  25. package/esm/authentication-providers/authentication-provider.d.ts.map +1 -0
  26. package/esm/authentication-providers/authentication-provider.js +2 -0
  27. package/esm/authentication-providers/authentication-provider.js.map +1 -0
  28. package/esm/authentication-providers/basic-auth-provider.d.ts +8 -0
  29. package/esm/authentication-providers/basic-auth-provider.d.ts.map +1 -0
  30. package/esm/authentication-providers/basic-auth-provider.js +18 -0
  31. package/esm/authentication-providers/basic-auth-provider.js.map +1 -0
  32. package/esm/authentication-providers/cookie-auth-provider.d.ts +11 -0
  33. package/esm/authentication-providers/cookie-auth-provider.d.ts.map +1 -0
  34. package/esm/authentication-providers/cookie-auth-provider.js +21 -0
  35. package/esm/authentication-providers/cookie-auth-provider.js.map +1 -0
  36. package/esm/authentication-providers/helpers.d.ts +11 -0
  37. package/esm/authentication-providers/helpers.d.ts.map +1 -0
  38. package/esm/authentication-providers/helpers.js +47 -0
  39. package/esm/authentication-providers/helpers.js.map +1 -0
  40. package/esm/authentication-providers/index.d.ts +5 -0
  41. package/esm/authentication-providers/index.d.ts.map +1 -0
  42. package/esm/authentication-providers/index.js +5 -0
  43. package/esm/authentication-providers/index.js.map +1 -0
  44. package/esm/endpoint-generators/utils.d.ts.map +1 -1
  45. package/esm/endpoint-generators/utils.js +4 -1
  46. package/esm/endpoint-generators/utils.js.map +1 -1
  47. package/esm/helpers.d.ts +5 -2
  48. package/esm/helpers.d.ts.map +1 -1
  49. package/esm/helpers.js +27 -3
  50. package/esm/helpers.js.map +1 -1
  51. package/esm/helpers.spec.js +37 -0
  52. package/esm/helpers.spec.js.map +1 -1
  53. package/esm/http-authentication-settings.d.ts +11 -4
  54. package/esm/http-authentication-settings.d.ts.map +1 -1
  55. package/esm/http-authentication-settings.js +9 -2
  56. package/esm/http-authentication-settings.js.map +1 -1
  57. package/esm/http-user-context.d.ts +9 -4
  58. package/esm/http-user-context.d.ts.map +1 -1
  59. package/esm/http-user-context.js +28 -55
  60. package/esm/http-user-context.js.map +1 -1
  61. package/esm/http-user-context.spec.d.ts +3 -1
  62. package/esm/http-user-context.spec.d.ts.map +1 -1
  63. package/esm/http-user-context.spec.js +103 -45
  64. package/esm/http-user-context.spec.js.map +1 -1
  65. package/esm/index.d.ts +2 -0
  66. package/esm/index.d.ts.map +1 -1
  67. package/esm/index.js +2 -0
  68. package/esm/index.js.map +1 -1
  69. package/esm/login-response-strategy.d.ts +28 -0
  70. package/esm/login-response-strategy.d.ts.map +1 -0
  71. package/esm/login-response-strategy.js +28 -0
  72. package/esm/login-response-strategy.js.map +1 -0
  73. package/esm/login-response-strategy.spec.d.ts +2 -0
  74. package/esm/login-response-strategy.spec.d.ts.map +1 -0
  75. package/esm/login-response-strategy.spec.js +78 -0
  76. package/esm/login-response-strategy.spec.js.map +1 -0
  77. package/esm/rest-service.integration.spec.d.ts.map +1 -1
  78. package/esm/rest-service.integration.spec.js +5 -4
  79. package/esm/rest-service.integration.spec.js.map +1 -1
  80. package/esm/validate.integration.spec.js +5 -0
  81. package/esm/validate.integration.spec.js.map +1 -1
  82. package/package.json +7 -7
  83. package/src/actions/index.ts +1 -0
  84. package/src/actions/login.ts +12 -6
  85. package/src/actions/password-login-action.spec.ts +122 -0
  86. package/src/actions/password-login-action.ts +35 -0
  87. package/src/authenticate.spec.ts +6 -4
  88. package/src/authenticate.ts +4 -1
  89. package/src/authentication-providers/authentication-provider.ts +25 -0
  90. package/src/authentication-providers/basic-auth-provider.ts +21 -0
  91. package/src/authentication-providers/cookie-auth-provider.ts +26 -0
  92. package/src/authentication-providers/helpers.ts +73 -0
  93. package/src/authentication-providers/index.ts +4 -0
  94. package/src/endpoint-generators/utils.ts +4 -1
  95. package/src/helpers.spec.ts +40 -0
  96. package/src/helpers.ts +48 -3
  97. package/src/http-authentication-settings.ts +30 -21
  98. package/src/http-user-context.spec.ts +462 -394
  99. package/src/http-user-context.ts +164 -194
  100. package/src/index.ts +2 -0
  101. package/src/login-response-strategy.spec.ts +90 -0
  102. package/src/login-response-strategy.ts +48 -0
  103. package/src/rest-service.integration.spec.ts +5 -4
  104. package/src/validate.integration.spec.ts +5 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,125 @@
1
1
  # Changelog
2
2
 
3
+ ## [12.1.0] - 2026-02-26
4
+
5
+ ### 🔧 Chores
6
+
7
+ - Normalized line endings in `http-user-context.ts`, `http-authentication-settings.ts`, and related spec files
8
+
9
+ ### ✨ Features
10
+
11
+ ### `LoginResponseStrategy<TResult>` type
12
+
13
+ New pluggable type that decouples login actions from session/token creation. A strategy turns an authenticated `User` into an `ActionResult<TResult>` — the generic parameter flows through to the action's return type for full type inference.
14
+
15
+ ```typescript
16
+ import type { LoginResponseStrategy } from '@furystack/rest-service'
17
+
18
+ type LoginResponseStrategy<TResult> = {
19
+ createLoginResponse: (user: User, injector: Injector) => Promise<ActionResult<TResult>>
20
+ }
21
+ ```
22
+
23
+ ### `createCookieLoginStrategy(injector)`
24
+
25
+ Factory that creates a cookie-based `LoginResponseStrategy<User>`. On login it generates a random session ID, persists it in the session DataSet, and returns the user with a `Set-Cookie` header.
26
+
27
+ ```typescript
28
+ import { createCookieLoginStrategy } from '@furystack/rest-service'
29
+
30
+ const cookieStrategy = createCookieLoginStrategy(injector)
31
+ // cookieStrategy.createLoginResponse(user, injector) → ActionResult<User> with Set-Cookie header
32
+ ```
33
+
34
+ ### `createPasswordLoginAction(strategy)`
35
+
36
+ Factory that creates a password-based login `RequestAction`. Authenticates via `HttpUserContext.authenticateUser()` then delegates session/token creation to the provided strategy. Includes timing-attack mitigation on failure.
37
+
38
+ ```typescript
39
+ import { createPasswordLoginAction, createCookieLoginStrategy } from '@furystack/rest-service'
40
+
41
+ const cookieStrategy = createCookieLoginStrategy(injector)
42
+ const loginAction = createPasswordLoginAction(cookieStrategy)
43
+ // loginAction: RequestAction<{ result: User; body: { username: string; password: string } }>
44
+ ```
45
+
46
+ ### 🧪 Tests
47
+
48
+ - Added `login-response-strategy.spec.ts` — tests cookie strategy session creation, Set-Cookie headers, session persistence, and session ID uniqueness
49
+ - Added `password-login-action.spec.ts` — tests strategy delegation, user forwarding, auth failure handling, and custom strategy result types
50
+
51
+ ### ⬆️ Dependencies
52
+
53
+ - Bumped `@types/node` from ^25.3.0 to ^25.3.1
54
+
55
+ ## [12.0.0] - 2026-02-26
56
+
57
+ ### ✨ Features
58
+
59
+ ### Pluggable Authentication Provider System
60
+
61
+ Introduced the `AuthenticationProvider` type and refactored `HttpUserContext.authenticateRequest()` to iterate an ordered provider chain instead of hardcoded Basic Auth and Cookie Auth logic.
62
+
63
+ - `AuthenticationProvider` - Type for pluggable authentication providers. Each provider returns `User` on success, `null` if it doesn't apply, or throws on auth failure.
64
+ - `createBasicAuthProvider()` - Factory that extracts the existing Basic Auth logic into a standalone provider
65
+ - `createCookieAuthProvider()` - Factory that extracts the existing Cookie Auth logic into a standalone provider
66
+ - `HttpAuthenticationSettings.authenticationProviders` - Ordered list of providers, populated by `useHttpAuthentication()` and extensible by auth plugins like `useJwtAuthentication()`
67
+
68
+ **Usage:**
69
+
70
+ ```typescript
71
+ useHttpAuthentication(injector, {
72
+ enableBasicAuth: true,
73
+ authenticationProviders: [myCustomProvider],
74
+ })
75
+ ```
76
+
77
+ Custom providers are appended after the built-in Basic Auth and Cookie Auth providers.
78
+
79
+ ### ♻️ Refactoring
80
+
81
+ - `useHttpAuthentication()` now eagerly resolves `PasswordAuthenticator` and store dependencies at setup time, constructing providers with resolved instances rather than passing the `Injector`
82
+ - `Authenticate()` middleware checks for a registered `'basic-auth'` provider by name instead of reading the `enableBasicAuth` flag when deciding whether to include the `WWW-Authenticate: Basic` response header
83
+ - Extracted shared store lookup helpers (`authenticateUserWithDataSet`, `findSessionById`, `findUserByName`, `extractSessionIdFromCookies`) into `authentication-providers/helpers.ts`
84
+
85
+ ### 💥 Breaking Changes
86
+
87
+ - `HttpAuthenticationSettings.getUserStore(StoreManager)` → `getUserDataSet(Injector)` — now returns a `DataSet` instead of a `PhysicalStore`
88
+ - `HttpAuthenticationSettings.getSessionStore(StoreManager)` → `getSessionDataSet(Injector)`
89
+ - `HttpUserContext.getUserStore()` → `getUserDataSet()`
90
+ - `HttpUserContext.getSessionStore()` → `getSessionDataSet()`
91
+ - `authenticateUserWithStore()` → `authenticateUserWithDataSet()` — renamed helper with updated signature
92
+ - `useHttpAuthentication()` now requires DataSets for `User` and `DefaultSession` to be registered via `getRepository(injector).createDataSet()` before calling
93
+
94
+ ### 🔄 Migration
95
+
96
+ **Setup:**
97
+
98
+ ```typescript
99
+ // Before
100
+ useHttpAuthentication(injector, {
101
+ getUserStore: (sm) => sm.getStoreFor(User, 'username'),
102
+ getSessionStore: (sm) => sm.getStoreFor(DefaultSession, 'sessionId'),
103
+ })
104
+
105
+ // After — register DataSets first, defaults resolve them automatically
106
+ getRepository(injector).createDataSet(User, 'username')
107
+ getRepository(injector).createDataSet(DefaultSession, 'sessionId')
108
+ useHttpAuthentication(injector)
109
+ ```
110
+
111
+ **Custom store accessors:**
112
+
113
+ ```typescript
114
+ // Before
115
+ settings.getUserStore(storeManager)
116
+ settings.getSessionStore(storeManager)
117
+
118
+ // After
119
+ settings.getUserDataSet(injector)
120
+ settings.getSessionDataSet(injector)
121
+ ```
122
+
3
123
  ## [11.0.7] - 2026-02-22
4
124
 
5
125
  ### ⬆️ Dependencies
package/README.md CHANGED
@@ -210,14 +210,15 @@ You can use the built-in authentication that comes with this package. It contain
210
210
  ```ts
211
211
  import { useHttpAuthentication, useRestService } from '@furystack/rest-service'
212
212
  import { Injector } from '@furystack/inject'
213
+ import { getDataSetFor } from '@furystack/repository'
213
214
 
214
215
  const myInjector = new Injector()
215
216
  useHttpAuthentication(myInjector, {
216
217
  cookieName: 'sessionId', // The session ID will be stored in this cookie
217
218
  enableBasicAuth: true, // Enables / disables standard Basic Authentication
218
219
  model: ApplicationUserModel, // The custom User model. Should implement `User`
219
- getUserStore: (storeManager) => storeManager.getStoreFor(ApplicationUserModel, 'username'), // Callback to retrieve the User Store
220
- getSessionStore: (storeManager) => storeManager.getStoreFor(MySessionModel, 'sessionId'), // Callback to retrieve the Session Store
220
+ getUserDataSet: (injector) => getDataSetFor(injector, ApplicationUserModel, 'username'),
221
+ getSessionDataSet: (injector) => getDataSetFor(injector, MySessionModel, 'sessionId'),
221
222
  })
222
223
  await useRestService<MyApi>({ injector: myInjector, ...apiOptions })
223
224
  ```
@@ -4,4 +4,5 @@ export * from './is-authenticated.js';
4
4
  export * from './login.js';
5
5
  export * from './logout.js';
6
6
  export * from './not-found-action.js';
7
+ export * from './password-login-action.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA"}
@@ -4,4 +4,5 @@ export * from './is-authenticated.js';
4
4
  export * from './login.js';
5
5
  export * from './logout.js';
6
6
  export * from './not-found-action.js';
7
+ export * from './password-login-action.js';
7
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/actions/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA"}
@@ -1,9 +1,13 @@
1
1
  import type { User } from '@furystack/core';
2
2
  import type { RequestAction } from '../request-action-implementation.js';
3
3
  /**
4
- * Action that logs in the current user
5
- * Should be called with a JSON Post body with ``username`` and ``password`` fields.
6
- * Returns the current user instance
4
+ * Action that logs in the current user.
5
+ * Should be called with a JSON POST body with `username` and `password` fields.
6
+ * Returns the current user instance.
7
+ *
8
+ * @deprecated Use `createPasswordLoginAction(createCookieLoginStrategy(injector))` instead.
9
+ * This static action resolves services from the request-scoped injector on
10
+ * every call; the factory approach captures them once at setup time.
7
11
  */
8
12
  export declare const LoginAction: RequestAction<{
9
13
  result: User;
@@ -1 +1 @@
1
- {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;;GAIG;AAEH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IACtC,MAAM,EAAE,IAAI,CAAA;IACZ,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC7C,CAUA,CAAA"}
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAK3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AAGxE;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC;IACtC,MAAM,EAAE,IAAI,CAAA;IACZ,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC7C,CAWA,CAAA"}
@@ -1,10 +1,15 @@
1
- import { HttpUserContext } from '../http-user-context.js';
2
1
  import { RequestError } from '@furystack/rest';
2
+ import { sleepAsync } from '@furystack/utils';
3
+ import { HttpUserContext } from '../http-user-context.js';
3
4
  import { JsonResult } from '../request-action-implementation.js';
4
5
  /**
5
- * Action that logs in the current user
6
- * Should be called with a JSON Post body with ``username`` and ``password`` fields.
7
- * Returns the current user instance
6
+ * Action that logs in the current user.
7
+ * Should be called with a JSON POST body with `username` and `password` fields.
8
+ * Returns the current user instance.
9
+ *
10
+ * @deprecated Use `createPasswordLoginAction(createCookieLoginStrategy(injector))` instead.
11
+ * This static action resolves services from the request-scoped injector on
12
+ * every call; the factory approach captures them once at setup time.
8
13
  */
9
14
  export const LoginAction = async ({ injector, getBody, response }) => {
10
15
  const userContext = injector.getInstance(HttpUserContext);
@@ -14,7 +19,8 @@ export const LoginAction = async ({ injector, getBody, response }) => {
14
19
  await userContext.cookieLogin(user, response);
15
20
  return JsonResult(user, 200);
16
21
  }
17
- catch (error) {
22
+ catch {
23
+ await sleepAsync(Math.random() * 1000);
18
24
  throw new RequestError('Login Failed', 400);
19
25
  }
20
26
  };
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE;;;;GAIG;AAEH,MAAM,CAAC,MAAM,WAAW,GAGnB,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAA;IACzD,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;IAC5B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC7E,MAAM,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC7C,OAAO,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/actions/login.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAEzD,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAEhE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAGnB,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAA;IACzD,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;IAC5B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC7E,MAAM,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC7C,OAAO,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;QACtC,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC,CAAA"}
@@ -0,0 +1,24 @@
1
+ import type { LoginResponseStrategy } from '../login-response-strategy.js';
2
+ import type { RequestAction } from '../request-action-implementation.js';
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
16
+ */
17
+ export declare const createPasswordLoginAction: <TResult>(strategy: LoginResponseStrategy<TResult>) => RequestAction<{
18
+ result: TResult;
19
+ body: {
20
+ username: string;
21
+ password: string;
22
+ };
23
+ }>;
24
+ //# sourceMappingURL=password-login-action.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,31 @@
1
+ import { RequestError } from '@furystack/rest';
2
+ import { sleepAsync } from '@furystack/utils';
3
+ import { HttpUserContext } from '../http-user-context.js';
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
17
+ */
18
+ export const createPasswordLoginAction = (strategy) => {
19
+ return async ({ injector, getBody }) => {
20
+ const body = await getBody();
21
+ try {
22
+ const user = await injector.getInstance(HttpUserContext).authenticateUser(body.username, body.password);
23
+ return strategy.createLoginResponse(user, injector);
24
+ }
25
+ catch {
26
+ await sleepAsync(Math.random() * 1000);
27
+ throw new RequestError('Login Failed', 400);
28
+ }
29
+ };
30
+ };
31
+ //# sourceMappingURL=password-login-action.js.map
@@ -0,0 +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,WAAW,CAAC,eAAe,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;YACvG,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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=password-login-action.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-login-action.spec.d.ts","sourceRoot":"","sources":["../../src/actions/password-login-action.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
1
+ import { InMemoryStore, StoreManager, User, addStore } from '@furystack/core';
2
+ import { Injector } from '@furystack/inject';
3
+ import { getRepository } from '@furystack/repository';
4
+ import { PasswordAuthenticator, PasswordCredential, PasswordResetToken, usePasswordPolicy } from '@furystack/security';
5
+ import { usingAsync } from '@furystack/utils';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import { useHttpAuthentication } from '../helpers.js';
8
+ import { DefaultSession } from '../models/default-session.js';
9
+ import { JsonResult } from '../request-action-implementation.js';
10
+ import { createPasswordLoginAction } from './password-login-action.js';
11
+ const setupInjector = async (i, username, password) => {
12
+ addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' }))
13
+ .addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
14
+ .addStore(new InMemoryStore({ model: PasswordCredential, primaryKey: 'userName' }))
15
+ .addStore(new InMemoryStore({ model: PasswordResetToken, primaryKey: 'token' }));
16
+ const repo = getRepository(i);
17
+ repo.createDataSet(User, 'username');
18
+ repo.createDataSet(DefaultSession, 'sessionId');
19
+ repo.createDataSet(PasswordCredential, 'userName');
20
+ repo.createDataSet(PasswordResetToken, 'token');
21
+ usePasswordPolicy(i);
22
+ useHttpAuthentication(i);
23
+ const sm = i.getInstance(StoreManager);
24
+ const pw = i.getInstance(PasswordAuthenticator);
25
+ const cred = await pw.hasher.createCredential(username, password);
26
+ await sm.getStoreFor(PasswordCredential, 'userName').add(cred);
27
+ await sm.getStoreFor(User, 'username').add({ username, roles: ['admin'] });
28
+ };
29
+ const mockStrategy = {
30
+ createLoginResponse: vi.fn(async (user) => JsonResult(user, 200)),
31
+ };
32
+ describe('createPasswordLoginAction', () => {
33
+ const request = {};
34
+ const response = {};
35
+ it('Should delegate to the strategy on successful authentication', async () => {
36
+ await usingAsync(new Injector(), async (i) => {
37
+ await setupInjector(i, 'testuser', 'testpass');
38
+ const action = createPasswordLoginAction(mockStrategy);
39
+ const result = await action({
40
+ injector: i,
41
+ request,
42
+ response,
43
+ getBody: () => Promise.resolve({ username: 'testuser', password: 'testpass' }),
44
+ });
45
+ expect(mockStrategy.createLoginResponse).toHaveBeenCalled();
46
+ expect(result.chunk.username).toBe('testuser');
47
+ });
48
+ });
49
+ it('Should pass the correct user to the strategy', async () => {
50
+ await usingAsync(new Injector(), async (i) => {
51
+ await setupInjector(i, 'testuser', 'testpass');
52
+ const strategyFn = vi.fn(async (user) => JsonResult(user, 200));
53
+ const strategy = { createLoginResponse: strategyFn };
54
+ const action = createPasswordLoginAction(strategy);
55
+ await action({
56
+ injector: i,
57
+ request,
58
+ response,
59
+ getBody: () => Promise.resolve({ username: 'testuser', password: 'testpass' }),
60
+ });
61
+ expect(strategyFn).toHaveBeenCalledWith(expect.objectContaining({ username: 'testuser', roles: ['admin'] }), i);
62
+ });
63
+ });
64
+ it('Should throw RequestError on invalid credentials', async () => {
65
+ await usingAsync(new Injector(), async (i) => {
66
+ await setupInjector(i, 'testuser', 'testpass');
67
+ const action = createPasswordLoginAction(mockStrategy);
68
+ await expect(action({
69
+ injector: i,
70
+ request,
71
+ response,
72
+ getBody: () => Promise.resolve({ username: 'testuser', password: 'wrongpass' }),
73
+ })).rejects.toThrow('Login Failed');
74
+ });
75
+ });
76
+ it('Should throw RequestError for nonexistent user', async () => {
77
+ await usingAsync(new Injector(), async (i) => {
78
+ await setupInjector(i, 'testuser', 'testpass');
79
+ const action = createPasswordLoginAction(mockStrategy);
80
+ await expect(action({
81
+ injector: i,
82
+ request,
83
+ response,
84
+ getBody: () => Promise.resolve({ username: 'nobody', password: 'nopass' }),
85
+ })).rejects.toThrow('Login Failed');
86
+ });
87
+ });
88
+ it('Should work with a custom result type from strategy', async () => {
89
+ await usingAsync(new Injector(), async (i) => {
90
+ await setupInjector(i, 'testuser', 'testpass');
91
+ const tokenStrategy = {
92
+ createLoginResponse: async () => JsonResult({ accessToken: 'tok123' }, 200),
93
+ };
94
+ const action = createPasswordLoginAction(tokenStrategy);
95
+ const result = await action({
96
+ injector: i,
97
+ request,
98
+ response,
99
+ getBody: () => Promise.resolve({ username: 'testuser', password: 'testpass' }),
100
+ });
101
+ expect(result.chunk.accessToken).toBe('tok123');
102
+ });
103
+ });
104
+ });
105
+ //# sourceMappingURL=password-login-action.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-login-action.spec.js","sourceRoot":"","sources":["../../src/actions/password-login-action.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACtH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAEjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAChE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAA;AAEtE,MAAM,aAAa,GAAG,KAAK,EAAE,CAAW,EAAE,QAAgB,EAAE,QAAgB,EAAE,EAAE;IAC9E,QAAQ,CAAC,CAAC,EAAE,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;SACpE,QAAQ,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;SAC/E,QAAQ,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;SAClF,QAAQ,CAAC,IAAI,aAAa,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;IAElF,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;IAC7B,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IACpC,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE,WAAW,CAAC,CAAA;IAC/C,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAA;IAClD,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAA;IAE/C,iBAAiB,CAAC,CAAC,CAAC,CAAA;IACpB,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAExB,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,CAAA;IACtC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAA;IAC/C,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IACjE,MAAM,EAAE,CAAC,WAAW,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC9D,MAAM,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;AAC5E,CAAC,CAAA;AAED,MAAM,YAAY,GAAgC;IAChD,mBAAmB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,IAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;CACxE,CAAA;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,MAAM,OAAO,GAAG,EAAqB,CAAA;IACrC,MAAM,QAAQ,GAAG,EAAoB,CAAA;IAErC,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;YAC9C,MAAM,MAAM,GAAG,yBAAyB,CAAC,YAAY,CAAC,CAAA;YACtD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC;gBAC1B,QAAQ,EAAE,CAAC;gBACX,OAAO;gBACP,QAAQ;gBACR,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;aAC/E,CAAC,CAAA;YACF,MAAM,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC,gBAAgB,EAAE,CAAA;YAC3D,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAChD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;YAC9C,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,IAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;YACrE,MAAM,QAAQ,GAAgC,EAAE,mBAAmB,EAAE,UAAU,EAAE,CAAA;YACjF,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAA;YAClD,MAAM,MAAM,CAAC;gBACX,QAAQ,EAAE,CAAC;gBACX,OAAO;gBACP,QAAQ;gBACR,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;aAC/E,CAAC,CAAA;YACF,MAAM,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QACjH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;YAC9C,MAAM,MAAM,GAAG,yBAAyB,CAAC,YAAY,CAAC,CAAA;YACtD,MAAM,MAAM,CACV,MAAM,CAAC;gBACL,QAAQ,EAAE,CAAC;gBACX,OAAO;gBACP,QAAQ;gBACR,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;aAChF,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;YAC9C,MAAM,MAAM,GAAG,yBAAyB,CAAC,YAAY,CAAC,CAAA;YACtD,MAAM,MAAM,CACV,MAAM,CAAC;gBACL,QAAQ,EAAE,CAAC;gBACX,OAAO;gBACP,QAAQ;gBACR,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;aAC3E,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,UAAU,CAAC,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAA;YAC9C,MAAM,aAAa,GAAmD;gBACpE,mBAAmB,EAAE,KAAK,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC;aAC5E,CAAA;YACD,MAAM,MAAM,GAAG,yBAAyB,CAAC,aAAa,CAAC,CAAA;YACvD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC;gBAC1B,QAAQ,EAAE,CAAC;gBACX,OAAO;gBACP,QAAQ;gBACR,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;aAC/E,CAAC,CAAA;YACF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -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,CAkBzE,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;;;;;;;;;;;;;;;;;;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"}
@@ -27,7 +27,10 @@ export const Authenticate = () => (action) => {
27
27
  const authenticated = await isAuthenticated(injector);
28
28
  if (!authenticated) {
29
29
  await sleepAsync(Math.random() * 1000);
30
- return JsonResult({ error: 'unauthorized' }, 401, injector.getInstance(HttpUserContext).authentication.enableBasicAuth ? { 'WWW-Authenticate': 'Basic' } : {});
30
+ const hasBasicAuth = injector
31
+ .getInstance(HttpUserContext)
32
+ .authentication.authenticationProviders.some((p) => p.name === 'basic-auth');
33
+ return JsonResult({ error: 'unauthorized' }, 401, hasBasicAuth ? { 'WWW-Authenticate': 'Basic' } : {});
31
34
  }
32
35
  return (await action(args));
33
36
  };
@@ -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,OAAO,UAAU,CACf,EAAE,KAAK,EAAE,cAAc,EAAE,EACzB,GAAG,EACH,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAC9E,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;;;;;;;;;;;;;;;;;;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,WAAW,CAAC,eAAe,CAAC;iBAC5B,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"}
@@ -8,12 +8,12 @@ import { EmptyResult } from './request-action-implementation.js';
8
8
  describe('Authenticate', () => {
9
9
  const response = {};
10
10
  const request = { url: 'http://google.com' };
11
- it('Should return 403 w/o basic auth header, when unauthorized and basic auth is disabled', async () => {
11
+ it('Should return 401 without WWW-Authenticate header when no basic-auth provider is registered', async () => {
12
12
  await usingAsync(new Injector(), async (i) => {
13
13
  const isAuthenticatedAction = vi.fn(async () => false);
14
14
  i.setExplicitInstance({ isAuthenticated: isAuthenticatedAction, getCurrentUser: async () => Promise.reject(new Error(':(')) }, IdentityContext);
15
15
  i.setExplicitInstance({
16
- authentication: { enableBasicAuth: false },
16
+ authentication: { authenticationProviders: [] },
17
17
  }, HttpUserContext);
18
18
  const exampleAuthenticatedAction = vi.fn(async () => EmptyResult());
19
19
  const authorized = Authenticate()(exampleAuthenticatedAction);
@@ -24,13 +24,15 @@ describe('Authenticate', () => {
24
24
  expect(exampleAuthenticatedAction).not.toBeCalled();
25
25
  });
26
26
  });
27
- it('Should return 403 with basic auth headers when unauthorized and basic auth is enabled', async () => {
27
+ it('Should return 401 with WWW-Authenticate: Basic header when basic-auth provider is registered', async () => {
28
28
  await usingAsync(new Injector(), async (i) => {
29
29
  const isAuthenticatedAction = vi.fn(async () => false);
30
30
  i.setExplicitInstance({
31
31
  isAuthenticated: isAuthenticatedAction,
32
32
  getCurrentUser: async () => Promise.reject(new Error(':(')),
33
- authentication: { enableBasicAuth: true },
33
+ authentication: {
34
+ authenticationProviders: [{ name: 'basic-auth', authenticate: async () => null }],
35
+ },
34
36
  }, HttpUserContext);
35
37
  const exampleAuthenticatedAction = vi.fn(async () => EmptyResult());
36
38
  const authorized = Authenticate()(exampleAuthenticatedAction);
@@ -1 +1 @@
1
- {"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../src/authenticate.spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"authenticate.spec.js","sourceRoot":"","sources":["../src/authenticate.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { User } from '@furystack/core';
2
+ import type { IncomingMessage } from 'http';
3
+ /**
4
+ * Interface for pluggable authentication providers.
5
+ *
6
+ * Each provider attempts to authenticate an HTTP request using
7
+ * a specific mechanism (e.g. Basic Auth, Cookie, JWT Bearer).
8
+ *
9
+ * **Error handling contract:**
10
+ * - Returns `User` if authentication succeeded.
11
+ * - Returns `null` if this provider does not apply to the request
12
+ * (e.g. no relevant header present). The next provider in the chain will be tried.
13
+ * - Throws if the provider applies but authentication fails due to
14
+ * bad credentials or an infrastructure error (DB down, etc.).
15
+ * Throwing skips remaining providers and results in a 401/500.
16
+ */
17
+ export type AuthenticationProvider = {
18
+ /**
19
+ * Identifies the provider for debugging and logging purposes only.
20
+ * Not used for deduplication or lookup.
21
+ */
22
+ readonly name: string;
23
+ authenticate: (request: Pick<IncomingMessage, 'headers'>) => Promise<User | null>;
24
+ };
25
+ //# sourceMappingURL=authentication-provider.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=authentication-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authentication-provider.js","sourceRoot":"","sources":["../../src/authentication-providers/authentication-provider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ import type { User } from '@furystack/core';
2
+ import type { AuthenticationProvider } from './authentication-provider.js';
3
+ /**
4
+ * Creates an authentication provider for HTTP Basic Authentication.
5
+ * @param authenticateUser Callback that verifies credentials and returns a User or throws
6
+ */
7
+ export declare const createBasicAuthProvider: (authenticateUser: (username: string, password: string) => Promise<User>) => AuthenticationProvider;
8
+ //# sourceMappingURL=basic-auth-provider.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Creates an authentication provider for HTTP Basic Authentication.
3
+ * @param authenticateUser Callback that verifies credentials and returns a User or throws
4
+ */
5
+ export const createBasicAuthProvider = (authenticateUser) => ({
6
+ name: 'basic-auth',
7
+ authenticate: async (request) => {
8
+ const authHeader = request.headers.authorization;
9
+ if (!authHeader?.startsWith('Basic '))
10
+ return null;
11
+ const decoded = Buffer.from(authHeader.split(' ')[1], 'base64').toString();
12
+ const colonIndex = decoded.indexOf(':');
13
+ const userName = decoded.slice(0, colonIndex);
14
+ const password = decoded.slice(colonIndex + 1);
15
+ return await authenticateUser(userName, password);
16
+ },
17
+ });
18
+ //# sourceMappingURL=basic-auth-provider.js.map
@@ -0,0 +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"}
@@ -0,0 +1,11 @@
1
+ import type { User } from '@furystack/core';
2
+ import type { DefaultSession } from '../models/default-session.js';
3
+ import type { AuthenticationProvider } from './authentication-provider.js';
4
+ /**
5
+ * Creates an authentication provider for cookie-based session authentication.
6
+ * @param cookieName The name of the session cookie
7
+ * @param getSessionById Callback to find a session by ID
8
+ * @param getUserByName Callback to find a user by username
9
+ */
10
+ export declare const createCookieAuthProvider: (cookieName: string, getSessionById: (sessionId: string) => Promise<DefaultSession | null>, getUserByName: (username: string) => Promise<User>) => AuthenticationProvider;
11
+ //# sourceMappingURL=cookie-auth-provider.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,21 @@
1
+ import { UnauthenticatedError } from '@furystack/security';
2
+ import { extractSessionIdFromCookies } from './helpers.js';
3
+ /**
4
+ * Creates an authentication provider for cookie-based session authentication.
5
+ * @param cookieName The name of the session cookie
6
+ * @param getSessionById Callback to find a session by ID
7
+ * @param getUserByName Callback to find a user by username
8
+ */
9
+ export const createCookieAuthProvider = (cookieName, getSessionById, getUserByName) => ({
10
+ name: 'cookie-auth',
11
+ authenticate: async (request) => {
12
+ const sessionId = extractSessionIdFromCookies(request, cookieName);
13
+ if (!sessionId)
14
+ return null;
15
+ const session = await getSessionById(sessionId);
16
+ if (!session)
17
+ throw new UnauthenticatedError();
18
+ return await getUserByName(session.username);
19
+ },
20
+ });
21
+ //# sourceMappingURL=cookie-auth-provider.js.map
@@ -0,0 +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"}