@twin.org/api-tenant-processor 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/ITenantProcessorConfig.js.map +1 -1
- package/dist/es/tenantProcessor.js +30 -12
- package/dist/es/tenantProcessor.js.map +1 -1
- package/dist/types/models/ITenantProcessorConfig.d.ts +4 -0
- package/docs/changelog.md +14 -0
- package/docs/reference/interfaces/ITenantProcessorConfig.md +8 -0
- package/locales/en.json +2 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ITenantProcessorConfig.js","sourceRoot":"","sources":["../../../src/models/ITenantProcessorConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the tenant processor\n */\nexport interface ITenantProcessorConfig {\n\t/**\n\t * The key to look for in the header or query params for the api key.\n\t * @default x-api-key\n\t */\n\tapiKeyName?: string;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ITenantProcessorConfig.js","sourceRoot":"","sources":["../../../src/models/ITenantProcessorConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2025 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the tenant processor\n */\nexport interface ITenantProcessorConfig {\n\t/**\n\t * The key to look for in the header or query params for the api key.\n\t * @default x-api-key\n\t */\n\tapiKeyName?: string;\n\n\t/**\n\t * The list of endpoint paths that should be checked for an api key header, can be regexp strings. Defaults to [\"/login$\"].\n\t */\n\tapiKeyEndpoints?: string[];\n}\n"]}
|
|
@@ -18,13 +18,18 @@ export class TenantProcessor {
|
|
|
18
18
|
* Runtime name for the class.
|
|
19
19
|
*/
|
|
20
20
|
static CLASS_NAME = "TenantProcessor";
|
|
21
|
+
/**
|
|
22
|
+
* The default endpoints to match for api key processing.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
static _DEFAULT_API_KEY_ENDPOINTS = ["/login$"];
|
|
21
26
|
/**
|
|
22
27
|
* The entity storage for api keys.
|
|
23
28
|
* @internal
|
|
24
29
|
*/
|
|
25
30
|
_entityStorageConnector;
|
|
26
31
|
/**
|
|
27
|
-
* The
|
|
32
|
+
* The transformer component, used to resolve public origins for tenants and encrypt/decrypt tenant tokens.
|
|
28
33
|
* @internal
|
|
29
34
|
*/
|
|
30
35
|
_urlTransformerService;
|
|
@@ -33,6 +38,11 @@ export class TenantProcessor {
|
|
|
33
38
|
* @internal
|
|
34
39
|
*/
|
|
35
40
|
_apiKeyName;
|
|
41
|
+
/**
|
|
42
|
+
* The list of regexp patterns to match against the request URL to determine if the api key header should be processed.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
_apiKeyEndpoints;
|
|
36
46
|
/**
|
|
37
47
|
* Create a new instance of TenantProcessor.
|
|
38
48
|
* @param options Options for the processor.
|
|
@@ -41,6 +51,7 @@ export class TenantProcessor {
|
|
|
41
51
|
this._entityStorageConnector = EntityStorageConnectorFactory.get(options?.tenantEntityStorageType ?? "tenant");
|
|
42
52
|
this._urlTransformerService = ComponentFactory.get(options?.urlTransformerComponentType ?? "url-transformer");
|
|
43
53
|
this._apiKeyName = options?.config?.apiKeyName ?? TenantProcessor.DEFAULT_API_KEY_NAME;
|
|
54
|
+
this._apiKeyEndpoints = (options?.config?.apiKeyEndpoints ?? TenantProcessor._DEFAULT_API_KEY_ENDPOINTS).map((ep) => new RegExp(ep));
|
|
44
55
|
}
|
|
45
56
|
/**
|
|
46
57
|
* Returns the class name of the component.
|
|
@@ -60,27 +71,34 @@ export class TenantProcessor {
|
|
|
60
71
|
async pre(request, response, route, contextIds, processorState) {
|
|
61
72
|
if (!Is.empty(route) && !(route.skipTenant ?? false)) {
|
|
62
73
|
try {
|
|
63
|
-
const apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName];
|
|
64
74
|
let tenant;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
75
|
+
const isSkipAuth = route.skipAuth ?? false;
|
|
76
|
+
const urlPath = new URL(request.url, "http://localhost").pathname;
|
|
77
|
+
const isApiKeyEndpoint = this._apiKeyEndpoints.some(re => re.test(urlPath));
|
|
78
|
+
if (isApiKeyEndpoint) {
|
|
79
|
+
const apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName];
|
|
80
|
+
if (Is.stringValue(apiKey)) {
|
|
81
|
+
tenant = await this.resolveByApiKey(apiKey);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new UnauthorizedError(TenantProcessor.CLASS_NAME, "missingApiKey", {
|
|
85
|
+
keyName: this._apiKeyName
|
|
86
|
+
});
|
|
87
|
+
}
|
|
70
88
|
}
|
|
71
89
|
else {
|
|
72
90
|
const tenantToken = await this._urlTransformerService.getEncryptedQueryParam(request.query, "tenant");
|
|
73
91
|
if (Is.stringValue(tenantToken)) {
|
|
74
92
|
tenant = await this.resolveByTenantToken(tenantToken);
|
|
75
93
|
}
|
|
76
|
-
else {
|
|
77
|
-
throw new UnauthorizedError(TenantProcessor.CLASS_NAME, "
|
|
78
|
-
|
|
94
|
+
else if (isSkipAuth) {
|
|
95
|
+
throw new UnauthorizedError(TenantProcessor.CLASS_NAME, "missingTenantToken", {
|
|
96
|
+
paramName: this._urlTransformerService.getParamName("tenant")
|
|
79
97
|
});
|
|
80
98
|
}
|
|
81
99
|
}
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
if (!Is.empty(tenant)) {
|
|
101
|
+
contextIds[ContextIdKeys.Tenant] = tenant.id;
|
|
84
102
|
processorState.publicOrigin = tenant.publicOrigin;
|
|
85
103
|
}
|
|
86
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenantProcessor.js","sourceRoot":"","sources":["../../src/tenantProcessor.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,eAAe,EAMf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAoB,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EACN,6BAA6B,EAE7B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAI/C;;GAEG;AACH,MAAM,OAAO,eAAe;IAC3B;;;OAGG;IACI,MAAM,CAAU,oBAAoB,GAAW,WAAW,CAAC;IAElE;;OAEG;IACI,MAAM,CAAU,UAAU,qBAAqC;IAEtE;;;OAGG;IACc,uBAAuB,CAAkC;IAE1E;;;OAGG;IACc,sBAAsB,CAA2B;IAElE;;;OAGG;IACc,WAAW,CAAS;IAErC;;;OAGG;IACH,YAAY,OAA4C;QACvD,IAAI,CAAC,uBAAuB,GAAG,6BAA6B,CAAC,GAAG,CAC/D,OAAO,EAAE,uBAAuB,IAAI,QAAQ,CAC5C,CAAC;QACF,IAAI,CAAC,sBAAsB,GAAG,gBAAgB,CAAC,GAAG,CACjD,OAAO,EAAE,2BAA2B,IAAI,iBAAiB,CACzD,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,OAAO,EAAE,MAAM,EAAE,UAAU,IAAI,eAAe,CAAC,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"tenantProcessor.js","sourceRoot":"","sources":["../../src/tenantProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2025 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\ttype IUrlTransformerComponent\n} from \"@twin.org/api-models\";\nimport { ContextIdKeys, type IContextIds } from \"@twin.org/context\";\nimport { BaseError, ComponentFactory, Is, UnauthorizedError } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HttpStatusCode } from \"@twin.org/web\";\nimport type { Tenant } from \"./entities/tenant.js\";\nimport type { ITenantProcessorConstructorOptions } from \"./models/ITenantProcessorConstructorOptions.js\";\n\n/**\n * Handles incoming api keys and maps them to tenant ids.\n */\nexport class TenantProcessor implements IBaseRouteProcessor {\n\t/**\n\t * The default name for the api key header.\n\t * @internal\n\t */\n\tpublic static readonly DEFAULT_API_KEY_NAME: string = \"x-api-key\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TenantProcessor>();\n\n\t/**\n\t * The default endpoints to match for api key processing.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_API_KEY_ENDPOINTS: string[] = [\"/login$\"];\n\n\t/**\n\t * The entity storage for api keys.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnector: IEntityStorageConnector<Tenant>;\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 key in the header to look for the api key.\n\t * @internal\n\t */\n\tprivate readonly _apiKeyName: string;\n\n\t/**\n\t * The list of regexp patterns to match against the request URL to determine if the api key header should be processed.\n\t * @internal\n\t */\n\tprivate readonly _apiKeyEndpoints: RegExp[];\n\n\t/**\n\t * Create a new instance of TenantProcessor.\n\t * @param options Options for the processor.\n\t */\n\tconstructor(options?: ITenantProcessorConstructorOptions) {\n\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\toptions?.tenantEntityStorageType ?? \"tenant\"\n\t\t);\n\t\tthis._urlTransformerService = ComponentFactory.get(\n\t\t\toptions?.urlTransformerComponentType ?? \"url-transformer\"\n\t\t);\n\t\tthis._apiKeyName = options?.config?.apiKeyName ?? TenantProcessor.DEFAULT_API_KEY_NAME;\n\t\tthis._apiKeyEndpoints = (\n\t\t\toptions?.config?.apiKeyEndpoints ?? TenantProcessor._DEFAULT_API_KEY_ENDPOINTS\n\t\t).map((ep: string) => new RegExp(ep));\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 TenantProcessor.CLASS_NAME;\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.skipTenant ?? false)) {\n\t\t\ttry {\n\t\t\t\tlet tenant;\n\t\t\t\tconst isSkipAuth = route.skipAuth ?? false;\n\t\t\t\tconst urlPath = new URL(request.url, \"http://localhost\").pathname;\n\t\t\t\tconst isApiKeyEndpoint = this._apiKeyEndpoints.some(re => re.test(urlPath));\n\n\t\t\t\tif (isApiKeyEndpoint) {\n\t\t\t\t\tconst apiKey = request.headers?.[this._apiKeyName] ?? request.query?.[this._apiKeyName];\n\t\t\t\t\tif (Is.stringValue(apiKey)) {\n\t\t\t\t\t\ttenant = await this.resolveByApiKey(apiKey);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"missingApiKey\", {\n\t\t\t\t\t\t\tkeyName: this._apiKeyName\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tconst tenantToken = await this._urlTransformerService.getEncryptedQueryParam(\n\t\t\t\t\t\trequest.query,\n\t\t\t\t\t\t\"tenant\"\n\t\t\t\t\t);\n\t\t\t\t\tif (Is.stringValue(tenantToken)) {\n\t\t\t\t\t\ttenant = await this.resolveByTenantToken(tenantToken);\n\t\t\t\t\t} else if (isSkipAuth) {\n\t\t\t\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"missingTenantToken\", {\n\t\t\t\t\t\t\tparamName: this._urlTransformerService.getParamName(\"tenant\")\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!Is.empty(tenant)) {\n\t\t\t\t\tcontextIds[ContextIdKeys.Tenant] = tenant.id;\n\t\t\t\t\tprocessorState.publicOrigin = tenant.publicOrigin;\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tHttpErrorHelper.buildResponse(\n\t\t\t\t\tresponse,\n\t\t\t\t\tBaseError.fromError(err),\n\t\t\t\t\tHttpStatusCode.unauthorized\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolve the tenant context from an api key.\n\t * @param apiKey The api key sent by the caller.\n\t * @returns The tenant associated with the api key.\n\t * @internal\n\t */\n\tprivate async resolveByApiKey(apiKey: string): Promise<Tenant> {\n\t\tconst nodeTenant = await this._entityStorageConnector.get(apiKey, \"apiKey\");\n\n\t\tif (Is.empty(nodeTenant)) {\n\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"apiKeyNotFound\", {\n\t\t\t\tkey: apiKey\n\t\t\t});\n\t\t}\n\n\t\treturn nodeTenant;\n\t}\n\n\t/**\n\t * Resolve the tenant context from an encrypted tenant token query param.\n\t * @param tenantId The encrypted tenant token.\n\t * @returns The tenant associated with the tenant token.\n\t * @internal\n\t */\n\tprivate async resolveByTenantToken(tenantId: string): Promise<Tenant> {\n\t\tconst nodeTenant = await this._entityStorageConnector.get(tenantId);\n\n\t\tif (Is.empty(nodeTenant)) {\n\t\t\tthrow new UnauthorizedError(TenantProcessor.CLASS_NAME, \"tenantNotFound\", {\n\t\t\t\ttenantId\n\t\t\t});\n\t\t}\n\n\t\treturn nodeTenant;\n\t}\n}\n"]}
|
|
@@ -7,4 +7,8 @@ export interface ITenantProcessorConfig {
|
|
|
7
7
|
* @default x-api-key
|
|
8
8
|
*/
|
|
9
9
|
apiKeyName?: string;
|
|
10
|
+
/**
|
|
11
|
+
* The list of endpoint paths that should be checked for an api key header, can be regexp strings. Defaults to ["/login$"].
|
|
12
|
+
*/
|
|
13
|
+
apiKeyEndpoints?: string[];
|
|
10
14
|
}
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.3-next.39](https://github.com/iotaledger/twin-api/compare/api-tenant-processor-v0.0.3-next.38...api-tenant-processor-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-models bumped from 0.0.3-next.38 to 0.0.3-next.39
|
|
16
|
+
|
|
3
17
|
## [0.0.3-next.38](https://github.com/iotaledger/twin-api/compare/api-tenant-processor-v0.0.3-next.37...api-tenant-processor-v0.0.3-next.38) (2026-05-28)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -15,3 +15,11 @@ The key to look for in the header or query params for the api key.
|
|
|
15
15
|
```ts
|
|
16
16
|
x-api-key
|
|
17
17
|
```
|
|
18
|
+
|
|
19
|
+
***
|
|
20
|
+
|
|
21
|
+
### apiKeyEndpoints? {#apikeyendpoints}
|
|
22
|
+
|
|
23
|
+
> `optional` **apiKeyEndpoints?**: `string`[]
|
|
24
|
+
|
|
25
|
+
The list of endpoint paths that should be checked for an api key header, can be regexp strings. Defaults to ["/login$"].
|
package/locales/en.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"error": {
|
|
3
3
|
"tenantProcessor": {
|
|
4
|
-
"
|
|
4
|
+
"missingApiKey": "API key \"{keyName}\" is missing",
|
|
5
|
+
"missingTenantToken": "Tenant token query parameter \"{paramName}\" is missing",
|
|
5
6
|
"apiKeyNotFound": "No node tenant found for API key \"{key}\".",
|
|
6
7
|
"tenantNotFound": "No node tenant found for the decrypted tenant token \"{tenantId}\"."
|
|
7
8
|
},
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/api-tenant-processor",
|
|
3
|
-
"version": "0.0.3-next.
|
|
3
|
+
"version": "0.0.3-next.39",
|
|
4
4
|
"description": "Tenant resolution services and route handlers that derive tenant context from API keys.",
|
|
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-tenant-processor"
|
|
9
9
|
},
|
|
10
10
|
"author": "martyn.janes@iota.org",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"node": ">=20.0.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@twin.org/api-models": "0.0.3-next.
|
|
17
|
+
"@twin.org/api-models": "0.0.3-next.39",
|
|
18
18
|
"@twin.org/context": "next",
|
|
19
19
|
"@twin.org/core": "next",
|
|
20
20
|
"@twin.org/entity": "next",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"utilities"
|
|
52
52
|
],
|
|
53
53
|
"bugs": {
|
|
54
|
-
"url": "git+https://github.com/iotaledger/api/issues"
|
|
54
|
+
"url": "git+https://github.com/iotaledger/twin-api/issues"
|
|
55
55
|
},
|
|
56
56
|
"homepage": "https://twindev.org"
|
|
57
57
|
}
|