@twin.org/api-auth-entity-storage-service 0.0.3-next.24 → 0.0.3-next.26

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.
@@ -1 +1 @@
1
- {"version":3,"file":"IAuthHeaderProcessorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IAuthHeaderProcessorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAuthHeaderProcessorConfig } from \"./IAuthHeaderProcessorConfig.js\";\n\n/**\n * Options for the AuthHeaderProcessor constructor.\n */\nexport interface IAuthHeaderProcessorConstructorOptions {\n\t/**\n\t * The admin service.\n\t * @default authentication-admin\n\t */\n\tauthenticationAdminServiceType?: string;\n\n\t/**\n\t * The vault for the private keys.\n\t * @default vault\n\t */\n\tvaultConnectorType?: string;\n\n\t/**\n\t * The configuration for the processor.\n\t */\n\tconfig?: IAuthHeaderProcessorConfig;\n}\n"]}
1
+ {"version":3,"file":"IAuthHeaderProcessorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IAuthHeaderProcessorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAuthHeaderProcessorConfig } from \"./IAuthHeaderProcessorConfig.js\";\n\n/**\n * Options for the AuthHeaderProcessor constructor.\n */\nexport interface IAuthHeaderProcessorConstructorOptions {\n\t/**\n\t * The entity storage for users.\n\t * @default authentication-user\n\t */\n\tuserEntityStorageType?: string;\n\n\t/**\n\t * The vault for the private keys.\n\t * @default vault\n\t */\n\tvaultConnectorType?: string;\n\n\t/**\n\t * The configuration for the processor.\n\t */\n\tconfig?: IAuthHeaderProcessorConfig;\n}\n"]}
@@ -1,6 +1,9 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
1
3
  import { HttpErrorHelper } from "@twin.org/api-models";
2
4
  import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
3
- import { BaseError, Coerce, ComponentFactory, GeneralError, Is } from "@twin.org/core";
5
+ import { BaseError, Coerce, GeneralError, Is } from "@twin.org/core";
6
+ import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
4
7
  import { VaultConnectorFactory } from "@twin.org/vault-models";
5
8
  import { CookieHelper, HeaderTypes, HttpStatusCode } from "@twin.org/web";
6
9
  import { TokenHelper } from "../utils/tokenHelper.js";
