@furystack/rest-service 12.3.5 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +47 -28
- package/esm/actions/get-current-user.spec.js +10 -6
- package/esm/actions/get-current-user.spec.js.map +1 -1
- package/esm/actions/index.d.ts +0 -1
- package/esm/actions/index.d.ts.map +1 -1
- package/esm/actions/index.js +0 -1
- package/esm/actions/index.js.map +1 -1
- package/esm/actions/is-authenticated.spec.js +10 -6
- package/esm/actions/is-authenticated.spec.js.map +1 -1
- package/esm/actions/logout-action.spec.js +11 -9
- package/esm/actions/logout-action.spec.js.map +1 -1
- package/esm/actions/logout.js +1 -1
- package/esm/actions/logout.js.map +1 -1
- package/esm/actions/password-login-action.js +1 -1
- package/esm/actions/password-login-action.js.map +1 -1
- package/esm/actions/password-login-action.spec.js +23 -28
- package/esm/actions/password-login-action.spec.js.map +1 -1
- package/esm/authenticate.js +1 -1
- package/esm/authenticate.js.map +1 -1
- package/esm/authenticate.spec.js +41 -33
- package/esm/authenticate.spec.js.map +1 -1
- package/esm/authorize.spec.js +34 -29
- package/esm/authorize.spec.js.map +1 -1
- package/esm/endpoint-generators/create-delete-endpoint.d.ts +6 -11
- package/esm/endpoint-generators/create-delete-endpoint.d.ts.map +1 -1
- package/esm/endpoint-generators/create-delete-endpoint.js +7 -11
- package/esm/endpoint-generators/create-delete-endpoint.js.map +1 -1
- package/esm/endpoint-generators/create-delete-endpoint.spec.js +14 -21
- package/esm/endpoint-generators/create-delete-endpoint.spec.js.map +1 -1
- package/esm/endpoint-generators/create-get-collection-endpoint.d.ts +11 -11
- package/esm/endpoint-generators/create-get-collection-endpoint.d.ts.map +1 -1
- package/esm/endpoint-generators/create-get-collection-endpoint.js +13 -12
- package/esm/endpoint-generators/create-get-collection-endpoint.js.map +1 -1
- package/esm/endpoint-generators/create-get-collection-endpoint.spec.js +43 -70
- package/esm/endpoint-generators/create-get-collection-endpoint.spec.js.map +1 -1
- package/esm/endpoint-generators/create-get-entity-endpoint.d.ts +6 -11
- package/esm/endpoint-generators/create-get-entity-endpoint.d.ts.map +1 -1
- package/esm/endpoint-generators/create-get-entity-endpoint.js +7 -11
- package/esm/endpoint-generators/create-get-entity-endpoint.js.map +1 -1
- package/esm/endpoint-generators/create-get-entity-endpoint.spec.js +16 -33
- package/esm/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -1
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +1 -15
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -1
- package/esm/endpoint-generators/create-get-openapi-document-action.js +1 -24
- package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -1
- package/esm/endpoint-generators/create-get-schema-action.d.ts +1 -1
- package/esm/endpoint-generators/create-get-schema-action.d.ts.map +1 -1
- package/esm/endpoint-generators/create-get-schema-action.js.map +1 -1
- package/esm/endpoint-generators/create-patch-endpoint.d.ts +7 -13
- package/esm/endpoint-generators/create-patch-endpoint.d.ts.map +1 -1
- package/esm/endpoint-generators/create-patch-endpoint.js +8 -13
- package/esm/endpoint-generators/create-patch-endpoint.js.map +1 -1
- package/esm/endpoint-generators/create-patch-endpoint.spec.js +10 -17
- package/esm/endpoint-generators/create-patch-endpoint.spec.js.map +1 -1
- package/esm/endpoint-generators/create-post-endpoint.d.ts +7 -13
- package/esm/endpoint-generators/create-post-endpoint.d.ts.map +1 -1
- package/esm/endpoint-generators/create-post-endpoint.js +8 -13
- package/esm/endpoint-generators/create-post-endpoint.js.map +1 -1
- package/esm/endpoint-generators/create-post-endpoint.spec.js +41 -14
- package/esm/endpoint-generators/create-post-endpoint.spec.js.map +1 -1
- package/esm/endpoint-generators/utils.d.ts +17 -2
- package/esm/endpoint-generators/utils.d.ts.map +1 -1
- package/esm/endpoint-generators/utils.js +33 -20
- package/esm/endpoint-generators/utils.js.map +1 -1
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts +0 -2
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts.map +1 -1
- package/esm/get-schema-from-api.d.ts +1 -1
- package/esm/get-schema-from-api.d.ts.map +1 -1
- package/esm/get-schema-from-api.spec.js.map +1 -1
- package/esm/helpers.d.ts +53 -87
- package/esm/helpers.d.ts.map +1 -1
- package/esm/helpers.js +91 -92
- package/esm/helpers.js.map +1 -1
- package/esm/helpers.spec.d.ts +2 -1
- package/esm/helpers.spec.d.ts.map +1 -1
- package/esm/helpers.spec.js +41 -38
- package/esm/helpers.spec.js.map +1 -1
- package/esm/http-authentication-settings.d.ts +35 -12
- package/esm/http-authentication-settings.d.ts.map +1 -1
- package/esm/http-authentication-settings.js +21 -29
- package/esm/http-authentication-settings.js.map +1 -1
- package/esm/http-server-pool.d.ts +64 -0
- package/esm/http-server-pool.d.ts.map +1 -0
- package/esm/http-server-pool.js +95 -0
- package/esm/http-server-pool.js.map +1 -0
- package/esm/http-server-pool.spec.d.ts +2 -0
- package/esm/http-server-pool.spec.d.ts.map +1 -0
- package/esm/http-server-pool.spec.js +193 -0
- package/esm/http-server-pool.spec.js.map +1 -0
- package/esm/http-user-context.d.ts +31 -44
- package/esm/http-user-context.d.ts.map +1 -1
- package/esm/http-user-context.js +76 -95
- package/esm/http-user-context.js.map +1 -1
- package/esm/http-user-context.spec.d.ts +8 -2
- package/esm/http-user-context.spec.d.ts.map +1 -1
- package/esm/http-user-context.spec.js +189 -337
- package/esm/http-user-context.spec.js.map +1 -1
- package/esm/index.d.ts +6 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +6 -4
- package/esm/index.js.map +1 -1
- package/esm/login-response-strategy.d.ts +11 -10
- package/esm/login-response-strategy.d.ts.map +1 -1
- package/esm/login-response-strategy.js +7 -6
- package/esm/login-response-strategy.js.map +1 -1
- package/esm/login-response-strategy.spec.js +25 -29
- package/esm/login-response-strategy.spec.js.map +1 -1
- package/esm/openapi/generate-openapi-document.d.ts +0 -8
- package/esm/openapi/generate-openapi-document.d.ts.map +1 -1
- package/esm/openapi/generate-openapi-document.js +0 -2
- package/esm/openapi/generate-openapi-document.js.map +1 -1
- package/esm/proxy-manager.spec.js +152 -176
- package/esm/proxy-manager.spec.js.map +1 -1
- package/esm/proxy-runtime.d.ts +27 -0
- package/esm/proxy-runtime.d.ts.map +1 -0
- package/esm/proxy-runtime.js +45 -0
- package/esm/proxy-runtime.js.map +1 -0
- package/esm/proxy-runtime.spec.d.ts +2 -0
- package/esm/proxy-runtime.spec.d.ts.map +1 -0
- package/esm/proxy-runtime.spec.js +39 -0
- package/esm/proxy-runtime.spec.js.map +1 -0
- package/esm/rest-api-runtime.d.ts +76 -0
- package/esm/rest-api-runtime.d.ts.map +1 -0
- package/esm/rest-api-runtime.js +131 -0
- package/esm/rest-api-runtime.js.map +1 -0
- package/esm/rest-api-runtime.spec.d.ts +2 -0
- package/esm/rest-api-runtime.spec.d.ts.map +1 -0
- package/esm/rest-api-runtime.spec.js +58 -0
- package/esm/rest-api-runtime.spec.js.map +1 -0
- package/esm/rest-service.integration.spec.d.ts +1 -1
- package/esm/rest-service.integration.spec.d.ts.map +1 -1
- package/esm/rest-service.integration.spec.js +60 -82
- package/esm/rest-service.integration.spec.js.map +1 -1
- package/esm/rest.integration.test.js +26 -84
- package/esm/rest.integration.test.js.map +1 -1
- package/esm/server-telemetry.d.ts +57 -0
- package/esm/server-telemetry.d.ts.map +1 -0
- package/esm/server-telemetry.js +26 -0
- package/esm/server-telemetry.js.map +1 -0
- package/esm/server-telemetry.spec.d.ts +2 -0
- package/esm/server-telemetry.spec.d.ts.map +1 -0
- package/esm/server-telemetry.spec.js +52 -0
- package/esm/server-telemetry.spec.js.map +1 -0
- package/esm/static-server-manager.spec.js +60 -125
- package/esm/static-server-manager.spec.js.map +1 -1
- package/esm/static-site-runtime.d.ts +21 -0
- package/esm/static-site-runtime.d.ts.map +1 -0
- package/esm/static-site-runtime.js +48 -0
- package/esm/static-site-runtime.js.map +1 -0
- package/esm/static-site-runtime.spec.d.ts +2 -0
- package/esm/static-site-runtime.spec.d.ts.map +1 -0
- package/esm/static-site-runtime.spec.js +30 -0
- package/esm/static-site-runtime.spec.js.map +1 -0
- package/esm/user-store.d.ts +39 -0
- package/esm/user-store.d.ts.map +1 -0
- package/esm/user-store.js +62 -0
- package/esm/user-store.js.map +1 -0
- package/esm/user-store.spec.d.ts +2 -0
- package/esm/user-store.spec.d.ts.map +1 -0
- package/esm/user-store.spec.js +72 -0
- package/esm/user-store.spec.js.map +1 -0
- package/esm/validate.integration.spec.js +26 -83
- package/esm/validate.integration.spec.js.map +1 -1
- package/package.json +8 -8
- package/src/actions/get-current-user.spec.ts +11 -10
- package/src/actions/index.ts +0 -1
- package/src/actions/is-authenticated.spec.ts +12 -7
- package/src/actions/logout-action.spec.ts +12 -12
- package/src/actions/logout.ts +1 -1
- package/src/actions/password-login-action.spec.ts +30 -30
- package/src/actions/password-login-action.ts +1 -1
- package/src/authenticate.spec.ts +45 -51
- package/src/authenticate.ts +1 -1
- package/src/authorize.spec.ts +37 -39
- package/src/endpoint-generators/create-delete-endpoint.spec.ts +16 -24
- package/src/endpoint-generators/create-delete-endpoint.ts +11 -17
- package/src/endpoint-generators/create-get-collection-endpoint.spec.ts +45 -78
- package/src/endpoint-generators/create-get-collection-endpoint.ts +17 -19
- package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +17 -33
- package/src/endpoint-generators/create-get-entity-endpoint.ts +11 -17
- package/src/endpoint-generators/create-get-openapi-document-action.ts +2 -38
- package/src/endpoint-generators/create-get-schema-action.ts +1 -1
- package/src/endpoint-generators/create-patch-endpoint.spec.ts +11 -18
- package/src/endpoint-generators/create-patch-endpoint.ts +12 -23
- package/src/endpoint-generators/create-post-endpoint.spec.ts +45 -14
- package/src/endpoint-generators/create-post-endpoint.ts +13 -23
- package/src/endpoint-generators/utils.ts +36 -28
- package/src/endpoint-generators/with-schema-and-openapi-action.ts +0 -3
- package/src/get-schema-from-api.spec.ts +1 -1
- package/src/get-schema-from-api.ts +1 -1
- package/src/helpers.spec.ts +51 -41
- package/src/helpers.ts +140 -109
- package/src/http-authentication-settings.ts +51 -25
- package/src/http-server-pool.spec.ts +220 -0
- package/src/http-server-pool.ts +169 -0
- package/src/http-user-context.spec.ts +203 -383
- package/src/http-user-context.ts +114 -91
- package/src/index.ts +6 -4
- package/src/login-response-strategy.spec.ts +32 -31
- package/src/login-response-strategy.ts +13 -12
- package/src/openapi/generate-openapi-document.ts +0 -3
- package/src/proxy-manager.spec.ts +152 -182
- package/src/proxy-runtime.spec.ts +50 -0
- package/src/proxy-runtime.ts +75 -0
- package/src/rest-api-runtime.spec.ts +77 -0
- package/src/rest-api-runtime.ts +204 -0
- package/src/rest-service.integration.spec.ts +68 -87
- package/src/rest.integration.test.ts +27 -87
- package/src/server-telemetry.spec.ts +56 -0
- package/src/server-telemetry.ts +51 -0
- package/src/static-server-manager.spec.ts +63 -151
- package/src/static-site-runtime.spec.ts +39 -0
- package/src/static-site-runtime.ts +75 -0
- package/src/user-store.spec.ts +86 -0
- package/src/user-store.ts +68 -0
- package/src/validate.integration.spec.ts +39 -97
- package/esm/actions/login-action.spec.d.ts +0 -2
- package/esm/actions/login-action.spec.d.ts.map +0 -1
- package/esm/actions/login-action.spec.js +0 -34
- package/esm/actions/login-action.spec.js.map +0 -1
- package/esm/actions/login.d.ts +0 -19
- package/esm/actions/login.d.ts.map +0 -1
- package/esm/actions/login.js +0 -27
- package/esm/actions/login.js.map +0 -1
- package/esm/api-manager.d.ts +0 -104
- package/esm/api-manager.d.ts.map +0 -1
- package/esm/api-manager.js +0 -180
- package/esm/api-manager.js.map +0 -1
- package/esm/proxy-manager.d.ts +0 -53
- package/esm/proxy-manager.d.ts.map +0 -1
- package/esm/proxy-manager.js +0 -84
- package/esm/proxy-manager.js.map +0 -1
- package/esm/server-manager.d.ts +0 -48
- package/esm/server-manager.d.ts.map +0 -1
- package/esm/server-manager.js +0 -100
- package/esm/server-manager.js.map +0 -1
- package/esm/static-server-manager.d.ts +0 -19
- package/esm/static-server-manager.d.ts.map +0 -1
- package/esm/static-server-manager.js +0 -65
- package/esm/static-server-manager.js.map +0 -1
- package/esm/swagger/generate-swagger-json.spec.d.ts +0 -2
- package/esm/swagger/generate-swagger-json.spec.d.ts.map +0 -1
- package/esm/swagger/generate-swagger-json.spec.js +0 -215
- package/esm/swagger/generate-swagger-json.spec.js.map +0 -1
- package/src/actions/login-action.spec.ts +0 -42
- package/src/actions/login.ts +0 -32
- package/src/api-manager.ts +0 -308
- package/src/proxy-manager.ts +0 -102
- package/src/server-manager.ts +0 -135
- package/src/static-server-manager.ts +0 -90
- package/src/swagger/generate-swagger-json.spec.ts +0 -250
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [14.0.0] - 2026-04-25
|
|
4
|
+
|
|
5
|
+
### 💥 Breaking Changes
|
|
6
|
+
|
|
7
|
+
Server managers are gone — every subsystem is now a DI token. See the [v7 migration guide](../../docs/migrations/v7-functional-di.md) for rationale, recipes, and pitfalls.
|
|
8
|
+
|
|
9
|
+
- Removed the `ServerManager`, `ApiManager`, `StaticServerManager`, and `ProxyManager` classes. They are replaced by:
|
|
10
|
+
- `HttpServerPoolToken` — shared HTTP server pool (singleton).
|
|
11
|
+
- `ServerTelemetryToken` — event hub for `onApiRequestError`, `onProxyFailed`, `onWebSocketActionFailed`, `onWebSocketProxyFailed`. Subscribe via `injector.get(ServerTelemetryToken).subscribe(...)`.
|
|
12
|
+
- `HttpAuthenticationSettings` interface + token.
|
|
13
|
+
- `HttpUserContext` interface + token.
|
|
14
|
+
- `useRestService` / `useHttpAuthentication` / `useStaticFiles` / `useProxy` keep their public shapes; internals are fully token-based.
|
|
15
|
+
- Endpoint generators (`createGetCollectionEndpoint`, `createGetEntityEndpoint`, `createPostEndpoint`, `createPatchEndpoint`, `createDeleteEndpoint`) now take a `DataSetToken` from `@furystack/repository` instead of `{ model, primaryKey }`.
|
|
16
|
+
- `UserStore` and `SessionStore` are throw-by-default. Apps must bind concrete persistent stores at bootstrap.
|
|
17
|
+
- `HttpUserContext.user` is no longer a shared field. Per-request identity is stored in a `WeakMap<request.headers, Promise<User>>` so ancestor-cached instances don't leak identity across scopes.
|
|
18
|
+
- `useHttpAuthentication` takes a `userDataSet: DataSetToken` — the old `getUserDataSet(injector)` / `getSessionDataSet(injector)` callbacks are gone.
|
|
19
|
+
|
|
20
|
+
## [13.0.0] - 2026-04-22
|
|
21
|
+
|
|
22
|
+
### 💥 Breaking Changes
|
|
23
|
+
|
|
24
|
+
### Removed the legacy `LoginAction`
|
|
25
|
+
|
|
26
|
+
The static `LoginAction` has been removed. It was deprecated in favor of the factory-based `createPasswordLoginAction(createCookieLoginStrategy(injector))`, which captures the auth services once at setup time instead of resolving them from the request-scoped injector on every call.
|
|
27
|
+
|
|
28
|
+
**Impact:** `import { LoginAction } from '@furystack/rest-service'` will fail.
|
|
29
|
+
|
|
30
|
+
**Migration:**
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createPasswordLoginAction, createCookieLoginStrategy } from '@furystack/rest-service'
|
|
34
|
+
|
|
35
|
+
// old:
|
|
36
|
+
// '/login': LoginAction,
|
|
37
|
+
// new:
|
|
38
|
+
'/login': createPasswordLoginAction(createCookieLoginStrategy(injector)),
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Removed Swagger-named aliases and the `/swagger.json` auto-registration
|
|
42
|
+
|
|
43
|
+
The following deprecated Swagger-era aliases and the automatic `/swagger.json` backward-compat endpoint have been removed from `ApiManager`:
|
|
44
|
+
|
|
45
|
+
- `generateSwaggerJsonFromApiSchema` (alias of `generateOpenApiDocument`)
|
|
46
|
+
- `CreateGetSwaggerJsonAction` (alias of `CreateGetOpenApiDocumentAction`)
|
|
47
|
+
- `CreateDeprecatedSwaggerRedirect`
|
|
48
|
+
- `WithSchemaAndSwaggerAction<T>` (alias of `WithSchemaAndOpenApiAction<T>`)
|
|
49
|
+
- The auto-registered `GET /swagger.json` endpoint that `useRestService({ enableGetSchema: true })` previously exposed alongside `/openapi.json`
|
|
50
|
+
|
|
51
|
+
**Impact:** Imports of the aliases will fail. Any client still calling `/swagger.json` against a FuryStack server will get a 404; the `Deprecation` / `Link` headers that previously pointed to `/openapi.json` are gone.
|
|
52
|
+
|
|
53
|
+
**Migration:**
|
|
54
|
+
|
|
55
|
+
- Rename type / function imports to their OpenAPI-prefixed counterparts (`generateOpenApiDocument`, `CreateGetOpenApiDocumentAction`, `WithSchemaAndOpenApiAction`).
|
|
56
|
+
- Point clients at `/openapi.json` directly.
|
|
57
|
+
|
|
3
58
|
## [12.3.5] - 2026-04-17
|
|
4
59
|
|
|
5
60
|
### ⬆️ Dependencies
|
package/README.md
CHANGED
|
@@ -19,8 +19,8 @@ Start by importing your custom API endpoint interface (see `@furystack/rest`) an
|
|
|
19
19
|
Usage example – authenticated GET, GET collection, and POST APIs for a custom entity that has a physical store and repository set up:
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
|
-
import { MyApi, MyEntity } from 'my-common-package'
|
|
23
|
-
import {
|
|
22
|
+
import { MyApi, MyEntity, MyEntityDataSet } from 'my-common-package'
|
|
23
|
+
import { createInjector } from '@furystack/inject'
|
|
24
24
|
import {
|
|
25
25
|
createGetCollectionEndpoint,
|
|
26
26
|
createGetEntityEndpoint,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
useRestService,
|
|
31
31
|
} from '@furystack/rest-service'
|
|
32
32
|
|
|
33
|
-
const myInjector =
|
|
33
|
+
const myInjector = createInjector()
|
|
34
34
|
useHttpAuthentication(myInjector)
|
|
35
35
|
await useRestService<MyApi>({
|
|
36
36
|
injector: myInjector,
|
|
@@ -45,20 +45,24 @@ await useRestService<MyApi>({
|
|
|
45
45
|
api: {
|
|
46
46
|
// Endpoints that can be called with GET HTTP method
|
|
47
47
|
GET: {
|
|
48
|
-
'/my-entities': Authenticate()(createGetCollectionEndpoint(
|
|
49
|
-
'/my-entities/:id': Authenticate()(createGetEntityEndpoint(
|
|
48
|
+
'/my-entities': Authenticate()(createGetCollectionEndpoint(MyEntityDataSet)),
|
|
49
|
+
'/my-entities/:id': Authenticate()(createGetEntityEndpoint(MyEntityDataSet)),
|
|
50
50
|
},
|
|
51
51
|
// Endpoints that can be called with POST HTTP method
|
|
52
52
|
POST: {
|
|
53
|
-
'/my-entities': Authenticate()(createPostEndpoint(
|
|
53
|
+
'/my-entities': Authenticate()(createPostEndpoint(MyEntityDataSet)),
|
|
54
54
|
},
|
|
55
55
|
},
|
|
56
56
|
})
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Endpoint generators now take a `DataSetToken` directly. Define the token
|
|
60
|
+
once with `defineDataSet` in a shared module and import it in both the
|
|
61
|
+
server and any generators that need it.
|
|
62
|
+
|
|
63
|
+
### Endpoint Generators (Based on DataSets)
|
|
60
64
|
|
|
61
|
-
If you use the underlying layers of FuryStack (`
|
|
65
|
+
If you use the underlying layers of FuryStack (`defineStore` → `defineDataSet`) for an entity type, you can easily create some CRUD endpoints for them. These include:
|
|
62
66
|
|
|
63
67
|
- createDeleteEndpoint()
|
|
64
68
|
- createGetCollectionEndpoint()
|
|
@@ -66,7 +70,7 @@ If you use the underlying layers of FuryStack (`PhysicalStore` -> `Repository`)
|
|
|
66
70
|
- createPatchEndpoint()
|
|
67
71
|
- createPostEndpoint()
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
Each generator takes a `DataSetToken` and resolves it via the per-request injector. Authorization, hooks, and events configured on the DataSet apply transparently.
|
|
70
74
|
|
|
71
75
|
### Custom Endpoint Implementation
|
|
72
76
|
|
|
@@ -109,9 +113,10 @@ export interface MyApiWithCustomEndpoint extends RestApi {
|
|
|
109
113
|
|
|
110
114
|
/** In the Backend code */
|
|
111
115
|
|
|
116
|
+
import { createInjector } from '@furystack/inject'
|
|
112
117
|
import { JsonResult, useRestService } from '@furystack/rest-service'
|
|
113
118
|
|
|
114
|
-
const i =
|
|
119
|
+
const i = createInjector()
|
|
115
120
|
|
|
116
121
|
await useRestService<MyApiWithCustomEndpoint>({
|
|
117
122
|
injector: i,
|
|
@@ -139,7 +144,8 @@ await useRestService<MyApiWithCustomEndpoint>({
|
|
|
139
144
|
|
|
140
145
|
console.log('The headers are:', headers) /** {foo: 'asd', bar: 2, baz: false} */
|
|
141
146
|
|
|
142
|
-
const
|
|
147
|
+
const httpUser = injector.get(HttpUserContext) // Injector is scoped to the Request
|
|
148
|
+
const currentUser = await httpUser.getCurrentUser()
|
|
143
149
|
console.log('The current user is:', currentUser)
|
|
144
150
|
|
|
145
151
|
return JsonResult({ success: true }, 200)
|
|
@@ -208,21 +214,32 @@ In that way, you will get full validation for _all_ defined endpoint data (heade
|
|
|
208
214
|
You can use the built-in authentication that comes with this package. It contains a session (~cookie) based authentication and Basic Auth. You can use it with the `useHttpAuthentication()` helper:
|
|
209
215
|
|
|
210
216
|
```ts
|
|
211
|
-
import {
|
|
212
|
-
import {
|
|
213
|
-
|
|
217
|
+
import { createInjector } from '@furystack/inject'
|
|
218
|
+
import { useHttpAuthentication, useRestService, UserStore, SessionStore } from '@furystack/rest-service'
|
|
219
|
+
// Persistent store factories and your app-specific DataSet tokens
|
|
220
|
+
import { applicationUserStoreFactory, applicationSessionStoreFactory, ApplicationUserDataSet } from './my-app/stores.js'
|
|
221
|
+
|
|
222
|
+
const myInjector = createInjector()
|
|
223
|
+
|
|
224
|
+
// Wire persistent stores behind the throw-by-default tokens
|
|
225
|
+
myInjector.bind(UserStore, applicationUserStoreFactory)
|
|
226
|
+
myInjector.bind(SessionStore, applicationSessionStoreFactory)
|
|
214
227
|
|
|
215
|
-
const myInjector = new Injector()
|
|
216
228
|
useHttpAuthentication(myInjector, {
|
|
217
|
-
cookieName: 'sessionId',
|
|
218
|
-
enableBasicAuth: true,
|
|
219
|
-
|
|
220
|
-
getUserDataSet: (injector) => getDataSetFor(injector, ApplicationUserModel, 'username'),
|
|
221
|
-
getSessionDataSet: (injector) => getDataSetFor(injector, MySessionModel, 'sessionId'),
|
|
229
|
+
cookieName: 'sessionId',
|
|
230
|
+
enableBasicAuth: true,
|
|
231
|
+
userDataSet: ApplicationUserDataSet,
|
|
222
232
|
})
|
|
233
|
+
|
|
223
234
|
await useRestService<MyApi>({ injector: myInjector, ...apiOptions })
|
|
224
235
|
```
|
|
225
236
|
|
|
237
|
+
`useHttpAuthentication` binds the `HttpAuthenticationSettings` and
|
|
238
|
+
`HttpUserContext` tokens. Your app is responsible for binding the
|
|
239
|
+
`UserStore` / `SessionStore` tokens (which throw by default) to concrete
|
|
240
|
+
persistent implementations, and for passing the `DataSetToken` that backs
|
|
241
|
+
user lookups.
|
|
242
|
+
|
|
226
243
|
### Static File Serving
|
|
227
244
|
|
|
228
245
|
You can serve static files using the `useStaticFiles` helper:
|
|
@@ -322,14 +339,14 @@ The proxy server acts as an intermediary, forwarding requests and responses whil
|
|
|
322
339
|
|
|
323
340
|
**Error Handling and Monitoring:**
|
|
324
341
|
|
|
325
|
-
|
|
342
|
+
Proxy failures are routed through the `ServerTelemetryToken`, which is a
|
|
343
|
+
singleton event hub shared by every `useXxx` helper in this package:
|
|
326
344
|
|
|
327
345
|
```ts
|
|
328
|
-
import {
|
|
346
|
+
import { ServerTelemetryToken } from '@furystack/rest-service'
|
|
329
347
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
proxyManager.subscribe('onProxyFailed', ({ from, to, error }) => {
|
|
348
|
+
const telemetry = injector.get(ServerTelemetryToken)
|
|
349
|
+
telemetry.subscribe('onProxyFailed', ({ from, to, error }) => {
|
|
333
350
|
console.error(`Proxy failed: ${from} -> ${to}`, error)
|
|
334
351
|
// Send to your logging service
|
|
335
352
|
})
|
|
@@ -369,11 +386,13 @@ When enabled, the proxy will forward WebSocket upgrade requests to the target se
|
|
|
369
386
|
- Timeout configuration (applies to upgrade handshake)
|
|
370
387
|
- Error monitoring via `onWebSocketProxyFailed` events
|
|
371
388
|
|
|
372
|
-
Monitor WebSocket proxy errors:
|
|
389
|
+
Monitor WebSocket proxy errors via the same telemetry token:
|
|
373
390
|
|
|
374
391
|
```ts
|
|
375
|
-
|
|
376
|
-
|
|
392
|
+
import { ServerTelemetryToken } from '@furystack/rest-service'
|
|
393
|
+
|
|
394
|
+
const telemetry = injector.get(ServerTelemetryToken)
|
|
395
|
+
telemetry.subscribe('onWebSocketProxyFailed', ({ from, to, error }) => {
|
|
377
396
|
console.error(`WebSocket proxy failed: ${from} -> ${to}`, error)
|
|
378
397
|
})
|
|
379
398
|
```
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IdentityContext } from '@furystack/core';
|
|
2
|
+
import { createInjector } from '@furystack/inject';
|
|
2
3
|
import { usingAsync } from '@furystack/utils';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
3
5
|
import { GetCurrentUser } from './get-current-user.js';
|
|
4
|
-
import { IdentityContext } from '@furystack/core';
|
|
5
|
-
import { describe, it, expect } from 'vitest';
|
|
6
6
|
describe('getCurrentUser', () => {
|
|
7
7
|
const request = { url: 'https://google.com' };
|
|
8
8
|
const response = {};
|
|
9
|
-
it('
|
|
9
|
+
it('returns the current user in a JSON result', async () => {
|
|
10
10
|
const testUser = { Name: 'Userke' };
|
|
11
|
-
await usingAsync(
|
|
12
|
-
i.
|
|
11
|
+
await usingAsync(createInjector(), async (i) => {
|
|
12
|
+
i.bind(IdentityContext, () => ({
|
|
13
|
+
getCurrentUser: () => Promise.resolve(testUser),
|
|
14
|
+
isAuthenticated: () => Promise.resolve(true),
|
|
15
|
+
isAuthorized: () => Promise.resolve(true),
|
|
16
|
+
}));
|
|
13
17
|
const result = await GetCurrentUser({ injector: i, request, response });
|
|
14
18
|
expect(result.statusCode).toBe(200);
|
|
15
19
|
expect(result.chunk).toEqual(testUser);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-current-user.spec.js","sourceRoot":"","sources":["../../src/actions/get-current-user.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"get-current-user.spec.js","sourceRoot":"","sources":["../../src/actions/get-current-user.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,oBAAoB,EAAqB,CAAA;IAChE,MAAM,QAAQ,GAAG,EAAoB,CAAA;IAErC,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;QACnC,MAAM,UAAU,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC7C,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC;gBAC7B,cAAc,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,QAAiB,CAAC;gBACxD,eAAe,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC5C,YAAY,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;aAC1C,CAAC,CAAC,CAAA;YACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;YACvE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
package/esm/actions/index.d.ts
CHANGED
|
@@ -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,
|
|
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,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA"}
|
package/esm/actions/index.js
CHANGED
package/esm/actions/index.js.map
CHANGED
|
@@ -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,
|
|
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,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA"}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IdentityContext } from '@furystack/core';
|
|
2
|
+
import { createInjector } from '@furystack/inject';
|
|
2
3
|
import { usingAsync } from '@furystack/utils';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
3
5
|
import { IsAuthenticated } from './is-authenticated.js';
|
|
4
|
-
import { IdentityContext } from '@furystack/core';
|
|
5
|
-
import { describe, it, expect } from 'vitest';
|
|
6
6
|
describe('isAuthenticated', () => {
|
|
7
7
|
const request = { url: 'https://google.com' };
|
|
8
8
|
const response = {};
|
|
9
|
-
it('
|
|
10
|
-
await usingAsync(
|
|
11
|
-
i.
|
|
9
|
+
it('returns the authentication status from the identity context', async () => {
|
|
10
|
+
await usingAsync(createInjector(), async (i) => {
|
|
11
|
+
i.bind(IdentityContext, () => ({
|
|
12
|
+
isAuthenticated: () => Promise.resolve(true),
|
|
13
|
+
isAuthorized: () => Promise.resolve(true),
|
|
14
|
+
getCurrentUser: () => Promise.reject(new Error('not needed')),
|
|
15
|
+
}));
|
|
12
16
|
const result = await IsAuthenticated({ injector: i, request, response });
|
|
13
17
|
expect(result.statusCode).toBe(200);
|
|
14
18
|
expect(result.chunk).toEqual({ isAuthenticated: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-authenticated.spec.js","sourceRoot":"","sources":["../../src/actions/is-authenticated.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"is-authenticated.spec.js","sourceRoot":"","sources":["../../src/actions/is-authenticated.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,oBAAoB,EAAqB,CAAA;IAChE,MAAM,QAAQ,GAAG,EAAoB,CAAA;IAErC,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,UAAU,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC7C,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC;gBAC7B,eAAe,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC5C,YAAY,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBACzC,cAAc,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;aAC9D,CAAC,CAAC,CAAA;YACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;YACxE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createInjector } from '@furystack/inject';
|
|
2
|
+
import { EventHub } from '@furystack/utils';
|
|
2
3
|
import { usingAsync } from '@furystack/utils';
|
|
3
4
|
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import { HttpUserContext } from '../http-user-context.js';
|
|
5
|
+
import { HttpUserContext as HttpUserContextToken } from '../http-user-context.js';
|
|
5
6
|
import { LogoutAction } from './logout.js';
|
|
6
7
|
describe('LogoutAction', () => {
|
|
7
8
|
const request = { url: 'https://google.com' };
|
|
8
9
|
const response = {};
|
|
9
|
-
it('
|
|
10
|
-
await usingAsync(
|
|
11
|
-
const cookieLogout = vi.fn(async () =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
it('delegates to HttpUserContext.cookieLogout and returns an empty result', async () => {
|
|
11
|
+
await usingAsync(createInjector(), async (i) => {
|
|
12
|
+
const cookieLogout = vi.fn(async () => {
|
|
13
|
+
/* noop */
|
|
14
|
+
});
|
|
15
|
+
const stub = Object.assign(new EventHub(), { cookieLogout });
|
|
16
|
+
i.bind(HttpUserContextToken, () => stub);
|
|
15
17
|
const result = await LogoutAction({ request, response, injector: i });
|
|
16
18
|
expect(result.statusCode).toBe(200);
|
|
17
19
|
expect(result.chunk).toBe(undefined);
|
|
18
|
-
expect(cookieLogout).
|
|
20
|
+
expect(cookieLogout).toHaveBeenCalled();
|
|
19
21
|
});
|
|
20
22
|
});
|
|
21
23
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout-action.spec.js","sourceRoot":"","sources":["../../src/actions/logout-action.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"logout-action.spec.js","sourceRoot":"","sources":["../../src/actions/logout-action.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAEjD,OAAO,EAAE,eAAe,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,oBAAoB,EAAqB,CAAA;IAChE,MAAM,QAAQ,GAAG,EAAoB,CAAA;IAErC,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,UAAU,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;YAC7C,MAAM,YAAY,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE;gBACpC,UAAU;YACZ,CAAC,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,CAA+B,CAAA;YAC1F,CAAC,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;YAExC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;YACrE,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACpC,MAAM,CAAC,YAAY,CAAC,CAAC,gBAAgB,EAAE,CAAA;QACzC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
package/esm/actions/logout.js
CHANGED
|
@@ -9,7 +9,7 @@ import { EmptyResult } from '../request-action-implementation.js';
|
|
|
9
9
|
* @returns An empty result that indicates the success
|
|
10
10
|
*/
|
|
11
11
|
export const LogoutAction = async ({ injector, request, response }) => {
|
|
12
|
-
await injector.
|
|
12
|
+
await injector.get(HttpUserContext).cookieLogout(request, response);
|
|
13
13
|
return EmptyResult();
|
|
14
14
|
};
|
|
15
15
|
//# sourceMappingURL=logout.js.map
|
|
@@ -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,
|
|
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"}
|
|
@@ -19,7 +19,7 @@ export const createPasswordLoginAction = (strategy) => {
|
|
|
19
19
|
return async ({ injector, getBody }) => {
|
|
20
20
|
const body = await getBody();
|
|
21
21
|
try {
|
|
22
|
-
const user = await injector.
|
|
22
|
+
const user = await injector.get(HttpUserContext).authenticateUser(body.username, body.password);
|
|
23
23
|
return strategy.createLoginResponse(user, injector);
|
|
24
24
|
}
|
|
25
25
|
catch {
|
|
@@ -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,
|
|
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,30 +1,25 @@
|
|
|
1
|
-
import { InMemoryStore,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { PasswordAuthenticator, PasswordCredential, PasswordResetToken, usePasswordPolicy } from '@furystack/security';
|
|
1
|
+
import { InMemoryStore, User as UserModel } from '@furystack/core';
|
|
2
|
+
import { createInjector } from '@furystack/inject';
|
|
3
|
+
import { PasswordCredential, PasswordCredentialStore, PasswordResetToken, PasswordResetTokenStore, usePasswordPolicy, } from '@furystack/security';
|
|
5
4
|
import { usingAsync } from '@furystack/utils';
|
|
6
5
|
import { describe, expect, it, vi } from 'vitest';
|
|
7
6
|
import { useHttpAuthentication } from '../helpers.js';
|
|
8
7
|
import { DefaultSession } from '../models/default-session.js';
|
|
9
8
|
import { JsonResult } from '../request-action-implementation.js';
|
|
9
|
+
import { SessionStore, UserStore } from '../user-store.js';
|
|
10
|
+
import { PasswordAuthenticator } from '@furystack/security';
|
|
10
11
|
import { createPasswordLoginAction } from './password-login-action.js';
|
|
11
12
|
const setupInjector = async (i, username, password) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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');
|
|
13
|
+
i.bind(UserStore, () => new InMemoryStore({ model: UserModel, primaryKey: 'username' }));
|
|
14
|
+
i.bind(SessionStore, () => new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }));
|
|
15
|
+
i.bind(PasswordCredentialStore, () => new InMemoryStore({ model: PasswordCredential, primaryKey: 'userName' }));
|
|
16
|
+
i.bind(PasswordResetTokenStore, () => new InMemoryStore({ model: PasswordResetToken, primaryKey: 'token' }));
|
|
21
17
|
usePasswordPolicy(i);
|
|
22
18
|
useHttpAuthentication(i);
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
await
|
|
27
|
-
await sm.getStoreFor(User, 'username').add({ username, roles: ['admin'] });
|
|
19
|
+
const authenticator = i.get(PasswordAuthenticator);
|
|
20
|
+
const cred = await authenticator.hasher.createCredential(username, password);
|
|
21
|
+
await i.get(PasswordCredentialStore).add(cred);
|
|
22
|
+
await i.get(UserStore).add({ username, roles: ['admin'] });
|
|
28
23
|
};
|
|
29
24
|
const mockStrategy = {
|
|
30
25
|
createLoginResponse: vi.fn(async (user) => JsonResult(user, 200)),
|
|
@@ -32,8 +27,8 @@ const mockStrategy = {
|
|
|
32
27
|
describe('createPasswordLoginAction', () => {
|
|
33
28
|
const request = {};
|
|
34
29
|
const response = {};
|
|
35
|
-
it('
|
|
36
|
-
await usingAsync(
|
|
30
|
+
it('delegates to the strategy on successful authentication', async () => {
|
|
31
|
+
await usingAsync(createInjector(), async (i) => {
|
|
37
32
|
await setupInjector(i, 'testuser', 'testpass');
|
|
38
33
|
const action = createPasswordLoginAction(mockStrategy);
|
|
39
34
|
const result = await action({
|
|
@@ -46,8 +41,8 @@ describe('createPasswordLoginAction', () => {
|
|
|
46
41
|
expect(result.chunk.username).toBe('testuser');
|
|
47
42
|
});
|
|
48
43
|
});
|
|
49
|
-
it('
|
|
50
|
-
await usingAsync(
|
|
44
|
+
it('passes the authenticated user to the strategy', async () => {
|
|
45
|
+
await usingAsync(createInjector(), async (i) => {
|
|
51
46
|
await setupInjector(i, 'testuser', 'testpass');
|
|
52
47
|
const strategyFn = vi.fn(async (user) => JsonResult(user, 200));
|
|
53
48
|
const strategy = { createLoginResponse: strategyFn };
|
|
@@ -61,8 +56,8 @@ describe('createPasswordLoginAction', () => {
|
|
|
61
56
|
expect(strategyFn).toHaveBeenCalledWith(expect.objectContaining({ username: 'testuser', roles: ['admin'] }), i);
|
|
62
57
|
});
|
|
63
58
|
});
|
|
64
|
-
it('
|
|
65
|
-
await usingAsync(
|
|
59
|
+
it('throws RequestError on invalid password', async () => {
|
|
60
|
+
await usingAsync(createInjector(), async (i) => {
|
|
66
61
|
await setupInjector(i, 'testuser', 'testpass');
|
|
67
62
|
const action = createPasswordLoginAction(mockStrategy);
|
|
68
63
|
await expect(action({
|
|
@@ -73,8 +68,8 @@ describe('createPasswordLoginAction', () => {
|
|
|
73
68
|
})).rejects.toThrow('Login Failed');
|
|
74
69
|
});
|
|
75
70
|
});
|
|
76
|
-
it('
|
|
77
|
-
await usingAsync(
|
|
71
|
+
it('throws RequestError for an unknown user', async () => {
|
|
72
|
+
await usingAsync(createInjector(), async (i) => {
|
|
78
73
|
await setupInjector(i, 'testuser', 'testpass');
|
|
79
74
|
const action = createPasswordLoginAction(mockStrategy);
|
|
80
75
|
await expect(action({
|
|
@@ -85,8 +80,8 @@ describe('createPasswordLoginAction', () => {
|
|
|
85
80
|
})).rejects.toThrow('Login Failed');
|
|
86
81
|
});
|
|
87
82
|
});
|
|
88
|
-
it('
|
|
89
|
-
await usingAsync(
|
|
83
|
+
it('supports strategies returning custom result shapes', async () => {
|
|
84
|
+
await usingAsync(createInjector(), async (i) => {
|
|
90
85
|
await setupInjector(i, 'testuser', 'testpass');
|
|
91
86
|
const tokenStrategy = {
|
|
92
87
|
createLoginResponse: async () => JsonResult({ accessToken: 'tok123' }, 200),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-login-action.spec.js","sourceRoot":"","sources":["../../src/actions/password-login-action.spec.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"password-login-action.spec.js","sourceRoot":"","sources":["../../src/actions/password-login-action.spec.ts"],"names":[],"mappings":"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"}
|
package/esm/authenticate.js
CHANGED
|
@@ -28,7 +28,7 @@ export const Authenticate = () => (action) => {
|
|
|
28
28
|
if (!authenticated) {
|
|
29
29
|
await sleepAsync(Math.random() * 1000);
|
|
30
30
|
const hasBasicAuth = injector
|
|
31
|
-
.
|
|
31
|
+
.get(HttpUserContext)
|
|
32
32
|
.authentication.authenticationProviders.some((p) => p.name === 'basic-auth');
|
|
33
33
|
return JsonResult({ error: 'unauthorized' }, 401, hasBasicAuth ? { 'WWW-Authenticate': 'Basic' } : {});
|
|
34
34
|
}
|
package/esm/authenticate.js.map
CHANGED
|
@@ -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,
|
|
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"}
|