@twin.org/api-auth-entity-storage-service 0.0.3-next.38 → 0.0.3-next.39
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/dist/es/models/IAuthHeaderProcessorConstructorOptions.js.map +1 -1
- package/dist/es/models/IEntityStorageAuthenticationServiceConstructorOptions.js.map +1 -1
- package/dist/es/processors/authHeaderProcessor.js +30 -10
- package/dist/es/processors/authHeaderProcessor.js.map +1 -1
- package/dist/es/routes/entityStorageAuthenticationRoutes.js +1 -2
- package/dist/es/routes/entityStorageAuthenticationRoutes.js.map +1 -1
- package/dist/es/services/entityStorageAuthenticationService.js +32 -14
- package/dist/es/services/entityStorageAuthenticationService.js.map +1 -1
- package/dist/es/utils/tokenHelper.js +9 -20
- package/dist/es/utils/tokenHelper.js.map +1 -1
- package/dist/types/models/IAuthHeaderProcessorConstructorOptions.d.ts +9 -0
- package/dist/types/models/IEntityStorageAuthenticationServiceConstructorOptions.d.ts +9 -0
- package/dist/types/processors/authHeaderProcessor.d.ts +1 -1
- package/dist/types/utils/tokenHelper.d.ts +4 -9
- package/docs/changelog.md +16 -0
- package/docs/reference/classes/AuthHeaderProcessor.md +1 -1
- package/docs/reference/classes/TokenHelper.md +8 -25
- package/docs/reference/interfaces/IAuthHeaderProcessorConstructorOptions.md +22 -0
- package/docs/reference/interfaces/IEntityStorageAuthenticationServiceConstructorOptions.md +22 -0
- package/package.json +6 -6
|
@@ -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 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
|
+
{"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 URL transformer component for the tenants.\n\t */\n\turlTransformerComponentType?: string;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @default tenant-admin\n\t */\n\ttenantAdminComponentType?: string;\n\n\t/**\n\t * The configuration for the processor.\n\t */\n\tconfig?: IAuthHeaderProcessorConfig;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IEntityStorageAuthenticationServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IEntityStorageAuthenticationServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IEntityStorageAuthenticationServiceConfig } from \"./IEntityStorageAuthenticationServiceConfig.js\";\n\n/**\n * Options for the EntityStorageAuthenticationService constructor.\n */\nexport interface IEntityStorageAuthenticationServiceConstructorOptions {\n\t/**\n\t * The entity storage for the 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 audit service.\n\t * @default authentication-audit\n\t */\n\tauthenticationAuditServiceType?: string;\n\n\t/**\n\t * The rate service.\n\t * @default authentication-rate\n\t */\n\tauthenticationRateServiceType?: string;\n\n\t/**\n\t * The configuration for the authentication.\n\t */\n\tconfig?: IEntityStorageAuthenticationServiceConfig;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IEntityStorageAuthenticationServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IEntityStorageAuthenticationServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IEntityStorageAuthenticationServiceConfig } from \"./IEntityStorageAuthenticationServiceConfig.js\";\n\n/**\n * Options for the EntityStorageAuthenticationService constructor.\n */\nexport interface IEntityStorageAuthenticationServiceConstructorOptions {\n\t/**\n\t * The entity storage for the 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 URL transformer component for the tenants.\n\t */\n\turlTransformerComponentType?: string;\n\n\t/**\n\t * The audit service.\n\t * @default authentication-audit\n\t */\n\tauthenticationAuditServiceType?: string;\n\n\t/**\n\t * The rate service.\n\t * @default authentication-rate\n\t */\n\tauthenticationRateServiceType?: string;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @default tenant-admin\n\t */\n\ttenantAdminComponentType?: string;\n\n\t/**\n\t * The configuration for the authentication.\n\t */\n\tconfig?: IEntityStorageAuthenticationServiceConfig;\n}\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0.
|
|
3
3
|
import { HttpErrorHelper } from "@twin.org/api-models";
|
|
4
4
|
import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context";
|
|
5
|
-
import { BaseError, Coerce, Is } from "@twin.org/core";
|
|
5
|
+
import { BaseError, Coerce, ComponentFactory, Is } from "@twin.org/core";
|
|
6
6
|
import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
|
|
7
7
|
import { VaultConnectorFactory } from "@twin.org/vault-models";
|
|
8
8
|
import { CookieHelper, HeaderTypes, HttpStatusCode } from "@twin.org/web";
|
|
@@ -25,6 +25,16 @@ export class AuthHeaderProcessor {
|
|
|
25
25
|
* @internal
|
|
26
26
|
*/
|
|
27
27
|
_vaultConnector;
|
|
28
|
+
/**
|
|
29
|
+
* The transformer component, used to resolve public origins for tenants and encrypt/decrypt tenant tokens.
|
|
30
|
+
* @internal
|
|
31
|
+
*/
|
|
32
|
+
_urlTransformerService;
|
|
33
|
+
/**
|
|
34
|
+
* The component to retrieve tenant information.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
_tenantAdminComponent;
|
|
28
38
|
/**
|
|
29
39
|
* The entity storage for users.
|
|
30
40
|
* @internal
|
|
@@ -46,12 +56,14 @@ export class AuthHeaderProcessor {
|
|
|
46
56
|
*/
|
|
47
57
|
_nodeId;
|
|
48
58
|
/**
|
|
49
|
-
* Create a new instance of
|
|
59
|
+
* Create a new instance of AuthHeaderProcessor.
|
|
50
60
|
* @param options Options for the processor.
|
|
51
61
|
*/
|
|
52
62
|
constructor(options) {
|
|
53
63
|
this._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
|
|
64
|
+
this._urlTransformerService = ComponentFactory.get(options?.urlTransformerComponentType ?? "url-transformer");
|
|
54
65
|
this._userEntityStorage = EntityStorageConnectorFactory.get(options?.userEntityStorageType ?? "authentication-user");
|
|
66
|
+
this._tenantAdminComponent = ComponentFactory.getIfExists(options?.tenantAdminComponentType ?? "tenant-admin");
|
|
55
67
|
this._signingKeyName = options?.config?.signingKeyName ?? "auth-signing";
|
|
56
68
|
this._cookieName = options?.config?.cookieName ?? AuthHeaderProcessor.DEFAULT_COOKIE_NAME;
|
|
57
69
|
}
|
|
@@ -84,9 +96,23 @@ export class AuthHeaderProcessor {
|
|
|
84
96
|
if (!Is.empty(route) && !(route.skipAuth ?? false)) {
|
|
85
97
|
try {
|
|
86
98
|
const tokenAndLocation = TokenHelper.extractTokenFromHeaders(request.headers, this._cookieName);
|
|
87
|
-
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeId}/${this._signingKeyName}`, tokenAndLocation?.token, route.requiredScope, async (userIdentity, organizationIdentity,
|
|
99
|
+
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeId}/${this._signingKeyName}`, tokenAndLocation?.token, route.requiredScope, async (userIdentity, organizationIdentity, encryptedTenantId, passwordVersion) => {
|
|
88
100
|
const validParts = [];
|
|
89
|
-
|
|
101
|
+
// If the token carries an encrypted tenant ID and the admin component is available,
|
|
102
|
+
// decrypt and resolve the tenant first so the user lookup runs in the correct partition.
|
|
103
|
+
if (Is.stringValue(encryptedTenantId)) {
|
|
104
|
+
const tenantId = await this._urlTransformerService.decryptParam(encryptedTenantId);
|
|
105
|
+
if (Is.stringValue(tenantId)) {
|
|
106
|
+
const tenant = await this._tenantAdminComponent?.get(tenantId);
|
|
107
|
+
if (!Is.empty(tenant)) {
|
|
108
|
+
processorState.publicOrigin = tenant.publicOrigin;
|
|
109
|
+
validParts.push("tenant");
|
|
110
|
+
contextIds[ContextIdKeys.Tenant] = tenantId;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Wrap the user lookup in the request context so partitioned storage uses the correct tenant.
|
|
115
|
+
const user = await ContextIdStore.run(contextIds, async () => this._userEntityStorage.get(userIdentity, "identity"));
|
|
90
116
|
if (user?.identity === userIdentity &&
|
|
91
117
|
(passwordVersion ?? 0) === (user.passwordVersion ?? 0)) {
|
|
92
118
|
validParts.push("user");
|
|
@@ -94,12 +120,6 @@ export class AuthHeaderProcessor {
|
|
|
94
120
|
if (user?.organization === organizationIdentity) {
|
|
95
121
|
validParts.push("organization");
|
|
96
122
|
}
|
|
97
|
-
// Context tenant id might be undefined on a single tenant system,
|
|
98
|
-
// in this case the hash method will return undefined and the verification will pass as long
|
|
99
|
-
// as the token also has an undefined tenant id.
|
|
100
|
-
if (TokenHelper.hashTenantId(contextIds?.[ContextIdKeys.Tenant]) === hashedTenantId) {
|
|
101
|
-
validParts.push("tenant");
|
|
102
|
-
}
|
|
103
123
|
return validParts;
|
|
104
124
|
});
|
|
105
125
|
contextIds[ContextIdKeys.User] = headerAndPayload.payload?.sub;
|
|
@@ -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 {\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, 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 (\n\t\t\t\t\t\tuserIdentity: string,\n\t\t\t\t\t\torganizationIdentity: string,\n\t\t\t\t\t\thashedTenantId: string | undefined,\n\t\t\t\t\t\tpasswordVersion: number | undefined\n\t\t\t\t\t) => {\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 (\n\t\t\t\t\t\t\tuser?.identity === userIdentity &&\n\t\t\t\t\t\t\t(passwordVersion ?? 0) === (user.passwordVersion ?? 0)\n\t\t\t\t\t\t) {\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\n\t\t\t\t\t\t// Context tenant id might be undefined on a single tenant system,\n\t\t\t\t\t\t// in this case the hash method will return undefined and the verification will pass as long\n\t\t\t\t\t\t// as the token also has an undefined tenant id.\n\t\t\t\t\t\tif (TokenHelper.hashTenantId(contextIds?.[ContextIdKeys.Tenant]) === hashedTenantId) {\n\t\t\t\t\t\t\tvalidParts.push(\"tenant\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn validParts;\n\t\t\t\t\t}\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 IUrlTransformerComponent,\n\ttype IBaseRoute,\n\ttype IBaseRouteProcessor,\n\ttype IHttpResponse,\n\ttype IHttpServerRequest,\n\ttype ITenantAdminComponent\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, 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 transformer component, used to resolve public origins for tenants and encrypt/decrypt tenant tokens.\n\t * @internal\n\t */\n\tprivate readonly _urlTransformerService: IUrlTransformerComponent;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponent?: ITenantAdminComponent;\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 AuthHeaderProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: IAuthHeaderProcessorConstructorOptions) {\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\t\tthis._urlTransformerService = ComponentFactory.get(\n\t\t\toptions?.urlTransformerComponentType ?? \"url-transformer\"\n\t\t);\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\t\tthis._tenantAdminComponent = ComponentFactory.getIfExists<ITenantAdminComponent>(\n\t\t\toptions?.tenantAdminComponentType ?? \"tenant-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 (\n\t\t\t\t\t\tuserIdentity: string,\n\t\t\t\t\t\torganizationIdentity: string,\n\t\t\t\t\t\tencryptedTenantId: string | undefined,\n\t\t\t\t\t\tpasswordVersion: number | undefined\n\t\t\t\t\t) => {\n\t\t\t\t\t\tconst validParts = [];\n\n\t\t\t\t\t\t// If the token carries an encrypted tenant ID and the admin component is available,\n\t\t\t\t\t\t// decrypt and resolve the tenant first so the user lookup runs in the correct partition.\n\t\t\t\t\t\tif (Is.stringValue(encryptedTenantId)) {\n\t\t\t\t\t\t\tconst tenantId = await this._urlTransformerService.decryptParam(encryptedTenantId);\n\n\t\t\t\t\t\t\tif (Is.stringValue(tenantId)) {\n\t\t\t\t\t\t\t\tconst tenant = await this._tenantAdminComponent?.get(tenantId);\n\t\t\t\t\t\t\t\tif (!Is.empty(tenant)) {\n\t\t\t\t\t\t\t\t\tprocessorState.publicOrigin = tenant.publicOrigin;\n\t\t\t\t\t\t\t\t\tvalidParts.push(\"tenant\");\n\t\t\t\t\t\t\t\t\tcontextIds[ContextIdKeys.Tenant] = tenantId;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Wrap the user lookup in the request context so partitioned storage uses the correct tenant.\n\t\t\t\t\t\tconst user = await ContextIdStore.run(contextIds, async () =>\n\t\t\t\t\t\t\tthis._userEntityStorage.get(userIdentity, \"identity\")\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tuser?.identity === userIdentity &&\n\t\t\t\t\t\t\t(passwordVersion ?? 0) === (user.passwordVersion ?? 0)\n\t\t\t\t\t\t) {\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\n\t\t\t\t\t\treturn validParts;\n\t\t\t\t\t}\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entityStorageAuthenticationRoutes.js","sourceRoot":"","sources":["../../../src/routes/entityStorageAuthenticationRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationComponent,\n\tILoginRequest,\n\tILoginResponse,\n\tILogoutRequest,\n\tIRefreshTokenRequest,\n\tIRefreshTokenResponse,\n\tIUpdatePasswordRequest\n} from \"@twin.org/api-auth-entity-storage-models\";\nimport type {\n\tIHttpRequestContext,\n\tINoContentResponse,\n\tIRestRoute,\n\tIRestRouteResponseOptions,\n\tITag,\n\tIUnauthorizedResponse\n} from \"@twin.org/api-models\";\nimport { ComponentFactory, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HttpStatusCode } from \"@twin.org/web\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"authenticationRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsAuthentication: ITag[] = [\n\t{\n\t\tname: \"Authentication\",\n\t\tdescription: \"Authentication endpoints for the REST server.\"\n\t}\n];\n\n/**\n * The REST routes for authentication.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesAuthentication(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst loginRoute: IRestRoute<ILoginRequest, ILoginResponse> = {\n\t\toperationId: \"authenticationLogin\",\n\t\tsummary: \"Login to the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/login`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogin(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILoginRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"loginRequestExample\",\n\t\t\t\t\tdescription: \"The request to login to the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\temail: \"user@example.com\",\n\t\t\t\t\t\t\tpassword: \"MyPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ILoginResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"loginResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the login request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst logoutRoute: IRestRoute<ILogoutRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationLogout\",\n\t\tsummary: \"Logout from the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/logout`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogout(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILogoutRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"logoutRequestExample\",\n\t\t\t\t\tdescription: \"The request to logout from the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst refreshTokenRoute: IRestRoute<IRefreshTokenRequest, IRefreshTokenResponse> = {\n\t\toperationId: \"authenticationRefreshToken\",\n\t\tsummary: \"Refresh an authentication token\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/refresh`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationRefreshToken(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IRefreshTokenRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"refreshTokenRequestExample\",\n\t\t\t\t\tdescription: \"The request to refresh an auth token.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IRefreshTokenResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"refreshTokenResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the refresh token request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\tconst updatePasswordRoute: IRestRoute<IUpdatePasswordRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationUpdatePassword\",\n\t\tsummary: \"Update the current user's password\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"PUT\",\n\t\tpath: `${baseRouteName}/password`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationUpdatePassword(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IUpdatePasswordRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"updatePasswordRequestExample\",\n\t\t\t\t\tdescription: \"The request to update the current user's password.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\tcurrentPassword: \"MyNewPassword123!\",\n\t\t\t\t\t\t\tnewPassword: \"MyNewPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\treturn [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute];\n}\n\n/**\n * Login to the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogin(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILoginRequest\n): Promise<ILoginResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILoginRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<ILoginRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tconst result = await component.login(request.body.email, request.body.password);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"login\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: {\n\t\t\texpiry: result.expiry\n\t\t}\n\t};\n}\n\n/**\n * Logout from the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogout(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILogoutRequest\n): Promise<INoContentResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILogoutRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tawait component.logout(request.body?.token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"logout\";\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n\n/**\n * Refresh the login token.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationRefreshToken(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IRefreshTokenRequest\n): Promise<IRefreshTokenResponse & IRestRouteResponseOptions> {\n\tGuards.object<IRefreshTokenRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\t// If the token is not in the body, then maybe an auth processor has extracted it\n\t// and stored it in the processor state\n\tconst token = request.body?.token ?? (httpRequestContext.processorState.authToken as string);\n\tconst result = await component.refresh(token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"refresh\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: {\n\t\t\texpiry: result.expiry\n\t\t}\n\t};\n}\n\n/**\n * Update the user's password.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationUpdatePassword(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IUpdatePasswordRequest\n): Promise<INoContentResponse> {\n\tGuards.object<IUpdatePasswordRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<IUpdatePasswordRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\tawait component.updatePassword(request.body.currentPassword, request.body.newPassword);\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"entityStorageAuthenticationRoutes.js","sourceRoot":"","sources":["../../../src/routes/entityStorageAuthenticationRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationComponent,\n\tILoginRequest,\n\tILoginResponse,\n\tILogoutRequest,\n\tIRefreshTokenRequest,\n\tIRefreshTokenResponse,\n\tIUpdatePasswordRequest\n} from \"@twin.org/api-auth-entity-storage-models\";\nimport type {\n\tIHttpRequestContext,\n\tINoContentResponse,\n\tIRestRoute,\n\tIRestRouteResponseOptions,\n\tITag,\n\tIUnauthorizedResponse\n} from \"@twin.org/api-models\";\nimport { ComponentFactory, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HttpStatusCode } from \"@twin.org/web\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"authenticationRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsAuthentication: ITag[] = [\n\t{\n\t\tname: \"Authentication\",\n\t\tdescription: \"Authentication endpoints for the REST server.\"\n\t}\n];\n\n/**\n * The REST routes for authentication.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesAuthentication(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst loginRoute: IRestRoute<ILoginRequest, ILoginResponse> = {\n\t\toperationId: \"authenticationLogin\",\n\t\tsummary: \"Login to the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/login`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogin(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILoginRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"loginRequestExample\",\n\t\t\t\t\tdescription: \"The request to login to the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\temail: \"user@example.com\",\n\t\t\t\t\t\t\tpassword: \"MyPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ILoginResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"loginResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the login request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst logoutRoute: IRestRoute<ILogoutRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationLogout\",\n\t\tsummary: \"Logout from the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/logout`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogout(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILogoutRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"logoutRequestExample\",\n\t\t\t\t\tdescription: \"The request to logout from the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\tconst refreshTokenRoute: IRestRoute<IRefreshTokenRequest, IRefreshTokenResponse> = {\n\t\toperationId: \"authenticationRefreshToken\",\n\t\tsummary: \"Refresh an authentication token\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/refresh`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationRefreshToken(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IRefreshTokenRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"refreshTokenRequestExample\",\n\t\t\t\t\tdescription: \"The request to refresh an auth token.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IRefreshTokenResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"refreshTokenResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the refresh token request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\tconst updatePasswordRoute: IRestRoute<IUpdatePasswordRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationUpdatePassword\",\n\t\tsummary: \"Update the current user's password\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"PUT\",\n\t\tpath: `${baseRouteName}/password`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationUpdatePassword(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IUpdatePasswordRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"updatePasswordRequestExample\",\n\t\t\t\t\tdescription: \"The request to update the current user's password.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\tcurrentPassword: \"MyNewPassword123!\",\n\t\t\t\t\t\t\tnewPassword: \"MyNewPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\treturn [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute];\n}\n\n/**\n * Login to the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogin(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILoginRequest\n): Promise<ILoginResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILoginRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<ILoginRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tconst result = await component.login(request.body.email, request.body.password);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"login\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: {\n\t\t\texpiry: result.expiry\n\t\t}\n\t};\n}\n\n/**\n * Logout from the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogout(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILogoutRequest\n): Promise<INoContentResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILogoutRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tawait component.logout(request.body?.token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"logout\";\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n\n/**\n * Refresh the login token.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationRefreshToken(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IRefreshTokenRequest\n): Promise<IRefreshTokenResponse & IRestRouteResponseOptions> {\n\tGuards.object<IRefreshTokenRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\t// If the token is not in the body, then maybe an auth processor has extracted it\n\t// and stored it in the processor state\n\tconst token = request.body?.token ?? (httpRequestContext.processorState.authToken as string);\n\tconst result = await component.refresh(token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"refresh\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: {\n\t\t\texpiry: result.expiry\n\t\t}\n\t};\n}\n\n/**\n * Update the user's password.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationUpdatePassword(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IUpdatePasswordRequest\n): Promise<INoContentResponse> {\n\tGuards.object<IUpdatePasswordRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<IUpdatePasswordRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\tawait component.updatePassword(request.body.currentPassword, request.body.newPassword);\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n"]}
|
|
@@ -69,6 +69,11 @@ export class EntityStorageAuthenticationService {
|
|
|
69
69
|
* @internal
|
|
70
70
|
*/
|
|
71
71
|
_vaultConnector;
|
|
72
|
+
/**
|
|
73
|
+
* The transformer component, used to resolve public origins for tenants and encrypt/decrypt tenant tokens.
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
_urlTransformerService;
|
|
72
77
|
/**
|
|
73
78
|
* The name of the key to retrieve from the vault for signing JWT.
|
|
74
79
|
* @internal
|
|
@@ -99,6 +104,11 @@ export class EntityStorageAuthenticationService {
|
|
|
99
104
|
* @internal
|
|
100
105
|
*/
|
|
101
106
|
_tokenRefreshRateLimit;
|
|
107
|
+
/**
|
|
108
|
+
* The component to retrieve tenant information.
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
_tenantAdminComponent;
|
|
102
112
|
/**
|
|
103
113
|
* The node identity.
|
|
104
114
|
* @internal
|
|
@@ -111,8 +121,10 @@ export class EntityStorageAuthenticationService {
|
|
|
111
121
|
constructor(options) {
|
|
112
122
|
this._userEntityStorage = EntityStorageConnectorFactory.get(options?.userEntityStorageType ?? "authentication-user");
|
|
113
123
|
this._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
|
|
124
|
+
this._urlTransformerService = ComponentFactory.get(options?.urlTransformerComponentType ?? "url-transformer");
|
|
114
125
|
this._authenticationAuditService = ComponentFactory.getIfExists(options?.authenticationAuditServiceType ?? "authentication-audit");
|
|
115
126
|
this._authenticationRateService = ComponentFactory.get(options?.authenticationRateServiceType ?? "authentication-rate");
|
|
127
|
+
this._tenantAdminComponent = ComponentFactory.getIfExists(options?.tenantAdminComponentType ?? "tenant-admin");
|
|
116
128
|
this._signingKeyName = options?.config?.signingKeyName ?? "auth-signing";
|
|
117
129
|
this._defaultTtlMinutes =
|
|
118
130
|
options?.config?.defaultTtlMinutes ?? EntityStorageAuthenticationService._DEFAULT_TTL_MINUTES;
|
|
@@ -195,7 +207,7 @@ export class EntityStorageAuthenticationService {
|
|
|
195
207
|
// the context
|
|
196
208
|
const contextIds = await ContextIdStore.getContextIds();
|
|
197
209
|
loginTenantId = contextIds?.[ContextIdKeys.Tenant];
|
|
198
|
-
tokenAndExpiry = await TokenHelper.createToken(this._vaultConnector, `${this._nodeId}/${this._signingKeyName}`, user.identity, user.organization, loginTenantId, this._defaultTtlMinutes, user.scope, user.passwordVersion ?? 0);
|
|
210
|
+
tokenAndExpiry = await TokenHelper.createToken(this._vaultConnector, this._urlTransformerService, `${this._nodeId}/${this._signingKeyName}`, user.identity, user.organization, loginTenantId, this._defaultTtlMinutes, user.scope, user.passwordVersion ?? 0);
|
|
199
211
|
loginUser = user;
|
|
200
212
|
}
|
|
201
213
|
catch (error) {
|
|
@@ -242,10 +254,25 @@ export class EntityStorageAuthenticationService {
|
|
|
242
254
|
*/
|
|
243
255
|
async refresh(token) {
|
|
244
256
|
let refreshPasswordVersion;
|
|
257
|
+
let tenantId;
|
|
245
258
|
// If the verify fails on the current token then it will throw an exception.
|
|
246
|
-
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeId}/${this._signingKeyName}`, token, undefined, async (userIdentity, organizationIdentity,
|
|
259
|
+
const headerAndPayload = await TokenHelper.verify(this._vaultConnector, `${this._nodeId}/${this._signingKeyName}`, token, undefined, async (userIdentity, organizationIdentity, encryptedTenantId, passwordVersion) => {
|
|
247
260
|
const validParts = [];
|
|
248
|
-
const
|
|
261
|
+
const contextIds = (await ContextIdStore.getContextIds()) ?? {};
|
|
262
|
+
// If the token carries an encrypted tenant ID and the admin component is available,
|
|
263
|
+
// decrypt and resolve the tenant first so the user lookup runs in the correct partition.
|
|
264
|
+
if (Is.stringValue(encryptedTenantId)) {
|
|
265
|
+
tenantId = await this._urlTransformerService.decryptParam(encryptedTenantId);
|
|
266
|
+
if (Is.stringValue(tenantId)) {
|
|
267
|
+
const tenant = await this._tenantAdminComponent?.get(tenantId);
|
|
268
|
+
if (!Is.empty(tenant)) {
|
|
269
|
+
validParts.push("tenant");
|
|
270
|
+
contextIds[ContextIdKeys.Tenant] = tenantId;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Wrap the user lookup in the request context so partitioned storage uses the correct tenant.
|
|
275
|
+
const user = await ContextIdStore.run(contextIds, async () => this._userEntityStorage.get(userIdentity, "identity"));
|
|
249
276
|
refreshPasswordVersion = user?.passwordVersion;
|
|
250
277
|
if (user?.identity === userIdentity &&
|
|
251
278
|
(passwordVersion ?? 0) === (refreshPasswordVersion ?? 0)) {
|
|
@@ -254,18 +281,11 @@ export class EntityStorageAuthenticationService {
|
|
|
254
281
|
if (user?.organization === organizationIdentity) {
|
|
255
282
|
validParts.push("organization");
|
|
256
283
|
}
|
|
257
|
-
// Context tenant id might be undefined on a single tenant system,
|
|
258
|
-
// in this case the hash method will return undefined and the verification will pass as long
|
|
259
|
-
// as the token also has an undefined tenant id.
|
|
260
|
-
const contextIds = await ContextIdStore.getContextIds();
|
|
261
|
-
if (TokenHelper.hashTenantId(contextIds?.[ContextIdKeys.Tenant]) === hashedTenantId) {
|
|
262
|
-
validParts.push("tenant");
|
|
263
|
-
}
|
|
264
284
|
return validParts;
|
|
265
285
|
});
|
|
266
286
|
const refreshSub = headerAndPayload.payload.sub ?? "";
|
|
267
287
|
await this._authenticationRateService.check("token-refresh", refreshSub);
|
|
268
|
-
const refreshTokenAndExpiry = await TokenHelper.createToken(this._vaultConnector, `${this._nodeId}/${this._signingKeyName}`, refreshSub, Is.stringValue(headerAndPayload.payload.org) ? headerAndPayload.payload.org : "",
|
|
288
|
+
const refreshTokenAndExpiry = await TokenHelper.createToken(this._vaultConnector, this._urlTransformerService, `${this._nodeId}/${this._signingKeyName}`, refreshSub, Is.stringValue(headerAndPayload.payload.org) ? headerAndPayload.payload.org : "", tenantId, this._defaultTtlMinutes, Coerce.string(headerAndPayload.payload?.scope), refreshPasswordVersion ?? 0);
|
|
269
289
|
const refreshScope = Coerce.string(headerAndPayload.payload?.scope) ?? "";
|
|
270
290
|
await this._authenticationAuditService?.create({
|
|
271
291
|
actorId: refreshSub,
|
|
@@ -274,9 +294,7 @@ export class EntityStorageAuthenticationService {
|
|
|
274
294
|
organizationIdentity: Is.stringValue(headerAndPayload.payload.org)
|
|
275
295
|
? headerAndPayload.payload.org
|
|
276
296
|
: "",
|
|
277
|
-
tenantId
|
|
278
|
-
? headerAndPayload.payload.tid
|
|
279
|
-
: undefined,
|
|
297
|
+
tenantId,
|
|
280
298
|
scope: refreshScope.split(",").filter(scope => scope.length > 0)
|
|
281
299
|
}
|
|
282
300
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entityStorageAuthenticationService.js","sourceRoot":"","sources":["../../../src/services/entityStorageAuthenticationService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationRateActionConfig,\n\tIAuthenticationRateComponent,\n\tIAuthenticationAuditComponent,\n\tIAuthenticationComponent\n} from \"@twin.org/api-auth-entity-storage-models\";\nimport { AuthAuditEvent } from \"@twin.org/api-auth-entity-storage-models\";\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tNotFoundError,\n\tUnauthorizedError\n} from \"@twin.org/core\";\nimport { PasswordGenerator, PasswordValidator } from \"@twin.org/crypto\";\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 type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IEntityStorageAuthenticationServiceConstructorOptions } from \"../models/IEntityStorageAuthenticationServiceConstructorOptions.js\";\nimport { PasswordHelper } from \"../utils/passwordHelper.js\";\nimport { TokenHelper } from \"../utils/tokenHelper.js\";\n\n/**\n * Implementation of the authentication component using entity storage.\n */\nexport class EntityStorageAuthenticationService implements IAuthenticationComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageAuthenticationService>();\n\n\t/**\n\t * Default TTL in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TTL_MINUTES: number = 60;\n\n\t/**\n\t * Default maximum login attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOGIN_RATE_MAX_ATTEMPTS: number = 5;\n\n\t/**\n\t * Default login rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOGIN_RATE_WINDOW_MINUTES: number = 15;\n\n\t/**\n\t * Default maximum password change attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_PASSWORD_CHANGE_RATE_MAX_ATTEMPTS: number = 5;\n\n\t/**\n\t * Default password change rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_PASSWORD_CHANGE_RATE_WINDOW_MINUTES: number = 15;\n\n\t/**\n\t * Default maximum token refresh attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_REFRESH_RATE_MAX_ATTEMPTS: number = 30;\n\n\t/**\n\t * Default token refresh rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_REFRESH_RATE_WINDOW_MINUTES: number = 60;\n\n\t/**\n\t * The audit service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationAuditService?: IAuthenticationAuditComponent;\n\n\t/**\n\t * The rate service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationRateService: IAuthenticationRateComponent;\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 vault for the keys.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\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 default TTL for the token.\n\t * @internal\n\t */\n\tprivate readonly _defaultTtlMinutes: number;\n\n\t/**\n\t * The minimum password length for validation.\n\t * @internal\n\t */\n\tprivate readonly _minPasswordLength?: number;\n\n\t/**\n\t * Rate limit configuration for login failures.\n\t * @internal\n\t */\n\tprivate readonly _loginRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Rate limit configuration for password changes.\n\t * @internal\n\t */\n\tprivate readonly _passwordChangeRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Rate limit configuration for token refresh.\n\t * @internal\n\t */\n\tprivate readonly _tokenRefreshRateLimit: IAuthenticationRateActionConfig;\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 EntityStorageAuthentication.\n\t * @param options The dependencies for the identity connector.\n\t */\n\tconstructor(options?: IEntityStorageAuthenticationServiceConstructorOptions) {\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\n\t\tthis._authenticationAuditService = ComponentFactory.getIfExists<IAuthenticationAuditComponent>(\n\t\t\toptions?.authenticationAuditServiceType ?? \"authentication-audit\"\n\t\t);\n\n\t\tthis._authenticationRateService = ComponentFactory.get<IAuthenticationRateComponent>(\n\t\t\toptions?.authenticationRateServiceType ?? \"authentication-rate\"\n\t\t);\n\n\t\tthis._signingKeyName = options?.config?.signingKeyName ?? \"auth-signing\";\n\t\tthis._defaultTtlMinutes =\n\t\t\toptions?.config?.defaultTtlMinutes ?? EntityStorageAuthenticationService._DEFAULT_TTL_MINUTES;\n\t\tthis._minPasswordLength = options?.config?.minPasswordLength;\n\t\tthis._loginRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.loginRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_LOGIN_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.loginRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_LOGIN_RATE_WINDOW_MINUTES\n\t\t};\n\t\tthis._passwordChangeRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.passwordChangeRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_PASSWORD_CHANGE_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.passwordChangeRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_PASSWORD_CHANGE_RATE_WINDOW_MINUTES\n\t\t};\n\t\tthis._tokenRefreshRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.tokenRefreshRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_TOKEN_REFRESH_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.tokenRefreshRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_TOKEN_REFRESH_RATE_WINDOW_MINUTES\n\t\t};\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 EntityStorageAuthenticationService.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\n\t\tawait this._authenticationRateService.registerAction(\"login\", this._loginRateLimit);\n\t\tawait this._authenticationRateService.registerAction(\n\t\t\t\"password-change\",\n\t\t\tthis._passwordChangeRateLimit\n\t\t);\n\t\tawait this._authenticationRateService.registerAction(\n\t\t\t\"token-refresh\",\n\t\t\tthis._tokenRefreshRateLimit\n\t\t);\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tawait this._authenticationRateService.unregisterAction(\"login\");\n\t\tawait this._authenticationRateService.unregisterAction(\"password-change\");\n\t\tawait this._authenticationRateService.unregisterAction(\"token-refresh\");\n\t}\n\n\t/**\n\t * Perform a login for the user.\n\t * @param email The email address for the user.\n\t * @param password The password for the user.\n\t * @returns The authentication token for the user, if it uses a mechanism with public access.\n\t */\n\tpublic async login(\n\t\temail: string,\n\t\tpassword: string\n\t): Promise<{\n\t\ttoken?: string;\n\t\texpiry: number;\n\t}> {\n\t\tGuards.stringValue(EntityStorageAuthenticationService.CLASS_NAME, nameof(email), email);\n\t\tGuards.stringValue(EntityStorageAuthenticationService.CLASS_NAME, nameof(password), password);\n\n\t\tlet loginUser: AuthenticationUser | undefined;\n\t\tlet loginTenantId: string | undefined;\n\t\tlet tokenAndExpiry: { token?: string; expiry: number } | undefined;\n\n\t\ttry {\n\t\t\tawait this._authenticationRateService.check(\"login\", email);\n\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!user) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"userNotFound\");\n\t\t\t}\n\n\t\t\tconst saltBytes = Converter.base64ToBytes(user.salt);\n\t\t\tconst passwordBytes = Converter.utf8ToBytes(password);\n\n\t\t\tconst hashedPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);\n\n\t\t\tif (!PasswordValidator.comparePasswordHashes(hashedPassword, user.password)) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"passwordMismatch\");\n\t\t\t}\n\n\t\t\t// This might be undefined if the login is performed in a single tenant context\n\t\t\t// it is verified during the token processing, tenant id will be matched against\n\t\t\t// the context\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tloginTenantId = contextIds?.[ContextIdKeys.Tenant];\n\n\t\t\ttokenAndExpiry = await TokenHelper.createToken(\n\t\t\t\tthis._vaultConnector,\n\t\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\t\tuser.identity,\n\t\t\t\tuser.organization,\n\t\t\t\tloginTenantId,\n\t\t\t\tthis._defaultTtlMinutes,\n\t\t\t\tuser.scope,\n\t\t\t\tuser.passwordVersion ?? 0\n\t\t\t);\n\t\t\tloginUser = user;\n\t\t} catch (error) {\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: email,\n\t\t\t\tevent: AuthAuditEvent.LoginFailure\n\t\t\t});\n\n\t\t\tthrow new UnauthorizedError(\n\t\t\t\tEntityStorageAuthenticationService.CLASS_NAME,\n\t\t\t\t\"loginFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\n\t\tawait this._authenticationRateService.clear(\"login\", email);\n\n\t\tawait this._authenticationAuditService?.create({\n\t\t\tactorId: email,\n\t\t\tevent: AuthAuditEvent.LoginSuccess,\n\t\t\tdata: {\n\t\t\t\tuserIdentity: loginUser.identity,\n\t\t\t\torganizationIdentity: loginUser.organization,\n\t\t\t\ttenantId: loginTenantId,\n\t\t\t\tscope: loginUser.scope.split(\",\")\n\t\t\t}\n\t\t});\n\n\t\treturn tokenAndExpiry;\n\t}\n\n\t/**\n\t * Logout the current user.\n\t * @param token The token to logout, if it uses a mechanism with public access.\n\t * @returns Nothing.\n\t */\n\tpublic async logout(token?: string): Promise<void> {\n\t\t// Nothing to do here, as we are stateless.\n\t\t// The cookie will be revoked by the REST route handling\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst identifier = contextIds?.[ContextIdKeys.User];\n\t\tif (Is.stringValue(identifier)) {\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: identifier,\n\t\t\t\tevent: AuthAuditEvent.Logout\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Refresh the token.\n\t * @param token The token to refresh, if it uses a mechanism with public access.\n\t * @returns The refreshed token, if it uses a mechanism with public access.\n\t */\n\tpublic async refresh(token?: string): Promise<{\n\t\ttoken?: string;\n\t\texpiry: number;\n\t}> {\n\t\tlet refreshPasswordVersion: number | undefined;\n\n\t\t// If the verify fails on the current token then it will throw an exception.\n\t\tconst headerAndPayload = await TokenHelper.verify(\n\t\t\tthis._vaultConnector,\n\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\ttoken,\n\t\t\tundefined,\n\t\t\tasync (userIdentity, organizationIdentity, hashedTenantId, passwordVersion) => {\n\t\t\t\tconst validParts = [];\n\t\t\t\tconst user = await this._userEntityStorage.get(userIdentity, \"identity\");\n\t\t\t\trefreshPasswordVersion = user?.passwordVersion;\n\t\t\t\tif (\n\t\t\t\t\tuser?.identity === userIdentity &&\n\t\t\t\t\t(passwordVersion ?? 0) === (refreshPasswordVersion ?? 0)\n\t\t\t\t) {\n\t\t\t\t\tvalidParts.push(\"user\");\n\t\t\t\t}\n\t\t\t\tif (user?.organization === organizationIdentity) {\n\t\t\t\t\tvalidParts.push(\"organization\");\n\t\t\t\t}\n\t\t\t\t// Context tenant id might be undefined on a single tenant system,\n\t\t\t\t// in this case the hash method will return undefined and the verification will pass as long\n\t\t\t\t// as the token also has an undefined tenant id.\n\t\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\t\tif (TokenHelper.hashTenantId(contextIds?.[ContextIdKeys.Tenant]) === hashedTenantId) {\n\t\t\t\t\tvalidParts.push(\"tenant\");\n\t\t\t\t}\n\t\t\t\treturn validParts;\n\t\t\t}\n\t\t);\n\n\t\tconst refreshSub = headerAndPayload.payload.sub ?? \"\";\n\t\tawait this._authenticationRateService.check(\"token-refresh\", refreshSub);\n\n\t\tconst refreshTokenAndExpiry = await TokenHelper.createToken(\n\t\t\tthis._vaultConnector,\n\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\trefreshSub,\n\t\t\tIs.stringValue(headerAndPayload.payload.org) ? headerAndPayload.payload.org : \"\",\n\t\t\tIs.string(headerAndPayload.payload.tid) ? headerAndPayload.payload.tid : undefined,\n\t\t\tthis._defaultTtlMinutes,\n\t\t\tCoerce.string(headerAndPayload.payload?.scope),\n\t\t\trefreshPasswordVersion ?? 0\n\t\t);\n\t\tconst refreshScope = Coerce.string(headerAndPayload.payload?.scope) ?? \"\";\n\n\t\tawait this._authenticationAuditService?.create({\n\t\t\tactorId: refreshSub,\n\t\t\tevent: AuthAuditEvent.TokenRefreshed,\n\t\t\tdata: {\n\t\t\t\torganizationIdentity: Is.stringValue(headerAndPayload.payload.org)\n\t\t\t\t\t? headerAndPayload.payload.org\n\t\t\t\t\t: \"\",\n\t\t\t\ttenantId: Is.string(headerAndPayload.payload.tid)\n\t\t\t\t\t? headerAndPayload.payload.tid\n\t\t\t\t\t: undefined,\n\t\t\t\tscope: refreshScope.split(\",\").filter(scope => scope.length > 0)\n\t\t\t}\n\t\t});\n\n\t\treturn refreshTokenAndExpiry;\n\t}\n\n\t/**\n\t * Update the user's password.\n\t * @param currentPassword The current password for the user.\n\t * @param newPassword The new password for the user.\n\t * @returns Nothing.\n\t */\n\tpublic async updatePassword(currentPassword: string, newPassword: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.User);\n\n\t\tconst userIdentity = contextIds[ContextIdKeys.User];\n\t\tawait this._authenticationRateService.check(\"password-change\", userIdentity);\n\n\t\tconst user = await this._userEntityStorage.get(userIdentity, \"identity\");\n\t\tif (!Is.object<AuthenticationUser>(user)) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tEntityStorageAuthenticationService.CLASS_NAME,\n\t\t\t\t\"userNotFound\",\n\t\t\t\tuserIdentity\n\t\t\t);\n\t\t}\n\n\t\tawait PasswordHelper.updatePassword(\n\t\t\tthis._userEntityStorage,\n\t\t\tthis._authenticationAuditService,\n\t\t\tuser,\n\t\t\tnewPassword,\n\t\t\tcurrentPassword,\n\t\t\tthis._minPasswordLength\n\t\t);\n\n\t\tawait this._authenticationRateService.clear(\"password-change\", userIdentity);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"entityStorageAuthenticationService.js","sourceRoot":"","sources":["../../../src/services/entityStorageAuthenticationService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationRateActionConfig,\n\tIAuthenticationRateComponent,\n\tIAuthenticationAuditComponent,\n\tIAuthenticationComponent\n} from \"@twin.org/api-auth-entity-storage-models\";\nimport { AuthAuditEvent } from \"@twin.org/api-auth-entity-storage-models\";\nimport type { ITenantAdminComponent, IUrlTransformerComponent } from \"@twin.org/api-models\";\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tNotFoundError,\n\tUnauthorizedError\n} from \"@twin.org/core\";\nimport { PasswordGenerator, PasswordValidator } from \"@twin.org/crypto\";\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 type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IEntityStorageAuthenticationServiceConstructorOptions } from \"../models/IEntityStorageAuthenticationServiceConstructorOptions.js\";\nimport { PasswordHelper } from \"../utils/passwordHelper.js\";\nimport { TokenHelper } from \"../utils/tokenHelper.js\";\n\n/**\n * Implementation of the authentication component using entity storage.\n */\nexport class EntityStorageAuthenticationService implements IAuthenticationComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageAuthenticationService>();\n\n\t/**\n\t * Default TTL in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TTL_MINUTES: number = 60;\n\n\t/**\n\t * Default maximum login attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOGIN_RATE_MAX_ATTEMPTS: number = 5;\n\n\t/**\n\t * Default login rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOGIN_RATE_WINDOW_MINUTES: number = 15;\n\n\t/**\n\t * Default maximum password change attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_PASSWORD_CHANGE_RATE_MAX_ATTEMPTS: number = 5;\n\n\t/**\n\t * Default password change rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_PASSWORD_CHANGE_RATE_WINDOW_MINUTES: number = 15;\n\n\t/**\n\t * Default maximum token refresh attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_REFRESH_RATE_MAX_ATTEMPTS: number = 30;\n\n\t/**\n\t * Default token refresh rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_REFRESH_RATE_WINDOW_MINUTES: number = 60;\n\n\t/**\n\t * The audit service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationAuditService?: IAuthenticationAuditComponent;\n\n\t/**\n\t * The rate service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationRateService: IAuthenticationRateComponent;\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 vault for the keys.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The transformer component, used to resolve public origins for tenants and encrypt/decrypt tenant tokens.\n\t * @internal\n\t */\n\tprivate readonly _urlTransformerService: IUrlTransformerComponent;\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 default TTL for the token.\n\t * @internal\n\t */\n\tprivate readonly _defaultTtlMinutes: number;\n\n\t/**\n\t * The minimum password length for validation.\n\t * @internal\n\t */\n\tprivate readonly _minPasswordLength?: number;\n\n\t/**\n\t * Rate limit configuration for login failures.\n\t * @internal\n\t */\n\tprivate readonly _loginRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Rate limit configuration for password changes.\n\t * @internal\n\t */\n\tprivate readonly _passwordChangeRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Rate limit configuration for token refresh.\n\t * @internal\n\t */\n\tprivate readonly _tokenRefreshRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponent?: ITenantAdminComponent;\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 EntityStorageAuthentication.\n\t * @param options The dependencies for the identity connector.\n\t */\n\tconstructor(options?: IEntityStorageAuthenticationServiceConstructorOptions) {\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\n\t\tthis._urlTransformerService = ComponentFactory.get(\n\t\t\toptions?.urlTransformerComponentType ?? \"url-transformer\"\n\t\t);\n\n\t\tthis._authenticationAuditService = ComponentFactory.getIfExists<IAuthenticationAuditComponent>(\n\t\t\toptions?.authenticationAuditServiceType ?? \"authentication-audit\"\n\t\t);\n\n\t\tthis._authenticationRateService = ComponentFactory.get<IAuthenticationRateComponent>(\n\t\t\toptions?.authenticationRateServiceType ?? \"authentication-rate\"\n\t\t);\n\n\t\tthis._tenantAdminComponent = ComponentFactory.getIfExists<ITenantAdminComponent>(\n\t\t\toptions?.tenantAdminComponentType ?? \"tenant-admin\"\n\t\t);\n\n\t\tthis._signingKeyName = options?.config?.signingKeyName ?? \"auth-signing\";\n\t\tthis._defaultTtlMinutes =\n\t\t\toptions?.config?.defaultTtlMinutes ?? EntityStorageAuthenticationService._DEFAULT_TTL_MINUTES;\n\t\tthis._minPasswordLength = options?.config?.minPasswordLength;\n\t\tthis._loginRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.loginRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_LOGIN_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.loginRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_LOGIN_RATE_WINDOW_MINUTES\n\t\t};\n\t\tthis._passwordChangeRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.passwordChangeRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_PASSWORD_CHANGE_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.passwordChangeRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_PASSWORD_CHANGE_RATE_WINDOW_MINUTES\n\t\t};\n\t\tthis._tokenRefreshRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.tokenRefreshRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_TOKEN_REFRESH_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.tokenRefreshRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_TOKEN_REFRESH_RATE_WINDOW_MINUTES\n\t\t};\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 EntityStorageAuthenticationService.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\n\t\tawait this._authenticationRateService.registerAction(\"login\", this._loginRateLimit);\n\t\tawait this._authenticationRateService.registerAction(\n\t\t\t\"password-change\",\n\t\t\tthis._passwordChangeRateLimit\n\t\t);\n\t\tawait this._authenticationRateService.registerAction(\n\t\t\t\"token-refresh\",\n\t\t\tthis._tokenRefreshRateLimit\n\t\t);\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tawait this._authenticationRateService.unregisterAction(\"login\");\n\t\tawait this._authenticationRateService.unregisterAction(\"password-change\");\n\t\tawait this._authenticationRateService.unregisterAction(\"token-refresh\");\n\t}\n\n\t/**\n\t * Perform a login for the user.\n\t * @param email The email address for the user.\n\t * @param password The password for the user.\n\t * @returns The authentication token for the user, if it uses a mechanism with public access.\n\t */\n\tpublic async login(\n\t\temail: string,\n\t\tpassword: string\n\t): Promise<{\n\t\ttoken?: string;\n\t\texpiry: number;\n\t}> {\n\t\tGuards.stringValue(EntityStorageAuthenticationService.CLASS_NAME, nameof(email), email);\n\t\tGuards.stringValue(EntityStorageAuthenticationService.CLASS_NAME, nameof(password), password);\n\n\t\tlet loginUser: AuthenticationUser | undefined;\n\t\tlet loginTenantId: string | undefined;\n\t\tlet tokenAndExpiry: { token?: string; expiry: number } | undefined;\n\n\t\ttry {\n\t\t\tawait this._authenticationRateService.check(\"login\", email);\n\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!user) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"userNotFound\");\n\t\t\t}\n\n\t\t\tconst saltBytes = Converter.base64ToBytes(user.salt);\n\t\t\tconst passwordBytes = Converter.utf8ToBytes(password);\n\n\t\t\tconst hashedPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);\n\n\t\t\tif (!PasswordValidator.comparePasswordHashes(hashedPassword, user.password)) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"passwordMismatch\");\n\t\t\t}\n\n\t\t\t// This might be undefined if the login is performed in a single tenant context\n\t\t\t// it is verified during the token processing, tenant id will be matched against\n\t\t\t// the context\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tloginTenantId = contextIds?.[ContextIdKeys.Tenant];\n\n\t\t\ttokenAndExpiry = await TokenHelper.createToken(\n\t\t\t\tthis._vaultConnector,\n\t\t\t\tthis._urlTransformerService,\n\t\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\t\tuser.identity,\n\t\t\t\tuser.organization,\n\t\t\t\tloginTenantId,\n\t\t\t\tthis._defaultTtlMinutes,\n\t\t\t\tuser.scope,\n\t\t\t\tuser.passwordVersion ?? 0\n\t\t\t);\n\t\t\tloginUser = user;\n\t\t} catch (error) {\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: email,\n\t\t\t\tevent: AuthAuditEvent.LoginFailure\n\t\t\t});\n\n\t\t\tthrow new UnauthorizedError(\n\t\t\t\tEntityStorageAuthenticationService.CLASS_NAME,\n\t\t\t\t\"loginFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\n\t\tawait this._authenticationRateService.clear(\"login\", email);\n\n\t\tawait this._authenticationAuditService?.create({\n\t\t\tactorId: email,\n\t\t\tevent: AuthAuditEvent.LoginSuccess,\n\t\t\tdata: {\n\t\t\t\tuserIdentity: loginUser.identity,\n\t\t\t\torganizationIdentity: loginUser.organization,\n\t\t\t\ttenantId: loginTenantId,\n\t\t\t\tscope: loginUser.scope.split(\",\")\n\t\t\t}\n\t\t});\n\n\t\treturn tokenAndExpiry;\n\t}\n\n\t/**\n\t * Logout the current user.\n\t * @param token The token to logout, if it uses a mechanism with public access.\n\t * @returns Nothing.\n\t */\n\tpublic async logout(token?: string): Promise<void> {\n\t\t// Nothing to do here, as we are stateless.\n\t\t// The cookie will be revoked by the REST route handling\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst identifier = contextIds?.[ContextIdKeys.User];\n\t\tif (Is.stringValue(identifier)) {\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: identifier,\n\t\t\t\tevent: AuthAuditEvent.Logout\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Refresh the token.\n\t * @param token The token to refresh, if it uses a mechanism with public access.\n\t * @returns The refreshed token, if it uses a mechanism with public access.\n\t */\n\tpublic async refresh(token?: string): Promise<{\n\t\ttoken?: string;\n\t\texpiry: number;\n\t}> {\n\t\tlet refreshPasswordVersion: number | undefined;\n\t\tlet tenantId: string | undefined;\n\n\t\t// If the verify fails on the current token then it will throw an exception.\n\t\tconst headerAndPayload = await TokenHelper.verify(\n\t\t\tthis._vaultConnector,\n\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\ttoken,\n\t\t\tundefined,\n\t\t\tasync (userIdentity, organizationIdentity, encryptedTenantId, passwordVersion) => {\n\t\t\t\tconst validParts = [];\n\n\t\t\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\n\t\t\t\t// If the token carries an encrypted tenant ID and the admin component is available,\n\t\t\t\t// decrypt and resolve the tenant first so the user lookup runs in the correct partition.\n\t\t\t\tif (Is.stringValue(encryptedTenantId)) {\n\t\t\t\t\ttenantId = await this._urlTransformerService.decryptParam(encryptedTenantId);\n\t\t\t\t\tif (Is.stringValue(tenantId)) {\n\t\t\t\t\t\tconst tenant = await this._tenantAdminComponent?.get(tenantId);\n\t\t\t\t\t\tif (!Is.empty(tenant)) {\n\t\t\t\t\t\t\tvalidParts.push(\"tenant\");\n\t\t\t\t\t\t\tcontextIds[ContextIdKeys.Tenant] = tenantId;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Wrap the user lookup in the request context so partitioned storage uses the correct tenant.\n\t\t\t\tconst user = await ContextIdStore.run(contextIds, async () =>\n\t\t\t\t\tthis._userEntityStorage.get(userIdentity, \"identity\")\n\t\t\t\t);\n\n\t\t\t\trefreshPasswordVersion = user?.passwordVersion;\n\t\t\t\tif (\n\t\t\t\t\tuser?.identity === userIdentity &&\n\t\t\t\t\t(passwordVersion ?? 0) === (refreshPasswordVersion ?? 0)\n\t\t\t\t) {\n\t\t\t\t\tvalidParts.push(\"user\");\n\t\t\t\t}\n\t\t\t\tif (user?.organization === organizationIdentity) {\n\t\t\t\t\tvalidParts.push(\"organization\");\n\t\t\t\t}\n\t\t\t\treturn validParts;\n\t\t\t}\n\t\t);\n\n\t\tconst refreshSub = headerAndPayload.payload.sub ?? \"\";\n\t\tawait this._authenticationRateService.check(\"token-refresh\", refreshSub);\n\n\t\tconst refreshTokenAndExpiry = await TokenHelper.createToken(\n\t\t\tthis._vaultConnector,\n\t\t\tthis._urlTransformerService,\n\t\t\t`${this._nodeId}/${this._signingKeyName}`,\n\t\t\trefreshSub,\n\t\t\tIs.stringValue(headerAndPayload.payload.org) ? headerAndPayload.payload.org : \"\",\n\t\t\ttenantId,\n\t\t\tthis._defaultTtlMinutes,\n\t\t\tCoerce.string(headerAndPayload.payload?.scope),\n\t\t\trefreshPasswordVersion ?? 0\n\t\t);\n\t\tconst refreshScope = Coerce.string(headerAndPayload.payload?.scope) ?? \"\";\n\n\t\tawait this._authenticationAuditService?.create({\n\t\t\tactorId: refreshSub,\n\t\t\tevent: AuthAuditEvent.TokenRefreshed,\n\t\t\tdata: {\n\t\t\t\torganizationIdentity: Is.stringValue(headerAndPayload.payload.org)\n\t\t\t\t\t? headerAndPayload.payload.org\n\t\t\t\t\t: \"\",\n\t\t\t\ttenantId,\n\t\t\t\tscope: refreshScope.split(\",\").filter(scope => scope.length > 0)\n\t\t\t}\n\t\t});\n\n\t\treturn refreshTokenAndExpiry;\n\t}\n\n\t/**\n\t * Update the user's password.\n\t * @param currentPassword The current password for the user.\n\t * @param newPassword The new password for the user.\n\t * @returns Nothing.\n\t */\n\tpublic async updatePassword(currentPassword: string, newPassword: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.User);\n\n\t\tconst userIdentity = contextIds[ContextIdKeys.User];\n\t\tawait this._authenticationRateService.check(\"password-change\", userIdentity);\n\n\t\tconst user = await this._userEntityStorage.get(userIdentity, \"identity\");\n\t\tif (!Is.object<AuthenticationUser>(user)) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tEntityStorageAuthenticationService.CLASS_NAME,\n\t\t\t\t\"userNotFound\",\n\t\t\t\tuserIdentity\n\t\t\t);\n\t\t}\n\n\t\tawait PasswordHelper.updatePassword(\n\t\t\tthis._userEntityStorage,\n\t\t\tthis._authenticationAuditService,\n\t\t\tuser,\n\t\t\tnewPassword,\n\t\t\tcurrentPassword,\n\t\t\tthis._minPasswordLength\n\t\t);\n\n\t\tawait this._authenticationRateService.clear(\"password-change\", userIdentity);\n\t}\n}\n"]}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
-
import { Coerce, Converter, Is, UnauthorizedError } from "@twin.org/core";
|
|
4
|
-
import { Blake2b } from "@twin.org/crypto";
|
|
1
|
+
import { Coerce, Is, UnauthorizedError } from "@twin.org/core";
|
|
5
2
|
import { VaultConnectorHelper } from "@twin.org/vault-models";
|
|
6
3
|
import { CookieHelper, HeaderHelper, HeaderTypes, Jwt } from "@twin.org/web";
|
|
7
4
|
/**
|
|
@@ -15,6 +12,7 @@ export class TokenHelper {
|
|
|
15
12
|
/**
|
|
16
13
|
* Create a new token.
|
|
17
14
|
* @param vaultConnector The vault connector.
|
|
15
|
+
* @param urlTransformerComponent The URL transformer component, used to encrypt the tenant ID for inclusion in the token.
|
|
18
16
|
* @param signingKeyName The signing key name.
|
|
19
17
|
* @param userIdentity The subject for the token.
|
|
20
18
|
* @param organizationIdentity The organization for the token.
|
|
@@ -24,13 +22,15 @@ export class TokenHelper {
|
|
|
24
22
|
* @param passwordVersion The user's current password version counter, embedded in the token so that a password change invalidates existing tokens.
|
|
25
23
|
* @returns The new token and its expiry date.
|
|
26
24
|
*/
|
|
27
|
-
static async createToken(vaultConnector, signingKeyName, userIdentity, organizationIdentity, tenantId, ttlMinutes, scope, passwordVersion) {
|
|
25
|
+
static async createToken(vaultConnector, urlTransformerComponent, signingKeyName, userIdentity, organizationIdentity, tenantId, ttlMinutes, scope, passwordVersion) {
|
|
28
26
|
const nowSeconds = Math.trunc(Date.now() / 1000);
|
|
29
27
|
const ttlSeconds = ttlMinutes * 60;
|
|
30
28
|
const jwt = await Jwt.encodeWithSigner({ alg: "EdDSA" }, {
|
|
31
29
|
sub: userIdentity,
|
|
32
30
|
org: organizationIdentity,
|
|
33
|
-
tid:
|
|
31
|
+
tid: Is.stringValue(tenantId)
|
|
32
|
+
? await urlTransformerComponent.encryptParam(tenantId)
|
|
33
|
+
: undefined,
|
|
34
34
|
exp: nowSeconds + ttlSeconds,
|
|
35
35
|
scope,
|
|
36
36
|
pver: passwordVersion
|
|
@@ -67,14 +67,15 @@ export class TokenHelper {
|
|
|
67
67
|
throw new UnauthorizedError(TokenHelper.CLASS_NAME, "expired");
|
|
68
68
|
}
|
|
69
69
|
if (Is.function(verifyUser)) {
|
|
70
|
-
const
|
|
70
|
+
const encryptedTenantId = Coerce.string(decoded.payload.tid);
|
|
71
|
+
const userVerified = await verifyUser(decoded.payload.sub, decoded.payload.org, encryptedTenantId, Coerce.integer(decoded.payload.pver));
|
|
71
72
|
if (!userVerified.includes("user")) {
|
|
72
73
|
throw new UnauthorizedError(TokenHelper.CLASS_NAME, "userNotVerified");
|
|
73
74
|
}
|
|
74
75
|
else if (!userVerified.includes("organization")) {
|
|
75
76
|
throw new UnauthorizedError(TokenHelper.CLASS_NAME, "organizationNotVerified");
|
|
76
77
|
}
|
|
77
|
-
else if (!userVerified.includes("tenant")) {
|
|
78
|
+
else if (Is.stringValue(encryptedTenantId) && !userVerified.includes("tenant")) {
|
|
78
79
|
throw new UnauthorizedError(TokenHelper.CLASS_NAME, "tenantNotVerified");
|
|
79
80
|
}
|
|
80
81
|
}
|
|
@@ -119,17 +120,5 @@ export class TokenHelper {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Hash the tenant ID using Blake2b and encode it in Base64URL format.
|
|
124
|
-
* Used to create a consistent and secure representation of tenant IDs without exposing the original values.
|
|
125
|
-
* @param tenantId The tenant ID to hash.
|
|
126
|
-
* @returns The hashed tenant ID in Base64URL format, or undefined if the input tenant ID is not a valid string.
|
|
127
|
-
*/
|
|
128
|
-
static hashTenantId(tenantId) {
|
|
129
|
-
if (!Is.stringValue(tenantId)) {
|
|
130
|
-
return undefined;
|
|
131
|
-
}
|
|
132
|
-
return Converter.bytesToBase64Url(Blake2b.sum256(Converter.utf8ToBytes(tenantId)));
|
|
133
|
-
}
|
|
134
123
|
}
|
|
135
124
|
//# sourceMappingURL=tokenHelper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenHelper.js","sourceRoot":"","sources":["../../../src/utils/tokenHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Coerce, Converter, Is, UnauthorizedError } from \"@twin.org/core\";\nimport { Blake2b } from \"@twin.org/crypto\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type IVaultConnector, VaultConnectorHelper } from \"@twin.org/vault-models\";\nimport {\n\tCookieHelper,\n\tHeaderHelper,\n\tHeaderTypes,\n\ttype IHttpHeaders,\n\ttype IJwtHeader,\n\ttype IJwtPayload,\n\tJwt\n} from \"@twin.org/web\";\n\n/**\n * Helper class for token operations.\n */\nexport class TokenHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TokenHelper>();\n\n\t/**\n\t * Create a new token.\n\t * @param vaultConnector The vault connector.\n\t * @param signingKeyName The signing key name.\n\t * @param userIdentity The subject for the token.\n\t * @param organizationIdentity The organization for the token.\n\t * @param tenantId The tenant id for the token.\n\t * @param ttlMinutes The time to live for the token in minutes.\n\t * @param scope The scopes for the token.\n\t * @param passwordVersion The user's current password version counter, embedded in the token so that a password change invalidates existing tokens.\n\t * @returns The new token and its expiry date.\n\t */\n\tpublic static async createToken(\n\t\tvaultConnector: IVaultConnector,\n\t\tsigningKeyName: string,\n\t\tuserIdentity: string,\n\t\torganizationIdentity: string | undefined,\n\t\ttenantId: string | undefined,\n\t\tttlMinutes: number,\n\t\tscope?: string,\n\t\tpasswordVersion?: number\n\t): Promise<{\n\t\ttoken: string;\n\t\texpiry: number;\n\t}> {\n\t\tconst nowSeconds = Math.trunc(Date.now() / 1000);\n\t\tconst ttlSeconds = ttlMinutes * 60;\n\n\t\tconst jwt = await Jwt.encodeWithSigner(\n\t\t\t{ alg: \"EdDSA\" },\n\t\t\t{\n\t\t\t\tsub: userIdentity,\n\t\t\t\torg: organizationIdentity,\n\t\t\t\ttid: TokenHelper.hashTenantId(tenantId),\n\t\t\t\texp: nowSeconds + ttlSeconds,\n\t\t\t\tscope,\n\t\t\t\tpver: passwordVersion\n\t\t\t},\n\t\t\tasync (header, payload) =>\n\t\t\t\tVaultConnectorHelper.jwtSigner(vaultConnector, signingKeyName, header, payload)\n\t\t);\n\n\t\treturn {\n\t\t\ttoken: jwt,\n\t\t\texpiry: (nowSeconds + ttlSeconds) * 1000\n\t\t};\n\t}\n\n\t/**\n\t * Verify the token.\n\t * @param vaultConnector The vault connector.\n\t * @param signingKeyName The signing key name.\n\t * @param token The token to verify.\n\t * @param requiredScopes The required scopes.\n\t * @param verifyUser A function to verify the user identity and organization. The password version counter embedded in the token (pver claim) is passed so callers can detect if the password has changed since the token was issued.\n\t * @returns The verified details.\n\t * @throws UnauthorizedError if the token is missing, invalid or expired.\n\t */\n\tpublic static async verify(\n\t\tvaultConnector: IVaultConnector,\n\t\tsigningKeyName: string,\n\t\ttoken: string | undefined,\n\t\trequiredScopes?: string[],\n\t\tverifyUser?: (\n\t\t\tuserIdentity: string,\n\t\t\torganizationIdentity: string,\n\t\t\thashedTenantId: string | undefined,\n\t\t\tpasswordVersion: number | undefined\n\t\t) => Promise<string[]>\n\t): Promise<{\n\t\theader: IJwtHeader;\n\t\tpayload: IJwtPayload;\n\t}> {\n\t\tif (!Is.stringValue(token)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"missing\");\n\t\t}\n\n\t\tconst decoded = await Jwt.verifyWithVerifier(token, async t =>\n\t\t\tVaultConnectorHelper.jwtVerifier(vaultConnector, signingKeyName, t)\n\t\t);\n\n\t\t// If some of the header/payload data is not properly populated then it is unauthorized.\n\t\tif (!Is.stringValue(decoded.payload.sub)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"payloadMissingSubject\");\n\t\t} else if (!Is.stringValue(decoded.payload.org)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"payloadMissingOrganization\");\n\t\t} else if (\n\t\t\t!Is.empty(decoded.payload?.exp) &&\n\t\t\tdecoded.payload.exp < Math.trunc(Date.now() / 1000)\n\t\t) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"expired\");\n\t\t}\n\n\t\tif (Is.function(verifyUser)) {\n\t\t\tconst userVerified = await verifyUser(\n\t\t\t\tdecoded.payload.sub,\n\t\t\t\tdecoded.payload.org,\n\t\t\t\tCoerce.string(decoded.payload.tid),\n\t\t\t\tCoerce.integer(decoded.payload.pver)\n\t\t\t);\n\t\t\tif (!userVerified.includes(\"user\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"userNotVerified\");\n\t\t\t} else if (!userVerified.includes(\"organization\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"organizationNotVerified\");\n\t\t\t} else if (!userVerified.includes(\"tenant\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"tenantNotVerified\");\n\t\t\t}\n\t\t}\n\n\t\tif (Is.arrayValue(requiredScopes)) {\n\t\t\tconst tokenScopes = Is.stringValue(decoded.payload.scope)\n\t\t\t\t? decoded.payload.scope.split(\",\")\n\t\t\t\t: [];\n\n\t\t\tfor (const requiredScope of requiredScopes) {\n\t\t\t\tif (!tokenScopes.includes(requiredScope)) {\n\t\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"insufficientScopes\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\theader: decoded.header,\n\t\t\tpayload: decoded.payload\n\t\t};\n\t}\n\n\t/**\n\t * Extract the auth token from the headers, either from the authorization header or the cookie header.\n\t * @param headers The headers to extract the token from.\n\t * @param cookieName The name of the cookie to extract the token from.\n\t * @returns The token if found.\n\t */\n\tpublic static extractTokenFromHeaders(\n\t\theaders?: IHttpHeaders,\n\t\tcookieName?: string\n\t):\n\t\t| {\n\t\t\t\ttoken: string;\n\t\t\t\tlocation: \"authorization\" | \"cookie\";\n\t\t }\n\t\t| undefined {\n\t\tconst authHeader = headers?.[HeaderTypes.Authorization];\n\t\tconst cookiesHeader = headers?.[HeaderTypes.Cookie];\n\n\t\tconst bearerToken = HeaderHelper.extractBearer(authHeader);\n\t\tif (Is.stringValue(bearerToken)) {\n\t\t\treturn {\n\t\t\t\ttoken: bearerToken,\n\t\t\t\tlocation: \"authorization\"\n\t\t\t};\n\t\t} else if (Is.notEmpty(cookiesHeader) && Is.stringValue(cookieName)) {\n\t\t\tconst value = CookieHelper.getCookieFromHeaders(cookiesHeader, cookieName);\n\t\t\tif (Is.stringValue(value)) {\n\t\t\t\treturn {\n\t\t\t\t\ttoken: value,\n\t\t\t\t\tlocation: \"cookie\"\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Hash the tenant ID using Blake2b and encode it in Base64URL format.\n\t * Used to create a consistent and secure representation of tenant IDs without exposing the original values.\n\t * @param tenantId The tenant ID to hash.\n\t * @returns The hashed tenant ID in Base64URL format, or undefined if the input tenant ID is not a valid string.\n\t */\n\tpublic static hashTenantId(tenantId: string | undefined): string | undefined {\n\t\tif (!Is.stringValue(tenantId)) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn Converter.bytesToBase64Url(Blake2b.sum256(Converter.utf8ToBytes(tenantId)));\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tokenHelper.js","sourceRoot":"","sources":["../../../src/utils/tokenHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IUrlTransformerComponent } from \"@twin.org/api-models\";\nimport { Coerce, Is, UnauthorizedError } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { type IVaultConnector, VaultConnectorHelper } from \"@twin.org/vault-models\";\nimport {\n\tCookieHelper,\n\tHeaderHelper,\n\tHeaderTypes,\n\ttype IHttpHeaders,\n\ttype IJwtHeader,\n\ttype IJwtPayload,\n\tJwt\n} from \"@twin.org/web\";\n\n/**\n * Helper class for token operations.\n */\nexport class TokenHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TokenHelper>();\n\n\t/**\n\t * Create a new token.\n\t * @param vaultConnector The vault connector.\n\t * @param urlTransformerComponent The URL transformer component, used to encrypt the tenant ID for inclusion in the token.\n\t * @param signingKeyName The signing key name.\n\t * @param userIdentity The subject for the token.\n\t * @param organizationIdentity The organization for the token.\n\t * @param tenantId The tenant id for the token.\n\t * @param ttlMinutes The time to live for the token in minutes.\n\t * @param scope The scopes for the token.\n\t * @param passwordVersion The user's current password version counter, embedded in the token so that a password change invalidates existing tokens.\n\t * @returns The new token and its expiry date.\n\t */\n\tpublic static async createToken(\n\t\tvaultConnector: IVaultConnector,\n\t\turlTransformerComponent: IUrlTransformerComponent,\n\t\tsigningKeyName: string,\n\t\tuserIdentity: string,\n\t\torganizationIdentity: string | undefined,\n\t\ttenantId: string | undefined,\n\t\tttlMinutes: number,\n\t\tscope?: string,\n\t\tpasswordVersion?: number\n\t): Promise<{\n\t\ttoken: string;\n\t\texpiry: number;\n\t}> {\n\t\tconst nowSeconds = Math.trunc(Date.now() / 1000);\n\t\tconst ttlSeconds = ttlMinutes * 60;\n\n\t\tconst jwt = await Jwt.encodeWithSigner(\n\t\t\t{ alg: \"EdDSA\" },\n\t\t\t{\n\t\t\t\tsub: userIdentity,\n\t\t\t\torg: organizationIdentity,\n\t\t\t\ttid: Is.stringValue(tenantId)\n\t\t\t\t\t? await urlTransformerComponent.encryptParam(tenantId)\n\t\t\t\t\t: undefined,\n\t\t\t\texp: nowSeconds + ttlSeconds,\n\t\t\t\tscope,\n\t\t\t\tpver: passwordVersion\n\t\t\t},\n\t\t\tasync (header, payload) =>\n\t\t\t\tVaultConnectorHelper.jwtSigner(vaultConnector, signingKeyName, header, payload)\n\t\t);\n\n\t\treturn {\n\t\t\ttoken: jwt,\n\t\t\texpiry: (nowSeconds + ttlSeconds) * 1000\n\t\t};\n\t}\n\n\t/**\n\t * Verify the token.\n\t * @param vaultConnector The vault connector.\n\t * @param signingKeyName The signing key name.\n\t * @param token The token to verify.\n\t * @param requiredScopes The required scopes.\n\t * @param verifyUser A function to verify the user identity and organization. The password version counter embedded in the token (pver claim) is passed so callers can detect if the password has changed since the token was issued.\n\t * @returns The verified details.\n\t * @throws UnauthorizedError if the token is missing, invalid or expired.\n\t */\n\tpublic static async verify(\n\t\tvaultConnector: IVaultConnector,\n\t\tsigningKeyName: string,\n\t\ttoken: string | undefined,\n\t\trequiredScopes?: string[],\n\t\tverifyUser?: (\n\t\t\tuserIdentity: string,\n\t\t\torganizationIdentity: string,\n\t\t\tencryptedTenantId: string | undefined,\n\t\t\tpasswordVersion: number | undefined\n\t\t) => Promise<string[]>\n\t): Promise<{\n\t\theader: IJwtHeader;\n\t\tpayload: IJwtPayload;\n\t}> {\n\t\tif (!Is.stringValue(token)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"missing\");\n\t\t}\n\n\t\tconst decoded = await Jwt.verifyWithVerifier(token, async t =>\n\t\t\tVaultConnectorHelper.jwtVerifier(vaultConnector, signingKeyName, t)\n\t\t);\n\n\t\t// If some of the header/payload data is not properly populated then it is unauthorized.\n\t\tif (!Is.stringValue(decoded.payload.sub)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"payloadMissingSubject\");\n\t\t} else if (!Is.stringValue(decoded.payload.org)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"payloadMissingOrganization\");\n\t\t} else if (\n\t\t\t!Is.empty(decoded.payload?.exp) &&\n\t\t\tdecoded.payload.exp < Math.trunc(Date.now() / 1000)\n\t\t) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"expired\");\n\t\t}\n\n\t\tif (Is.function(verifyUser)) {\n\t\t\tconst encryptedTenantId = Coerce.string(decoded.payload.tid);\n\t\t\tconst userVerified = await verifyUser(\n\t\t\t\tdecoded.payload.sub,\n\t\t\t\tdecoded.payload.org,\n\t\t\t\tencryptedTenantId,\n\t\t\t\tCoerce.integer(decoded.payload.pver)\n\t\t\t);\n\t\t\tif (!userVerified.includes(\"user\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"userNotVerified\");\n\t\t\t} else if (!userVerified.includes(\"organization\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"organizationNotVerified\");\n\t\t\t} else if (Is.stringValue(encryptedTenantId) && !userVerified.includes(\"tenant\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"tenantNotVerified\");\n\t\t\t}\n\t\t}\n\n\t\tif (Is.arrayValue(requiredScopes)) {\n\t\t\tconst tokenScopes = Is.stringValue(decoded.payload.scope)\n\t\t\t\t? decoded.payload.scope.split(\",\")\n\t\t\t\t: [];\n\n\t\t\tfor (const requiredScope of requiredScopes) {\n\t\t\t\tif (!tokenScopes.includes(requiredScope)) {\n\t\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"insufficientScopes\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\theader: decoded.header,\n\t\t\tpayload: decoded.payload\n\t\t};\n\t}\n\n\t/**\n\t * Extract the auth token from the headers, either from the authorization header or the cookie header.\n\t * @param headers The headers to extract the token from.\n\t * @param cookieName The name of the cookie to extract the token from.\n\t * @returns The token if found.\n\t */\n\tpublic static extractTokenFromHeaders(\n\t\theaders?: IHttpHeaders,\n\t\tcookieName?: string\n\t):\n\t\t| {\n\t\t\t\ttoken: string;\n\t\t\t\tlocation: \"authorization\" | \"cookie\";\n\t\t }\n\t\t| undefined {\n\t\tconst authHeader = headers?.[HeaderTypes.Authorization];\n\t\tconst cookiesHeader = headers?.[HeaderTypes.Cookie];\n\n\t\tconst bearerToken = HeaderHelper.extractBearer(authHeader);\n\t\tif (Is.stringValue(bearerToken)) {\n\t\t\treturn {\n\t\t\t\ttoken: bearerToken,\n\t\t\t\tlocation: \"authorization\"\n\t\t\t};\n\t\t} else if (Is.notEmpty(cookiesHeader) && Is.stringValue(cookieName)) {\n\t\t\tconst value = CookieHelper.getCookieFromHeaders(cookiesHeader, cookieName);\n\t\t\tif (Is.stringValue(value)) {\n\t\t\t\treturn {\n\t\t\t\t\ttoken: value,\n\t\t\t\t\tlocation: \"cookie\"\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -13,6 +13,15 @@ export interface IAuthHeaderProcessorConstructorOptions {
|
|
|
13
13
|
* @default vault
|
|
14
14
|
*/
|
|
15
15
|
vaultConnectorType?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The URL transformer component for the tenants.
|
|
18
|
+
*/
|
|
19
|
+
urlTransformerComponentType?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The component to retrieve tenant information.
|
|
22
|
+
* @default tenant-admin
|
|
23
|
+
*/
|
|
24
|
+
tenantAdminComponentType?: string;
|
|
16
25
|
/**
|
|
17
26
|
* The configuration for the processor.
|
|
18
27
|
*/
|
|
@@ -13,6 +13,10 @@ export interface IEntityStorageAuthenticationServiceConstructorOptions {
|
|
|
13
13
|
* @default vault
|
|
14
14
|
*/
|
|
15
15
|
vaultConnectorType?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The URL transformer component for the tenants.
|
|
18
|
+
*/
|
|
19
|
+
urlTransformerComponentType?: string;
|
|
16
20
|
/**
|
|
17
21
|
* The audit service.
|
|
18
22
|
* @default authentication-audit
|
|
@@ -23,6 +27,11 @@ export interface IEntityStorageAuthenticationServiceConstructorOptions {
|
|
|
23
27
|
* @default authentication-rate
|
|
24
28
|
*/
|
|
25
29
|
authenticationRateServiceType?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The component to retrieve tenant information.
|
|
32
|
+
* @default tenant-admin
|
|
33
|
+
*/
|
|
34
|
+
tenantAdminComponentType?: string;
|
|
26
35
|
/**
|
|
27
36
|
* The configuration for the authentication.
|
|
28
37
|
*/
|
|
@@ -10,7 +10,7 @@ export declare class AuthHeaderProcessor implements IBaseRouteProcessor {
|
|
|
10
10
|
*/
|
|
11
11
|
static readonly CLASS_NAME: string;
|
|
12
12
|
/**
|
|
13
|
-
* Create a new instance of
|
|
13
|
+
* Create a new instance of AuthHeaderProcessor.
|
|
14
14
|
* @param options Options for the processor.
|
|
15
15
|
*/
|
|
16
16
|
constructor(options?: IAuthHeaderProcessorConstructorOptions);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IUrlTransformerComponent } from "@twin.org/api-models";
|
|
1
2
|
import { type IVaultConnector } from "@twin.org/vault-models";
|
|
2
3
|
import { type IHttpHeaders, type IJwtHeader, type IJwtPayload } from "@twin.org/web";
|
|
3
4
|
/**
|
|
@@ -11,6 +12,7 @@ export declare class TokenHelper {
|
|
|
11
12
|
/**
|
|
12
13
|
* Create a new token.
|
|
13
14
|
* @param vaultConnector The vault connector.
|
|
15
|
+
* @param urlTransformerComponent The URL transformer component, used to encrypt the tenant ID for inclusion in the token.
|
|
14
16
|
* @param signingKeyName The signing key name.
|
|
15
17
|
* @param userIdentity The subject for the token.
|
|
16
18
|
* @param organizationIdentity The organization for the token.
|
|
@@ -20,7 +22,7 @@ export declare class TokenHelper {
|
|
|
20
22
|
* @param passwordVersion The user's current password version counter, embedded in the token so that a password change invalidates existing tokens.
|
|
21
23
|
* @returns The new token and its expiry date.
|
|
22
24
|
*/
|
|
23
|
-
static createToken(vaultConnector: IVaultConnector, signingKeyName: string, userIdentity: string, organizationIdentity: string | undefined, tenantId: string | undefined, ttlMinutes: number, scope?: string, passwordVersion?: number): Promise<{
|
|
25
|
+
static createToken(vaultConnector: IVaultConnector, urlTransformerComponent: IUrlTransformerComponent, signingKeyName: string, userIdentity: string, organizationIdentity: string | undefined, tenantId: string | undefined, ttlMinutes: number, scope?: string, passwordVersion?: number): Promise<{
|
|
24
26
|
token: string;
|
|
25
27
|
expiry: number;
|
|
26
28
|
}>;
|
|
@@ -34,7 +36,7 @@ export declare class TokenHelper {
|
|
|
34
36
|
* @returns The verified details.
|
|
35
37
|
* @throws UnauthorizedError if the token is missing, invalid or expired.
|
|
36
38
|
*/
|
|
37
|
-
static verify(vaultConnector: IVaultConnector, signingKeyName: string, token: string | undefined, requiredScopes?: string[], verifyUser?: (userIdentity: string, organizationIdentity: string,
|
|
39
|
+
static verify(vaultConnector: IVaultConnector, signingKeyName: string, token: string | undefined, requiredScopes?: string[], verifyUser?: (userIdentity: string, organizationIdentity: string, encryptedTenantId: string | undefined, passwordVersion: number | undefined) => Promise<string[]>): Promise<{
|
|
38
40
|
header: IJwtHeader;
|
|
39
41
|
payload: IJwtPayload;
|
|
40
42
|
}>;
|
|
@@ -48,11 +50,4 @@ export declare class TokenHelper {
|
|
|
48
50
|
token: string;
|
|
49
51
|
location: "authorization" | "cookie";
|
|
50
52
|
} | undefined;
|
|
51
|
-
/**
|
|
52
|
-
* Hash the tenant ID using Blake2b and encode it in Base64URL format.
|
|
53
|
-
* Used to create a consistent and secure representation of tenant IDs without exposing the original values.
|
|
54
|
-
* @param tenantId The tenant ID to hash.
|
|
55
|
-
* @returns The hashed tenant ID in Base64URL format, or undefined if the input tenant ID is not a valid string.
|
|
56
|
-
*/
|
|
57
|
-
static hashTenantId(tenantId: string | undefined): string | undefined;
|
|
58
53
|
}
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.3-next.39](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.38...api-auth-entity-storage-service-v0.0.3-next.39) (2026-06-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* tenant id in jwt ([#140](https://github.com/iotaledger/twin-api/issues/140)) ([8d37a7b](https://github.com/iotaledger/twin-api/commit/8d37a7b98b45fde53df1e909cffc0869aa758655))
|
|
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.38 to 0.0.3-next.39
|
|
16
|
+
* @twin.org/api-core bumped from 0.0.3-next.38 to 0.0.3-next.39
|
|
17
|
+
* @twin.org/api-models bumped from 0.0.3-next.38 to 0.0.3-next.39
|
|
18
|
+
|
|
3
19
|
## [0.0.3-next.38](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.37...api-auth-entity-storage-service-v0.0.3-next.38) (2026-05-28)
|
|
4
20
|
|
|
5
21
|
|
|
@@ -12,7 +12,7 @@ Handle a JWT token in the authorization header or cookies and validate it to pop
|
|
|
12
12
|
|
|
13
13
|
> **new AuthHeaderProcessor**(`options?`): `AuthHeaderProcessor`
|
|
14
14
|
|
|
15
|
-
Create a new instance of
|
|
15
|
+
Create a new instance of AuthHeaderProcessor.
|
|
16
16
|
|
|
17
17
|
#### Parameters
|
|
18
18
|
|
|
@@ -24,7 +24,7 @@ Runtime name for the class.
|
|
|
24
24
|
|
|
25
25
|
### createToken() {#createtoken}
|
|
26
26
|
|
|
27
|
-
> `static` **createToken**(`vaultConnector`, `signingKeyName`, `userIdentity`, `organizationIdentity`, `tenantId`, `ttlMinutes`, `scope?`, `passwordVersion?`): `Promise`\<\{ `token`: `string`; `expiry`: `number`; \}\>
|
|
27
|
+
> `static` **createToken**(`vaultConnector`, `urlTransformerComponent`, `signingKeyName`, `userIdentity`, `organizationIdentity`, `tenantId`, `ttlMinutes`, `scope?`, `passwordVersion?`): `Promise`\<\{ `token`: `string`; `expiry`: `number`; \}\>
|
|
28
28
|
|
|
29
29
|
Create a new token.
|
|
30
30
|
|
|
@@ -36,6 +36,12 @@ Create a new token.
|
|
|
36
36
|
|
|
37
37
|
The vault connector.
|
|
38
38
|
|
|
39
|
+
##### urlTransformerComponent
|
|
40
|
+
|
|
41
|
+
`IUrlTransformerComponent`
|
|
42
|
+
|
|
43
|
+
The URL transformer component, used to encrypt the tenant ID for inclusion in the token.
|
|
44
|
+
|
|
39
45
|
##### signingKeyName
|
|
40
46
|
|
|
41
47
|
`string`
|
|
@@ -120,7 +126,7 @@ The required scopes.
|
|
|
120
126
|
|
|
121
127
|
##### verifyUser?
|
|
122
128
|
|
|
123
|
-
(`userIdentity`, `organizationIdentity`, `
|
|
129
|
+
(`userIdentity`, `organizationIdentity`, `encryptedTenantId`, `passwordVersion`) => `Promise`\<`string`[]\>
|
|
124
130
|
|
|
125
131
|
A function to verify the user identity and organization. The password version counter embedded in the token (pver claim) is passed so callers can detect if the password has changed since the token was issued.
|
|
126
132
|
|
|
@@ -161,26 +167,3 @@ The name of the cookie to extract the token from.
|
|
|
161
167
|
\{ `token`: `string`; `location`: `"authorization"` \| `"cookie"`; \} \| `undefined`
|
|
162
168
|
|
|
163
169
|
The token if found.
|
|
164
|
-
|
|
165
|
-
***
|
|
166
|
-
|
|
167
|
-
### hashTenantId() {#hashtenantid}
|
|
168
|
-
|
|
169
|
-
> `static` **hashTenantId**(`tenantId`): `string` \| `undefined`
|
|
170
|
-
|
|
171
|
-
Hash the tenant ID using Blake2b and encode it in Base64URL format.
|
|
172
|
-
Used to create a consistent and secure representation of tenant IDs without exposing the original values.
|
|
173
|
-
|
|
174
|
-
#### Parameters
|
|
175
|
-
|
|
176
|
-
##### tenantId
|
|
177
|
-
|
|
178
|
-
`string` \| `undefined`
|
|
179
|
-
|
|
180
|
-
The tenant ID to hash.
|
|
181
|
-
|
|
182
|
-
#### Returns
|
|
183
|
-
|
|
184
|
-
`string` \| `undefined`
|
|
185
|
-
|
|
186
|
-
The hashed tenant ID in Base64URL format, or undefined if the input tenant ID is not a valid string.
|
|
@@ -32,6 +32,28 @@ vault
|
|
|
32
32
|
|
|
33
33
|
***
|
|
34
34
|
|
|
35
|
+
### urlTransformerComponentType? {#urltransformercomponenttype}
|
|
36
|
+
|
|
37
|
+
> `optional` **urlTransformerComponentType?**: `string`
|
|
38
|
+
|
|
39
|
+
The URL transformer component for the tenants.
|
|
40
|
+
|
|
41
|
+
***
|
|
42
|
+
|
|
43
|
+
### tenantAdminComponentType? {#tenantadmincomponenttype}
|
|
44
|
+
|
|
45
|
+
> `optional` **tenantAdminComponentType?**: `string`
|
|
46
|
+
|
|
47
|
+
The component to retrieve tenant information.
|
|
48
|
+
|
|
49
|
+
#### Default
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
tenant-admin
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
***
|
|
56
|
+
|
|
35
57
|
### config? {#config}
|
|
36
58
|
|
|
37
59
|
> `optional` **config?**: [`IAuthHeaderProcessorConfig`](IAuthHeaderProcessorConfig.md)
|
|
@@ -32,6 +32,14 @@ vault
|
|
|
32
32
|
|
|
33
33
|
***
|
|
34
34
|
|
|
35
|
+
### urlTransformerComponentType? {#urltransformercomponenttype}
|
|
36
|
+
|
|
37
|
+
> `optional` **urlTransformerComponentType?**: `string`
|
|
38
|
+
|
|
39
|
+
The URL transformer component for the tenants.
|
|
40
|
+
|
|
41
|
+
***
|
|
42
|
+
|
|
35
43
|
### authenticationAuditServiceType? {#authenticationauditservicetype}
|
|
36
44
|
|
|
37
45
|
> `optional` **authenticationAuditServiceType?**: `string`
|
|
@@ -60,6 +68,20 @@ authentication-rate
|
|
|
60
68
|
|
|
61
69
|
***
|
|
62
70
|
|
|
71
|
+
### tenantAdminComponentType? {#tenantadmincomponenttype}
|
|
72
|
+
|
|
73
|
+
> `optional` **tenantAdminComponentType?**: `string`
|
|
74
|
+
|
|
75
|
+
The component to retrieve tenant information.
|
|
76
|
+
|
|
77
|
+
#### Default
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
tenant-admin
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
***
|
|
84
|
+
|
|
63
85
|
### config? {#config}
|
|
64
86
|
|
|
65
87
|
> `optional` **config?**: [`IEntityStorageAuthenticationServiceConfig`](IEntityStorageAuthenticationServiceConfig.md)
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/api-auth-entity-storage-service",
|
|
3
|
-
"version": "0.0.3-next.
|
|
3
|
+
"version": "0.0.3-next.39",
|
|
4
4
|
"description": "Authentication service implementation and REST routes backed by entity storage.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
|
-
"url": "git+https://github.com/iotaledger/api.git",
|
|
7
|
+
"url": "git+https://github.com/iotaledger/twin-api.git",
|
|
8
8
|
"directory": "packages/api-auth-entity-storage-service"
|
|
9
9
|
},
|
|
10
10
|
"author": "martyn.janes@iota.org",
|
|
@@ -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.
|
|
18
|
-
"@twin.org/api-core": "0.0.3-next.
|
|
19
|
-
"@twin.org/api-models": "0.0.3-next.
|
|
17
|
+
"@twin.org/api-auth-entity-storage-models": "0.0.3-next.39",
|
|
18
|
+
"@twin.org/api-core": "0.0.3-next.39",
|
|
19
|
+
"@twin.org/api-models": "0.0.3-next.39",
|
|
20
20
|
"@twin.org/background-task-models": "next",
|
|
21
21
|
"@twin.org/context": "next",
|
|
22
22
|
"@twin.org/core": "next",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"business-logic"
|
|
60
60
|
],
|
|
61
61
|
"bugs": {
|
|
62
|
-
"url": "git+https://github.com/iotaledger/api/issues"
|
|
62
|
+
"url": "git+https://github.com/iotaledger/twin-api/issues"
|
|
63
63
|
},
|
|
64
64
|
"homepage": "https://twindev.org"
|
|
65
65
|
}
|