@@ -23,10 +26,10 @@ export class AuthHeaderProcessor {
23
26
  */
24
27
  _vaultConnector;
25
28
  /**
26
- * The user admin service.
29
+ * The entity storage for users.
27
30
  * @internal
28
31
  */
29
- _authenticationAdminService;
32
+ _userEntityStorage;
30
33
  /**
31
34
  * The name of the key to retrieve from the vault for signing JWT.
32
35
  * @internal
@@ -48,7 +51,7 @@ export class AuthHeaderProcessor {
48
51
  */
49
52
  constructor(options) {
50
53
  this._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
51
- this._authenticationAdminService = ComponentFactory.get(options?.authenticationAdminServiceType ?? "authentication-admin");
54
+ this._userEntityStorage = EntityStorageConnectorFactory.get(options?.userEntityStorageType ?? "authentication-user");
52
55
  this._signingKeyName = options?.config?.signingKeyName ?? "auth-signing";
53
56
  this._cookieName = options?.config?.cookieName ?? AuthHeaderProcessor.DEFAULT_COOKIE_NAME;
54
57
  }
@@ -83,11 +86,11 @@ export class AuthHeaderProcessor {
83
86
  const tokenAndLocation = TokenHelper.extractTokenFromHeaders(request.headers, this._cookieName);
84
87
  const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeId}/${this._signingKeyName}`, tokenAndLocation?.token, route.requiredScope, async (userIdentity, organizationIdentity) => {
85
88
  const validParts = [];
86
- const user = await this._authenticationAdminService.getByIdentity(userIdentity);
87
- if (user?.userIdentity === userIdentity) {
89
+ const user = await this._userEntityStorage.get(userIdentity, "identity");
90
+ if (user?.identity === userIdentity) {
88
91
  validParts.push("user");
89
92
  }
90
- if (user?.organizationIdentity === organizationIdentity) {
93
+ if (user?.organization === organizationIdentity) {
91
94
  validParts.push("organization");
92
95
  }
93
96
  return validParts;
@@ -1 +1 @@
1
- {"version":3,"file":"authHeaderProcessor.js","sourceRoot":"","sources":["../../../src/processors/authHeaderProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAuthenticationAdminComponent } from \"@twin.org/api-auth-entity-storage-models\";\nimport {\n\tHttpErrorHelper,\n\ttype IBaseRoute,\n\ttype IBaseRouteProcessor,\n\ttype IHttpResponse,\n\ttype IHttpServerRequest\n} from \"@twin.org/api-models\";\nimport {\n\tContextIdHelper,\n\tContextIdKeys,\n\tContextIdStore,\n\ttype IContextIds\n} from \"@twin.org/context\";\nimport { BaseError, Coerce, ComponentFactory, GeneralError, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { VaultConnectorFactory, type IVaultConnector } from \"@twin.org/vault-models\";\nimport { CookieHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\nimport type { IAuthHeaderProcessorConstructorOptions } from \"../models/IAuthHeaderProcessorConstructorOptions.js\";\nimport { TokenHelper } from \"../utils/tokenHelper.js\";\n\n/**\n * Handle a JWT token in the authorization header or cookies and validate it to populate request context identity.\n */\nexport class AuthHeaderProcessor implements IBaseRouteProcessor {\n\t/**\n\t * The default name for the access token as a cookie.\n\t * @internal\n\t */\n\tpublic static readonly DEFAULT_COOKIE_NAME: string = \"access_token\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<AuthHeaderProcessor>();\n\n\t/**\n\t * The vault for the keys.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The user admin service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationAdminService: IAuthenticationAdminComponent;\n\n\t/**\n\t * The name of the key to retrieve from the vault for signing JWT.\n\t * @internal\n\t */\n\tprivate readonly _signingKeyName: string;\n\n\t/**\n\t * The name of the cookie to use for the token.\n\t * @internal\n\t */\n\tprivate readonly _cookieName: string;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of AuthCookiePreProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: IAuthHeaderProcessorConstructorOptions) {\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\n\t\tthis._authenticationAdminService = ComponentFactory.get<IAuthenticationAdminComponent>(\n\t\t\toptions?.authenticationAdminServiceType ?? \"authentication-admin\"\n\t\t);\n\n\t\tthis._signingKeyName = options?.config?.signingKeyName ?? \"auth-signing\";\n\t\tthis._cookieName = options?.config?.cookieName ?? AuthHeaderProcessor.DEFAULT_COOKIE_NAME;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn AuthHeaderProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (!Is.empty(route) && !(route.skipAuth ?? false)) {\n\t\t\ttry {\n\t\t\t\tconst tokenAndLocation = TokenHelper.extractTokenFromHeaders(\n\t\t\t\t\trequest.headers,\n\t\t\t\t\tthis._cookieName\n\t\t\t\t);\n\n\t\t\t\tconst headerAndPayload = await TokenHelper.verify(\n\t\t\t\t\tthis._vaultConnector,\n\t\t\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\t\t\ttokenAndLocation?.token,\n\t\t\t\t\troute.requiredScope,\n\t\t\t\t\tasync (userIdentity: string, organizationIdentity: string) => {\n\t\t\t\t\t\tconst validParts = [];\n\t\t\t\t\t\tconst user = await this._authenticationAdminService.getByIdentity(userIdentity);\n\n\t\t\t\t\t\tif (user?.userIdentity === userIdentity) {\n\t\t\t\t\t\t\tvalidParts.push(\"user\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (user?.organizationIdentity === organizationIdentity) {\n\t\t\t\t\t\t\tvalidParts.push(\"organization\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn validParts;\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\t// If tenant id is defined in the context, then it must match the one in the token\n\t\t\t\t// but both can be undefined in a single tenant context\n\t\t\t\tif (contextIds?.[ContextIdKeys.Tenant] !== headerAndPayload?.payload?.tid) {\n\t\t\t\t\tthrow new GeneralError(AuthHeaderProcessor.CLASS_NAME, \"tenantIdMismatch\");\n\t\t\t\t}\n\n\t\t\t\tcontextIds[ContextIdKeys.User] = headerAndPayload.payload?.sub;\n\t\t\t\tcontextIds[ContextIdKeys.Organization] = Coerce.string(headerAndPayload.payload?.org);\n\n\t\t\t\tprocessorState.authToken = tokenAndLocation?.token;\n\t\t\t\tprocessorState.authTokenLocation = tokenAndLocation?.location;\n\t\t\t} catch (err) {\n\t\t\t\tconst error = BaseError.fromError(err);\n\t\t\t\tHttpErrorHelper.buildResponse(response, error, HttpStatusCode.unauthorized);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Post process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t */\n\tpublic async post(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tconst responseAuthOperation = processorState?.authOperation;\n\t\tconst responseAuthToken = processorState?.authToken;\n\n\t\t// We don't populate the cookie if the incoming request was from an authorization header.\n\t\tif (\n\t\t\t!Is.empty(route) &&\n\t\t\tIs.stringValue(responseAuthOperation) &&\n\t\t\tprocessorState.authTokenLocation !== \"authorization\"\n\t\t) {\n\t\t\tif (\n\t\t\t\t(responseAuthOperation === \"login\" || responseAuthOperation === \"refresh\") &&\n\t\t\t\tIs.stringValue(responseAuthToken)\n\t\t\t) {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.createCookie(\n\t\t\t\t\tthis._cookieName,\n\t\t\t\t\tresponseAuthToken,\n\t\t\t\t\t{\n\t\t\t\t\t\tsecure: true,\n\t\t\t\t\t\thttpOnly: true,\n\t\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\t\tpath: \"/\"\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} else if (responseAuthOperation === \"logout\") {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.deleteCookie(this._cookieName, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\tpath: \"/\"\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"authHeaderProcessor.js","sourceRoot":"","sources":["../../../src/processors/authHeaderProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tHttpErrorHelper,\n\ttype IBaseRoute,\n\ttype IBaseRouteProcessor,\n\ttype IHttpResponse,\n\ttype IHttpServerRequest\n} from \"@twin.org/api-models\";\nimport {\n\tContextIdHelper,\n\tContextIdKeys,\n\tContextIdStore,\n\ttype IContextIds\n} from \"@twin.org/context\";\nimport { BaseError, Coerce, GeneralError, Is } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { VaultConnectorFactory, type IVaultConnector } from \"@twin.org/vault-models\";\nimport { CookieHelper, HeaderTypes, HttpStatusCode } from \"@twin.org/web\";\nimport type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IAuthHeaderProcessorConstructorOptions } from \"../models/IAuthHeaderProcessorConstructorOptions.js\";\nimport { TokenHelper } from \"../utils/tokenHelper.js\";\n\n/**\n * Handle a JWT token in the authorization header or cookies and validate it to populate request context identity.\n */\nexport class AuthHeaderProcessor implements IBaseRouteProcessor {\n\t/**\n\t * The default name for the access token as a cookie.\n\t * @internal\n\t */\n\tpublic static readonly DEFAULT_COOKIE_NAME: string = \"access_token\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<AuthHeaderProcessor>();\n\n\t/**\n\t * The vault for the keys.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The entity storage for users.\n\t * @internal\n\t */\n\tprivate readonly _userEntityStorage: IEntityStorageConnector<AuthenticationUser>;\n\n\t/**\n\t * The name of the key to retrieve from the vault for signing JWT.\n\t * @internal\n\t */\n\tprivate readonly _signingKeyName: string;\n\n\t/**\n\t * The name of the cookie to use for the token.\n\t * @internal\n\t */\n\tprivate readonly _cookieName: string;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of AuthCookiePreProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: IAuthHeaderProcessorConstructorOptions) {\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\n\t\tthis._signingKeyName = options?.config?.signingKeyName ?? \"auth-signing\";\n\t\tthis._cookieName = options?.config?.cookieName ?? AuthHeaderProcessor.DEFAULT_COOKIE_NAME;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn AuthHeaderProcessor.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\t}\n\n\t/**\n\t * Pre process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t */\n\tpublic async pre(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tif (!Is.empty(route) && !(route.skipAuth ?? false)) {\n\t\t\ttry {\n\t\t\t\tconst tokenAndLocation = TokenHelper.extractTokenFromHeaders(\n\t\t\t\t\trequest.headers,\n\t\t\t\t\tthis._cookieName\n\t\t\t\t);\n\n\t\t\t\tconst headerAndPayload = await TokenHelper.verify(\n\t\t\t\t\tthis._vaultConnector,\n\t\t\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\t\t\ttokenAndLocation?.token,\n\t\t\t\t\troute.requiredScope,\n\t\t\t\t\tasync (userIdentity: string, organizationIdentity: string) => {\n\t\t\t\t\t\tconst validParts = [];\n\t\t\t\t\t\tconst user = await this._userEntityStorage.get(userIdentity, \"identity\");\n\n\t\t\t\t\t\tif (user?.identity === userIdentity) {\n\t\t\t\t\t\t\tvalidParts.push(\"user\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (user?.organization === organizationIdentity) {\n\t\t\t\t\t\t\tvalidParts.push(\"organization\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn validParts;\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\t// If tenant id is defined in the context, then it must match the one in the token\n\t\t\t\t// but both can be undefined in a single tenant context\n\t\t\t\tif (contextIds?.[ContextIdKeys.Tenant] !== headerAndPayload?.payload?.tid) {\n\t\t\t\t\tthrow new GeneralError(AuthHeaderProcessor.CLASS_NAME, \"tenantIdMismatch\");\n\t\t\t\t}\n\n\t\t\t\tcontextIds[ContextIdKeys.User] = headerAndPayload.payload?.sub;\n\t\t\t\tcontextIds[ContextIdKeys.Organization] = Coerce.string(headerAndPayload.payload?.org);\n\n\t\t\t\tprocessorState.authToken = tokenAndLocation?.token;\n\t\t\t\tprocessorState.authTokenLocation = tokenAndLocation?.location;\n\t\t\t} catch (err) {\n\t\t\t\tconst error = BaseError.fromError(err);\n\t\t\t\tHttpErrorHelper.buildResponse(response, error, HttpStatusCode.unauthorized);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Post process the REST request for the specified route.\n\t * @param request The incoming request.\n\t * @param response The outgoing response.\n\t * @param route The route to process.\n\t * @param contextIds The context IDs of the request.\n\t * @param processorState The state handed through the processors.\n\t */\n\tpublic async post(\n\t\trequest: IHttpServerRequest,\n\t\tresponse: IHttpResponse,\n\t\troute: IBaseRoute | undefined,\n\t\tcontextIds: IContextIds,\n\t\tprocessorState: { [id: string]: unknown }\n\t): Promise<void> {\n\t\tconst responseAuthOperation = processorState?.authOperation;\n\t\tconst responseAuthToken = processorState?.authToken;\n\n\t\t// We don't populate the cookie if the incoming request was from an authorization header.\n\t\tif (\n\t\t\t!Is.empty(route) &&\n\t\t\tIs.stringValue(responseAuthOperation) &&\n\t\t\tprocessorState.authTokenLocation !== \"authorization\"\n\t\t) {\n\t\t\tif (\n\t\t\t\t(responseAuthOperation === \"login\" || responseAuthOperation === \"refresh\") &&\n\t\t\t\tIs.stringValue(responseAuthToken)\n\t\t\t) {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.createCookie(\n\t\t\t\t\tthis._cookieName,\n\t\t\t\t\tresponseAuthToken,\n\t\t\t\t\t{\n\t\t\t\t\t\tsecure: true,\n\t\t\t\t\t\thttpOnly: true,\n\t\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\t\tpath: \"/\"\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} else if (responseAuthOperation === \"logout\") {\n\t\t\t\tresponse.headers ??= {};\n\t\t\t\tresponse.headers[HeaderTypes.SetCookie] = CookieHelper.deleteCookie(this._cookieName, {\n\t\t\t\t\tsecure: true,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tsameSite: \"None\",\n\t\t\t\t\tpath: \"/\"\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -4,10 +4,10 @@ import type { IAuthHeaderProcessorConfig } from "./IAuthHeaderProcessorConfig.js
4
4
  */
5
5
  export interface IAuthHeaderProcessorConstructorOptions {
6
6
  /**
7
- * The admin service.
8
- * @default authentication-admin
7
+ * The entity storage for users.
8
+ * @default authentication-user
9
9
  */
10
- authenticationAdminServiceType?: string;
10
+ userEntityStorageType?: string;
11
11
  /**
12
12
  * The vault for the private keys.
13
13
  * @default vault
package/docs/changelog.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.26](https://github.com/twinfoundation/api/compare/api-auth-entity-storage-service-v0.0.3-next.25...api-auth-entity-storage-service-v0.0.3-next.26) (2026-04-22)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **api-auth-entity-storage-service:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.25 to 0.0.3-next.26
16
+ * @twin.org/api-core bumped from 0.0.3-next.25 to 0.0.3-next.26
17
+ * @twin.org/api-models bumped from 0.0.3-next.25 to 0.0.3-next.26
18
+
19
+ ## [0.0.3-next.25](https://github.com/twinfoundation/api/compare/api-auth-entity-storage-service-v0.0.3-next.24...api-auth-entity-storage-service-v0.0.3-next.25) (2026-04-14)
20
+
21
+
22
+ ### Features
23
+
24
+ * auth header processor use user entity storage directly ([93084cf](https://github.com/twinfoundation/api/commit/93084cfed8f3ab16c80a1ab1bc90b1d9fc14bd25))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.24 to 0.0.3-next.25
32
+ * @twin.org/api-core bumped from 0.0.3-next.24 to 0.0.3-next.25
33
+ * @twin.org/api-models bumped from 0.0.3-next.24 to 0.0.3-next.25
34
+
3
35
  ## [0.0.3-next.24](https://github.com/twinfoundation/api/compare/api-auth-entity-storage-service-v0.0.3-next.23...api-auth-entity-storage-service-v0.0.3-next.24) (2026-04-14)
4
36
 
5
37
 
package/docs/examples.md CHANGED
@@ -60,6 +60,96 @@ await authService.logout(refreshResult.token);
60
60
  console.log(refreshResult.expiry > 0); // true
61
61
  ```
62
62
 
63
+ ## EntityStorageAuthenticationRateService
64
+
65
+ ```typescript
66
+ import { EntityStorageAuthenticationRateService } from '@twin.org/api-auth-entity-storage-service';
67
+ import { BaseError, GeneralError } from '@twin.org/core';
68
+ import { TooManyRequestsError } from '@twin.org/api-models';
69
+
70
+ const rateService = new EntityStorageAuthenticationRateService({
71
+ config: {
72
+ cleanupIntervalMinutes: 5
73
+ }
74
+ });
75
+
76
+ console.log(rateService.className()); // EntityStorageAuthenticationRateService
77
+
78
+ await rateService.registerAction('login', {
79
+ maxAttempts: 3,
80
+ windowMinutes: 15
81
+ });
82
+
83
+ await rateService.start('default');
84
+
85
+ await rateService.check('login', 'alice@example.org');
86
+ await rateService.check('login', 'alice@example.org');
87
+ await rateService.check('login', 'alice@example.org');
88
+
89
+ try {
90
+ await rateService.check('login', 'alice@example.org');
91
+ } catch (error) {
92
+ if (BaseError.isErrorName(error, TooManyRequestsError.CLASS_NAME)) {
93
+ const tooMany = error as TooManyRequestsError;
94
+ console.log(tooMany.properties?.retryAfterSeconds); // 900
95
+ console.log(tooMany.properties?.nextRequestTime); // 2026-04-13T10:15:00.000Z
96
+ }
97
+ }
98
+
99
+ await rateService.clear('login', 'alice@example.org');
100
+ await rateService.unregisterAction('login');
101
+
102
+ try {
103
+ await rateService.check('login', 'alice@example.org');
104
+ } catch (error) {
105
+ if (BaseError.isErrorName(error, GeneralError.CLASS_NAME)) {
106
+ console.log((error as GeneralError).name); // GeneralError
107
+ }
108
+ }
109
+
110
+ await rateService.stop('default');
111
+ ```
112
+
113
+ ## EntityStorageAuthenticationAuditService
114
+
115
+ ```typescript
116
+ import { EntityStorageAuthenticationAuditService } from '@twin.org/api-auth-entity-storage-service';
117
+
118
+ const auditService = new EntityStorageAuthenticationAuditService({
119
+ config: {
120
+ ipHashSalt: 'StrongServerSideSaltForAuditHashing123'
121
+ }
122
+ });
123
+
124
+ console.log(auditService.className()); // EntityStorageAuthenticationAuditService
125
+
126
+ const createdAuditId = await auditService.create({
127
+ event: 'login-failure',
128
+ actorId: 'did:example:user:alice',
129
+ nodeId: 'did:example:node:eu-west-1',
130
+ organizationId: 'did:example:org:core',
131
+ tenantId: 'did:example:tenant:alpha',
132
+ data: {
133
+ reason: 'invalid-password'
134
+ }
135
+ });
136
+
137
+ const auditPage = await auditService.query(
138
+ {
139
+ actorId: 'did:example:user:alice',
140
+ event: 'login-failure',
141
+ startDate: '2026-04-01T00:00:00.000Z',
142
+ endDate: '2026-04-30T23:59:59.999Z'
143
+ },
144
+ undefined,
145
+ 50
146
+ );
147
+
148
+ console.log(createdAuditId); // 018f2f67bb9d4a0caad8386f56df85ce
149
+ console.log(auditPage.entries.length); // 1
150
+ console.log(auditPage.cursor); // eyJpZCI6IjAxOGYyZjY3YmI5ZDRhMGNhYWQ4Mzg2ZjU2ZGY4NWNlIn0=
151
+ ```
152
+
63
153
  ## AuthHeaderProcessor
64
154
 
65
155
  ```typescript
@@ -4,16 +4,16 @@ Options for the AuthHeaderProcessor constructor.
4
4
 
5
5
  ## Properties
6
6
 
7
- ### authenticationAdminServiceType? {#authenticationadminservicetype}
7
+ ### userEntityStorageType? {#userentitystoragetype}
8
8
 
9
- > `optional` **authenticationAdminServiceType?**: `string`
9
+ > `optional` **userEntityStorageType?**: `string`
10
10
 
11
- The admin service.
11
+ The entity storage for users.
12
12
 
13
13
  #### Default
14
14
 
15
15
  ```ts
16
- authentication-admin
16
+ authentication-user
17
17
  ```
18
18
 
19
19
  ***
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/api-auth-entity-storage-service",
3
- "version": "0.0.3-next.24",
3
+ "version": "0.0.3-next.26",
4
4
  "description": "Authentication service implementation and REST routes backed by entity storage.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,9 +14,9 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/api-auth-entity-storage-models": "0.0.3-next.24",
18
- "@twin.org/api-core": "0.0.3-next.24",
19
- "@twin.org/api-models": "0.0.3-next.24",
17
+ "@twin.org/api-auth-entity-storage-models": "0.0.3-next.26",
18
+ "@twin.org/api-core": "0.0.3-next.26",
19
+ "@twin.org/api-models": "0.0.3-next.26",
20
20
  "@twin.org/background-task-models": "next",
21
21
  "@twin.org/context": "next",
22
22
  "@twin.org/core": "next",