From 01556e762fa46bcc4b3e14fb91455367a6dfbd04 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Mon, 17 Aug 2026 11:12:34 +0800 Subject: [PATCH 01/20] feat: align with api repo versions --- packages/api-auth-entity-storage-models/package.json | 2 +- packages/api-auth-entity-storage-rest-client/package.json | 4 ++-- packages/api-auth-entity-storage-service/package.json | 4 ++-- release/release-please-manifest.prerelease.json | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/api-auth-entity-storage-models/package.json b/packages/api-auth-entity-storage-models/package.json index 5605048..abf4298 100644 --- a/packages/api-auth-entity-storage-models/package.json +++ b/packages/api-auth-entity-storage-models/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-models", - "version": "0.9.2-next.7", + "version": "0.9.2-next.9", "description": "Contracts for authentication flows and admin user management with entity storage.", "repository": { "type": "git", diff --git a/packages/api-auth-entity-storage-rest-client/package.json b/packages/api-auth-entity-storage-rest-client/package.json index 61cd0dc..8935d19 100644 --- a/packages/api-auth-entity-storage-rest-client/package.json +++ b/packages/api-auth-entity-storage-rest-client/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-rest-client", - "version": "0.9.2-next.7", + "version": "0.9.2-next.9", "description": "REST clients for authentication and admin operations against entity storage endpoints.", "repository": { "type": "git", @@ -28,7 +28,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.7", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-entity-storage-service/package.json b/packages/api-auth-entity-storage-service/package.json index 1020d3b..2acaad6 100644 --- a/packages/api-auth-entity-storage-service/package.json +++ b/packages/api-auth-entity-storage-service/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-service", - "version": "0.9.2-next.7", + "version": "0.9.2-next.9", "description": "Authentication service implementation and REST routes backed by entity storage.", "repository": { "type": "git", @@ -29,7 +29,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.7", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", diff --git a/release/release-please-manifest.prerelease.json b/release/release-please-manifest.prerelease.json index e37f824..f2186ce 100644 --- a/release/release-please-manifest.prerelease.json +++ b/release/release-please-manifest.prerelease.json @@ -1,5 +1,5 @@ { - "packages/api-auth-entity-storage-models": "0.9.2-next.7", - "packages/api-auth-entity-storage-service": "0.9.2-next.7", - "packages/api-auth-entity-storage-rest-client": "0.9.2-next.7" + "packages/api-auth-entity-storage-models": "0.9.2-next.9", + "packages/api-auth-entity-storage-service": "0.9.2-next.9", + "packages/api-auth-entity-storage-rest-client": "0.9.2-next.9" } From f20a690dcd58499d239231a0aa2a8f9f349075b5 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Mon, 17 Aug 2026 14:03:26 +0800 Subject: [PATCH 02/20] feat: package refactoring --- eslint.config.js | 4 + package-lock.json | 260 ++- package.json | 3 + .../docs/reference/index.md | 19 - .../interfaces/IAuthenticationUser.md | 6 +- .../interfaces/IAuthenticationUserSecure.md | 8 +- .../src/index.ts | 12 - .../src/models/IAuthenticationUser.ts | 4 +- .../docs/reference/index.md | 1 - ...henticationRestClientConstructorOptions.md | 12 + .../package.json | 1 + .../src/index.ts | 1 - ...orageAuthenticationAdminRestClient.spec.ts | 2 +- .../docs/open-api/spec.json | 965 +--------- .../reference/classes/AuthenticationUser.md | 6 +- .../docs/reference/classes/TokenHelper.md | 16 +- .../docs/reference/index.md | 15 - .../locales/en.json | 13 - .../package.json | 1 + .../src/entities/authenticationUser.ts | 4 +- .../src/index.ts | 9 - ...ntityStorageAuthenticationServiceConfig.ts | 2 +- .../src/processors/authHeaderProcessor.ts | 1 + .../src/restEntryPoints.ts | 10 - .../entityStorageAuthenticationAdminRoutes.ts | 35 +- .../entityStorageAuthenticationRoutes.ts | 21 +- .../src/schema.ts | 8 - ...entityStorageAuthenticationAdminService.ts | 39 +- .../entityStorageAuthenticationService.ts | 28 +- .../src/utils/passwordHelper.ts | 6 +- .../src/utils/tokenHelper.ts | 3 - .../tests/helpers/tokenHelper.spec.ts | 83 - .../processors/authHeaderProcessor.spec.ts | 16 +- ...yStorageAuthenticationAdminService.spec.ts | 90 +- ...entityStorageAuthenticationService.spec.ts | 74 +- packages/api-auth-models/LICENSE | 201 ++ packages/api-auth-models/README.md | 21 + packages/api-auth-models/docs/changelog.md | 1 + packages/api-auth-models/docs/examples.md | 1 + .../api-auth-models/docs/reference/index.md | 23 + .../interfaces/IAuditCreateRequest.md | 0 .../reference/interfaces/IAuditGetRequest.md | 0 .../reference/interfaces/IAuditGetResponse.md | 0 .../interfaces/IAuditQueryRequest.md | 2 +- .../interfaces/IAuditQueryResponse.md | 0 .../interfaces/IAuditRemoveRequest.md | 0 .../interfaces/IAuditUpdateRequest.md | 0 .../IAuthenticationAuditComponent.md | 2 +- .../interfaces/IAuthenticationAuditEntry.md | 0 .../IAuthenticationRateActionConfig.md | 0 .../IAuthenticationRateComponent.md | 0 .../reference/type-aliases/AuthAuditEvent.md | 0 .../reference/variables/AuthAuditEvent.md | 0 packages/api-auth-models/locales/en.json | 1 + packages/api-auth-models/package.json | 82 + packages/api-auth-models/src/index.ts | 14 + .../models/IAuthenticationAuditComponent.ts | 2 +- .../src/models/IAuthenticationAuditEntry.ts | 0 .../models/IAuthenticationRateActionConfig.ts | 0 .../models/IAuthenticationRateComponent.ts | 0 .../src/models/api/IAuditCreateRequest.ts | 0 .../src/models/api/IAuditGetRequest.ts | 0 .../src/models/api/IAuditGetResponse.ts | 0 .../src/models/api/IAuditQueryRequest.ts | 2 +- .../src/models/api/IAuditQueryResponse.ts | 0 .../src/models/api/IAuditRemoveRequest.ts | 0 .../src/models/api/IAuditUpdateRequest.ts | 0 .../src/models/authAuditEvent.ts | 0 packages/api-auth-models/tests/index.spec.ts | 6 + packages/api-auth-models/tests/tsconfig.json | 8 + packages/api-auth-models/tsconfig.json | 24 + packages/api-auth-models/typedoc.config.mjs | 24 + packages/api-auth-models/vitest.config.ts | 23 + packages/api-auth-rest-client/LICENSE | 201 ++ packages/api-auth-rest-client/README.md | 21 + .../api-auth-rest-client/docs/changelog.md | 1553 ++++++++++++++++ .../api-auth-rest-client/docs/examples.md | 111 ++ ...ityStorageAuthenticationAuditRestClient.md | 0 .../docs/reference/index.md | 5 + packages/api-auth-rest-client/locales/en.json | 1 + packages/api-auth-rest-client/package.json | 84 + ...ityStorageAuthenticationAuditRestClient.ts | 2 +- packages/api-auth-rest-client/src/index.ts | 3 + ...orageAuthenticationAuditRestClient.spec.ts | 0 .../tests/helpers/restClientTestHelpers.ts | 93 + .../api-auth-rest-client/tests/index.spec.ts | 6 + .../api-auth-rest-client/tests/tsconfig.json | 8 + packages/api-auth-rest-client/tsconfig.json | 24 + .../api-auth-rest-client/typedoc.config.mjs | 24 + .../api-auth-rest-client/vitest.config.ts | 23 + packages/api-auth-service/LICENSE | 201 ++ packages/api-auth-service/README.md | 21 + packages/api-auth-service/docs/changelog.md | 1619 +++++++++++++++++ packages/api-auth-service/docs/examples.md | 178 ++ .../api-auth-service/docs/open-api/spec.json | 911 ++++++++++ .../classes/AuthenticationAuditEntry.md | 0 .../classes/AuthenticationRateEntry.md | 0 ...EntityStorageAuthenticationAuditService.md | 0 .../EntityStorageAuthenticationRateService.md | 0 .../functions/authenticationAuditCreate.md | 0 .../functions/authenticationAuditGet.md | 0 .../functions/authenticationAuditQuery.md | 0 .../functions/authenticationAuditRemove.md | 0 .../functions/authenticationAuditUpdate.md | 0 .../generateRestRoutesAuthenticationAudit.md | 0 .../docs/reference/functions/initSchema.md | 9 + .../api-auth-service/docs/reference/index.md | 30 + ...StorageAuthenticationAuditServiceConfig.md | 0 ...nticationAuditServiceConstructorOptions.md | 0 ...yStorageAuthenticationRateServiceConfig.md | 0 ...enticationRateServiceConstructorOptions.md | 0 .../reference/variables/restEntryPoints.md | 5 + .../variables/tagsAuthenticationAudit.md | 0 packages/api-auth-service/locales/en.json | 17 + packages/api-auth-service/package.json | 97 + .../src/entities/authenticationAuditEntry.ts | 0 .../src/entities/authenticationRateEntry.ts | 0 packages/api-auth-service/src/index.ts | 13 + ...StorageAuthenticationAuditServiceConfig.ts | 0 ...nticationAuditServiceConstructorOptions.ts | 0 ...yStorageAuthenticationRateServiceConfig.ts | 0 ...enticationRateServiceConstructorOptions.ts | 0 .../api-auth-service/src/restEntryPoints.ts | 19 + .../entityStorageAuthenticationAuditRoutes.ts | 26 +- packages/api-auth-service/src/schema.ts | 18 + ...entityStorageAuthenticationAuditService.ts | 2 +- .../entityStorageAuthenticationRateService.ts | 2 +- ...tyStorageAuthenticationAuditRoutes.spec.ts | 2 +- ...yStorageAuthenticationAuditService.spec.ts | 0 ...tyStorageAuthenticationRateService.spec.ts | 0 packages/api-auth-service/tests/tsconfig.json | 8 + packages/api-auth-service/ts-to-openapi.json | 14 + packages/api-auth-service/tsconfig.json | 24 + packages/api-auth-service/typedoc.config.mjs | 24 + packages/api-auth-service/vitest.config.ts | 23 + release/release-please-config.prerelease.json | 15 + release/release-please-config.prod.json | 15 + .../release-please-manifest.prerelease.json | 3 + release/release-please-manifest.prod.json | 3 + 139 files changed, 6345 insertions(+), 1328 deletions(-) create mode 100644 packages/api-auth-models/LICENSE create mode 100644 packages/api-auth-models/README.md create mode 100644 packages/api-auth-models/docs/changelog.md create mode 100644 packages/api-auth-models/docs/examples.md create mode 100644 packages/api-auth-models/docs/reference/index.md rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuditCreateRequest.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuditGetRequest.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuditGetResponse.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuditQueryRequest.md (95%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuditQueryResponse.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuditRemoveRequest.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuditUpdateRequest.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuthenticationAuditComponent.md (97%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuthenticationAuditEntry.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuthenticationRateActionConfig.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/interfaces/IAuthenticationRateComponent.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/type-aliases/AuthAuditEvent.md (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/docs/reference/variables/AuthAuditEvent.md (100%) create mode 100644 packages/api-auth-models/locales/en.json create mode 100644 packages/api-auth-models/package.json create mode 100644 packages/api-auth-models/src/index.ts rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/IAuthenticationAuditComponent.ts (97%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/IAuthenticationAuditEntry.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/IAuthenticationRateActionConfig.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/IAuthenticationRateComponent.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/api/IAuditCreateRequest.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/api/IAuditGetRequest.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/api/IAuditGetResponse.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/api/IAuditQueryRequest.ts (95%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/api/IAuditQueryResponse.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/api/IAuditRemoveRequest.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/api/IAuditUpdateRequest.ts (100%) rename packages/{api-auth-entity-storage-models => api-auth-models}/src/models/authAuditEvent.ts (100%) create mode 100644 packages/api-auth-models/tests/index.spec.ts create mode 100644 packages/api-auth-models/tests/tsconfig.json create mode 100644 packages/api-auth-models/tsconfig.json create mode 100644 packages/api-auth-models/typedoc.config.mjs create mode 100644 packages/api-auth-models/vitest.config.ts create mode 100644 packages/api-auth-rest-client/LICENSE create mode 100644 packages/api-auth-rest-client/README.md create mode 100644 packages/api-auth-rest-client/docs/changelog.md create mode 100644 packages/api-auth-rest-client/docs/examples.md rename packages/{api-auth-entity-storage-rest-client => api-auth-rest-client}/docs/reference/classes/EntityStorageAuthenticationAuditRestClient.md (100%) create mode 100644 packages/api-auth-rest-client/docs/reference/index.md create mode 100644 packages/api-auth-rest-client/locales/en.json create mode 100644 packages/api-auth-rest-client/package.json rename packages/{api-auth-entity-storage-rest-client => api-auth-rest-client}/src/entityStorageAuthenticationAuditRestClient.ts (99%) create mode 100644 packages/api-auth-rest-client/src/index.ts rename packages/{api-auth-entity-storage-rest-client => api-auth-rest-client}/tests/entityStorageAuthenticationAuditRestClient.spec.ts (100%) create mode 100644 packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts create mode 100644 packages/api-auth-rest-client/tests/index.spec.ts create mode 100644 packages/api-auth-rest-client/tests/tsconfig.json create mode 100644 packages/api-auth-rest-client/tsconfig.json create mode 100644 packages/api-auth-rest-client/typedoc.config.mjs create mode 100644 packages/api-auth-rest-client/vitest.config.ts create mode 100644 packages/api-auth-service/LICENSE create mode 100644 packages/api-auth-service/README.md create mode 100644 packages/api-auth-service/docs/changelog.md create mode 100644 packages/api-auth-service/docs/examples.md create mode 100644 packages/api-auth-service/docs/open-api/spec.json rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/classes/AuthenticationAuditEntry.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/classes/AuthenticationRateEntry.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/classes/EntityStorageAuthenticationAuditService.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/classes/EntityStorageAuthenticationRateService.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/functions/authenticationAuditCreate.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/functions/authenticationAuditGet.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/functions/authenticationAuditQuery.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/functions/authenticationAuditRemove.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/functions/authenticationAuditUpdate.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/functions/generateRestRoutesAuthenticationAudit.md (100%) create mode 100644 packages/api-auth-service/docs/reference/functions/initSchema.md create mode 100644 packages/api-auth-service/docs/reference/index.md rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md (100%) create mode 100644 packages/api-auth-service/docs/reference/variables/restEntryPoints.md rename packages/{api-auth-entity-storage-service => api-auth-service}/docs/reference/variables/tagsAuthenticationAudit.md (100%) create mode 100644 packages/api-auth-service/locales/en.json create mode 100644 packages/api-auth-service/package.json rename packages/{api-auth-entity-storage-service => api-auth-service}/src/entities/authenticationAuditEntry.ts (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/src/entities/authenticationRateEntry.ts (100%) create mode 100644 packages/api-auth-service/src/index.ts rename packages/{api-auth-entity-storage-service => api-auth-service}/src/models/IEntityStorageAuthenticationAuditServiceConfig.ts (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/src/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.ts (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/src/models/IEntityStorageAuthenticationRateServiceConfig.ts (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/src/models/IEntityStorageAuthenticationRateServiceConstructorOptions.ts (100%) create mode 100644 packages/api-auth-service/src/restEntryPoints.ts rename packages/{api-auth-entity-storage-service => api-auth-service}/src/routes/entityStorageAuthenticationAuditRoutes.ts (95%) create mode 100644 packages/api-auth-service/src/schema.ts rename packages/{api-auth-entity-storage-service => api-auth-service}/src/services/entityStorageAuthenticationAuditService.ts (99%) rename packages/{api-auth-entity-storage-service => api-auth-service}/src/services/entityStorageAuthenticationRateService.ts (99%) rename packages/{api-auth-entity-storage-service => api-auth-service}/tests/routes/entityStorageAuthenticationAuditRoutes.spec.ts (98%) rename packages/{api-auth-entity-storage-service => api-auth-service}/tests/services/entityStorageAuthenticationAuditService.spec.ts (100%) rename packages/{api-auth-entity-storage-service => api-auth-service}/tests/services/entityStorageAuthenticationRateService.spec.ts (100%) create mode 100644 packages/api-auth-service/tests/tsconfig.json create mode 100644 packages/api-auth-service/ts-to-openapi.json create mode 100644 packages/api-auth-service/tsconfig.json create mode 100644 packages/api-auth-service/typedoc.config.mjs create mode 100644 packages/api-auth-service/vitest.config.ts diff --git a/eslint.config.js b/eslint.config.js index 7f3d738..4e2b91b 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -253,6 +253,10 @@ const tsRestrictedSyntaxCommon = [ message: 'new Error is disallowed as it is not specific enough, and bypasses the i18n formatting' }, + { + selector: "NewExpression[callee.name='RangeError']", + message: 'new RangeError is disallowed; use Guards and Validation from @twin.org/core instead' + }, { selector: "MemberExpression[object.name='process'][property.name='env']", message: diff --git a/package-lock.json b/package-lock.json index e6a0df6..7e95e82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "0.0.0", "license": "Apache-2.0", "workspaces": [ + "packages/api-auth-models", + "packages/api-auth-service", + "packages/api-auth-rest-client", "packages/api-auth-entity-storage-models", "packages/api-auth-entity-storage-service", "packages/api-auth-entity-storage-rest-client" @@ -200,9 +203,9 @@ } }, "node_modules/@commitlint/format": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.0.tgz", - "integrity": "sha512-c4q64xaav2U83t7k7RyzJerBZurPer7FxUOY0RL5L/6CZijZ7K+s6HIBGIghj0ey1P2+seRX0J9XQYtDued6tg==", + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.2.tgz", + "integrity": "sha512-v6fvxZSc/AvVMROlr3H34+1766bZSYApRUSCAMjWamStPjKMvZ8GdvVA5YW/VQNgbFTmcMz6OYmSTJEvIjPrfA==", "dev": true, "license": "MIT", "dependencies": { @@ -214,9 +217,9 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.0.tgz", - "integrity": "sha512-4/eB0vBN7L88O/oC4ajAEqi7j2ZfNgxl/+11RfAV9YosejZgDXhY2C9VcHnHJhOzPLoSy5P3Mg/46kqeyJfXKw==", + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.2.tgz", + "integrity": "sha512-9UoKNgfFE3LU7FrzierCvk3CdDfMDeVGC86qZiT/n0TIjfq/dmZ9MHuXd45OTNRa26ZanmJRxEtmiXk/lEJihg==", "dev": true, "license": "MIT", "dependencies": { @@ -228,15 +231,15 @@ } }, "node_modules/@commitlint/lint": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.0.tgz", - "integrity": "sha512-ceO5dp9pLjEZ6y6qbq/uXWXDPykqqlTsyzoQ0NzecpisSJhK3kTy9qzQoPeJuWG/IMNdV1lO0RgmzqoAlSi1uw==", + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.2.tgz", + "integrity": "sha512-Fy8JxEBzdmsYWFude/61GxXu5O+wEymwiRK2z9GL9R8mCsXphCoGxAFc5iHn5mjlfcSrhiiONE+ksf4KOjnaPg==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/is-ignored": "^21.2.0", - "@commitlint/parse": "^21.2.0", - "@commitlint/rules": "^21.2.0", + "@commitlint/is-ignored": "^21.2.2", + "@commitlint/parse": "^21.2.2", + "@commitlint/rules": "^21.2.2", "@commitlint/types": "^21.2.0" }, "engines": { @@ -244,15 +247,15 @@ } }, "node_modules/@commitlint/load": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.0.tgz", - "integrity": "sha512-RjlzWQqruRwIenJEfZtq7kG97co97nKoHpflE5YnF61tDLXxHPrdWImgzw6VL6MlFyaOcVlk74eBV8ZQmc3oIA==", + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.2.tgz", + "integrity": "sha512-0Tt6wDPX167cjKC5D4zhm0+20wJJG+TN/TKovMOspfSe78rOnKX+MNzlVNiu6HyQPZChPJ8QBH31MVt6Bb8fCg==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-validator": "^21.2.0", "@commitlint/execute-rule": "^21.0.1", - "@commitlint/resolve-extends": "^21.2.0", + "@commitlint/resolve-extends": "^21.2.2", "@commitlint/types": "^21.2.0", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", @@ -275,9 +278,9 @@ } }, "node_modules/@commitlint/parse": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.0.tgz", - "integrity": "sha512-QHWxG4d0PLTF634/AdyZ0MQS+CLn5YOuJlCFhMMlSGKFxzYGUetkHBj18xgBD+6fVzUrA2lrCdi/vlS2f/oYXg==", + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.2.tgz", + "integrity": "sha512-MEkobPfvRp+z06Wro8HMG1BDGHzZmj82A1LH1nWeG3ipHpg/x4m6v3wEDvMBIKjRFUnfR3nBeFs3MVCr7UdAmg==", "dev": true, "license": "MIT", "dependencies": { @@ -306,9 +309,9 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.0.tgz", - "integrity": "sha512-4O/1j51+79Wth9s/MGxt/5gs0XYLDgNlYpltQfhAvLE0itusLKs9zruxbiNg1oOkmkb9L9L4USYGjEj7n87NxA==", + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.2.tgz", + "integrity": "sha512-RPkJ/IFi7sMUUVbZLqwWFtWw/zRDcfFsmrPSiTMrt5wb7AdxOr86EGQFvmGzef5QKV5IPBWWCujqVTw1RWX44A==", "dev": true, "license": "MIT", "dependencies": { @@ -323,9 +326,9 @@ } }, "node_modules/@commitlint/rules": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.0.tgz", - "integrity": "sha512-C2yXMNpiB8ETZKfx5JD8+ExgF8vTU1VQMKPSUUYwqKpw9oJWQBrlXBpdU038mj2WPjof7o9UzFpmTyBeGMZwZg==", + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.2.tgz", + "integrity": "sha512-eplQzyYkBjYB1HyyRj8hkcK11Y9DU9nuBz7uOKEd6NpE9NGDytLFCAnlRE+OoiK/5sHEJsaz2RGhuWBvYzIbNA==", "dev": true, "license": "MIT", "dependencies": { @@ -2316,13 +2319,25 @@ "resolved": "packages/api-auth-entity-storage-service", "link": true }, + "node_modules/@twin.org/api-auth-models": { + "resolved": "packages/api-auth-models", + "link": true + }, + "node_modules/@twin.org/api-auth-rest-client": { + "resolved": "packages/api-auth-rest-client", + "link": true + }, + "node_modules/@twin.org/api-auth-service": { + "resolved": "packages/api-auth-service", + "link": true + }, "node_modules/@twin.org/api-core": { - "version": "0.9.2-next.7", - "resolved": "https://registry.npmjs.org/@twin.org/api-core/-/api-core-0.9.2-next.7.tgz", - "integrity": "sha512-+6mjwRBYesk/q9zGuyUhi++ekqXLH2oc7JsfW+HVYa+CxyjvRqDu00pHIrjjC/4CtYsrUdhUjMEa2zBORr4bEw==", + "version": "0.9.2-next.9", + "resolved": "https://registry.npmjs.org/@twin.org/api-core/-/api-core-0.9.2-next.9.tgz", + "integrity": "sha512-JZXWHeaNyV+0a+feqYE7RHNSLvwa8wzb45s/Bmes2N8nF+vW447IW9uPesmLTBR47HKO/szk0q/rV0IkqKZzQw==", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-models": "0.9.2-next.7", + "@twin.org/api-models": "0.9.2-next.9", "@twin.org/core": "next", "@twin.org/nameof": "next", "@twin.org/web": "next", @@ -2333,9 +2348,9 @@ } }, "node_modules/@twin.org/api-models": { - "version": "0.9.2-next.7", - "resolved": "https://registry.npmjs.org/@twin.org/api-models/-/api-models-0.9.2-next.7.tgz", - "integrity": "sha512-zAalwVegxhdbk3uQ4lvUKYQq0dUWw4k4+HBhauTecVssMurpEaE66Fe+Gdt2N8S/N67Q/VDSgUnCnP6K3ie1yw==", + "version": "0.9.2-next.9", + "resolved": "https://registry.npmjs.org/@twin.org/api-models/-/api-models-0.9.2-next.9.tgz", + "integrity": "sha512-hdNZFe1IDC/Xmk54QcgUTiMtcydPWL/2bvphdW7NvT4i5U5/Sxkw/ffahfu6UOBSx1tCHYahgfj83YsBCIBOUg==", "license": "Apache-2.0", "dependencies": { "@twin.org/context": "next", @@ -2442,9 +2457,9 @@ } }, "node_modules/@twin.org/entity-storage-connector-memory": { - "version": "0.9.2-next.9", - "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-connector-memory/-/entity-storage-connector-memory-0.9.2-next.9.tgz", - "integrity": "sha512-glr6JsxpMj3IXxOkuOb3UUFLgDVuM1vZBuDzanDvCcSEeIyCEuNyhHehmEBQkSmOqgApbR096uft/O42jtivoA==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-connector-memory/-/entity-storage-connector-memory-0.9.2-next.11.tgz", + "integrity": "sha512-Cwb4ZWjHLHaMTTw3I72kx+XXVoHA7SGnHWodBsVFpbFuPLWwF/S9kkc13jyi70LNq3z4lewu4/Tbf5BElAMLYA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2452,7 +2467,7 @@ "@twin.org/context": "next", "@twin.org/core": "next", "@twin.org/entity": "next", - "@twin.org/entity-storage-models": "0.9.2-next.9", + "@twin.org/entity-storage-models": "0.9.2-next.11", "@twin.org/logging-models": "next", "@twin.org/nameof": "next" }, @@ -2461,9 +2476,9 @@ } }, "node_modules/@twin.org/entity-storage-models": { - "version": "0.9.2-next.9", - "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-models/-/entity-storage-models-0.9.2-next.9.tgz", - "integrity": "sha512-/MOeJlT45DxWwyYxzJ8WtEkX8zQS1btIgCY0osJe98IQ7SZhuTQ3lmNiLVnxyKH4GpH31sA/ksI5VE+yOgB1Lg==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-models/-/entity-storage-models-0.9.2-next.11.tgz", + "integrity": "sha512-0x6yyzPmTo8DwVv7LAPNPawDg3YzKdaDgzhwPVB4xh9aQzUm3+8f6zWAzyZwcBeouACzmfFEmJZ/ellNP1Tp2A==", "license": "Apache-2.0", "dependencies": { "@twin.org/context": "next", @@ -3815,9 +3830,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.13", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz", - "integrity": "sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==", + "version": "2.11.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.14.tgz", + "integrity": "sha512-JyJ954WzuIR8/FFzX0o5krdSTrBAkcCSRfWSleRsIHSWV+cZe2FI1PKggVkFke1hBldRs+LRxUczzE9iPmgZww==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5065,9 +5080,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.405", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.405.tgz", - "integrity": "sha512-bNglH7lPH5l+yHOes7Zr4VqxhOy4BQ9ZBUX4VdoFgxMpzJk7W1ZoO3Vgd9Pxa9PyjQ76sfm2aKH/nzEcCNRlew==", + "version": "1.5.408", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.408.tgz", + "integrity": "sha512-SLoprcYpJ/OH2v2ps0+N5biv9H4/KBT3+YmmDew64TwK5y9j2wv7pMOFY7IorVkyMtEyLSCRlXKLsNlakeAlPw==", "dev": true, "license": "ISC" }, @@ -5144,22 +5159,23 @@ } }, "node_modules/es-module-lexer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", - "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, "node_modules/es-toolkit": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.50.0.tgz", - "integrity": "sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.51.0.tgz", + "integrity": "sha512-zC2lQGkM7QX+Gm6iM3+WIdZJzthsEd14LvRNJneSO2hzyz/zNBENR8+YXWo1cKxgPBtV6ksPYHELbcwBRzmdCw==", "dev": true, "license": "MIT", "workspaces": [ "docs", "benchmarks", - "tests/types" + "tests/types", + "tests/browser-compat" ] }, "node_modules/escalade": { @@ -10859,7 +10875,7 @@ }, "packages/api-auth-entity-storage-models": { "name": "@twin.org/api-auth-entity-storage-models", - "version": "0.9.2-next.7", + "version": "0.9.2-next.9", "license": "Apache-2.0", "dependencies": { "@twin.org/core": "next", @@ -10897,10 +10913,11 @@ }, "packages/api-auth-entity-storage-rest-client": { "name": "@twin.org/api-auth-entity-storage-rest-client", - "version": "0.9.2-next.7", + "version": "0.9.2-next.9", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.7", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.2-next.9", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", @@ -10938,10 +10955,11 @@ }, "packages/api-auth-entity-storage-service": { "name": "@twin.org/api-auth-entity-storage-service", - "version": "0.9.2-next.7", + "version": "0.9.2-next.9", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.7", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.2-next.9", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", @@ -10985,6 +11003,134 @@ "dependencies": { "undici-types": "~8.3.0" } + }, + "packages/api-auth-models": { + "name": "@twin.org/api-auth-models", + "version": "0.9.2-next.9", + "license": "Apache-2.0", + "dependencies": { + "@twin.org/core": "next", + "@twin.org/nameof": "next" + }, + "devDependencies": { + "@twin.org/nameof-transformer": "next", + "@twin.org/nameof-vitest-plugin": "next", + "@twin.org/validate-locales": "next", + "@types/node": "26.1.2", + "@vitest/coverage-v8": "4.1.10", + "copyfiles": "2.4.1", + "nodemon": "3.1.14", + "rimraf": "6.1.3", + "ts-patch": "4.0.1", + "typedoc": "0.28.20", + "typedoc-plugin-markdown": "4.12.0", + "typescript": "6.0.3", + "vitest": "4.1.10" + }, + "engines": { + "node": ">=24.0.0" + } + }, + "packages/api-auth-models/node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "packages/api-auth-rest-client": { + "name": "@twin.org/api-auth-rest-client", + "version": "0.9.2-next.9", + "license": "Apache-2.0", + "dependencies": { + "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-core": "next", + "@twin.org/api-models": "next", + "@twin.org/core": "next", + "@twin.org/nameof": "next", + "@twin.org/web": "next" + }, + "devDependencies": { + "@twin.org/nameof-transformer": "next", + "@twin.org/nameof-vitest-plugin": "next", + "@twin.org/validate-locales": "next", + "@types/node": "26.1.2", + "@vitest/coverage-v8": "4.1.10", + "copyfiles": "2.4.1", + "nodemon": "3.1.14", + "rimraf": "6.1.3", + "ts-patch": "4.0.1", + "typedoc": "0.28.20", + "typedoc-plugin-markdown": "4.12.0", + "typescript": "6.0.3", + "vitest": "4.1.10" + }, + "engines": { + "node": ">=24.0.0" + } + }, + "packages/api-auth-rest-client/node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "packages/api-auth-service": { + "name": "@twin.org/api-auth-service", + "version": "0.9.2-next.9", + "license": "Apache-2.0", + "dependencies": { + "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-core": "next", + "@twin.org/api-models": "next", + "@twin.org/background-task-models": "next", + "@twin.org/context": "next", + "@twin.org/core": "next", + "@twin.org/crypto": "next", + "@twin.org/entity": "next", + "@twin.org/entity-storage-models": "next", + "@twin.org/logging-models": "next", + "@twin.org/nameof": "next", + "@twin.org/vault-models": "next", + "@twin.org/web": "next" + }, + "devDependencies": { + "@twin.org/entity-storage-connector-memory": "next", + "@twin.org/nameof-transformer": "next", + "@twin.org/nameof-vitest-plugin": "next", + "@twin.org/ts-to-openapi": "next", + "@twin.org/validate-locales": "next", + "@types/node": "26.1.2", + "@vitest/coverage-v8": "4.1.10", + "copyfiles": "2.4.1", + "nodemon": "3.1.14", + "rimraf": "6.1.3", + "ts-patch": "4.0.1", + "typedoc": "0.28.20", + "typedoc-plugin-markdown": "4.12.0", + "typescript": "6.0.3", + "vitest": "4.1.10" + }, + "engines": { + "node": ">=24.0.0" + } + }, + "packages/api-auth-service/node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } } } } diff --git a/package.json b/package.json index 6cf7958..1a5ce30 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,9 @@ "release-please": "17.10.4" }, "workspaces": [ + "packages/api-auth-models", + "packages/api-auth-service", + "packages/api-auth-rest-client", "packages/api-auth-entity-storage-models", "packages/api-auth-entity-storage-service", "packages/api-auth-entity-storage-rest-client" diff --git a/packages/api-auth-entity-storage-models/docs/reference/index.md b/packages/api-auth-entity-storage-models/docs/reference/index.md index 74d88bb..726ceb6 100644 --- a/packages/api-auth-entity-storage-models/docs/reference/index.md +++ b/packages/api-auth-entity-storage-models/docs/reference/index.md @@ -3,11 +3,7 @@ ## Interfaces - [IAuthenticationAdminComponent](interfaces/IAuthenticationAdminComponent.md) -- [IAuthenticationAuditComponent](interfaces/IAuthenticationAuditComponent.md) -- [IAuthenticationAuditEntry](interfaces/IAuthenticationAuditEntry.md) - [IAuthenticationComponent](interfaces/IAuthenticationComponent.md) -- [IAuthenticationRateActionConfig](interfaces/IAuthenticationRateActionConfig.md) -- [IAuthenticationRateComponent](interfaces/IAuthenticationRateComponent.md) - [IAuthenticationUser](interfaces/IAuthenticationUser.md) - [IAuthenticationUserSecure](interfaces/IAuthenticationUserSecure.md) - [IAdminUserCreateRequest](interfaces/IAdminUserCreateRequest.md) @@ -17,24 +13,9 @@ - [IAdminUserRemoveRequest](interfaces/IAdminUserRemoveRequest.md) - [IAdminUserUpdatePasswordRequest](interfaces/IAdminUserUpdatePasswordRequest.md) - [IAdminUserUpdateRequest](interfaces/IAdminUserUpdateRequest.md) -- [IAuditCreateRequest](interfaces/IAuditCreateRequest.md) -- [IAuditGetRequest](interfaces/IAuditGetRequest.md) -- [IAuditGetResponse](interfaces/IAuditGetResponse.md) -- [IAuditQueryRequest](interfaces/IAuditQueryRequest.md) -- [IAuditQueryResponse](interfaces/IAuditQueryResponse.md) -- [IAuditRemoveRequest](interfaces/IAuditRemoveRequest.md) -- [IAuditUpdateRequest](interfaces/IAuditUpdateRequest.md) - [ILoginRequest](interfaces/ILoginRequest.md) - [ILoginResponse](interfaces/ILoginResponse.md) - [ILogoutRequest](interfaces/ILogoutRequest.md) - [IRefreshTokenRequest](interfaces/IRefreshTokenRequest.md) - [IRefreshTokenResponse](interfaces/IRefreshTokenResponse.md) - [IUpdatePasswordRequest](interfaces/IUpdatePasswordRequest.md) - -## Type Aliases - -- [AuthAuditEvent](type-aliases/AuthAuditEvent.md) - -## Variables - -- [AuthAuditEvent](variables/AuthAuditEvent.md) diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md index 1a9502d..6978a5e 100644 --- a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md +++ b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md @@ -32,8 +32,8 @@ The users organization. *** -### scope {#scope} +### roles {#roles} -> **scope**: `string`[] +> **roles**: `string`[] -The scope assigned to the user, comma separated. +The roles assigned to the user. diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md index 81adf21..b543c44 100644 --- a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md +++ b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md @@ -44,15 +44,15 @@ The users organization. *** -### scope {#scope} +### roles {#roles} -> **scope**: `string`[] +> **roles**: `string`[] -The scope assigned to the user, comma separated. +The roles assigned to the user. #### Inherited from -[`IAuthenticationUser`](IAuthenticationUser.md).[`scope`](IAuthenticationUser.md#scope) +[`IAuthenticationUser`](IAuthenticationUser.md).[`roles`](IAuthenticationUser.md#roles) *** diff --git a/packages/api-auth-entity-storage-models/src/index.ts b/packages/api-auth-entity-storage-models/src/index.ts index ff0bf13..eb7db4c 100644 --- a/packages/api-auth-entity-storage-models/src/index.ts +++ b/packages/api-auth-entity-storage-models/src/index.ts @@ -7,25 +7,13 @@ export * from "./models/api/IAdminUserGetResponse.js"; export * from "./models/api/IAdminUserRemoveRequest.js"; export * from "./models/api/IAdminUserUpdatePasswordRequest.js"; export * from "./models/api/IAdminUserUpdateRequest.js"; -export * from "./models/api/IAuditCreateRequest.js"; -export * from "./models/api/IAuditGetRequest.js"; -export * from "./models/api/IAuditGetResponse.js"; -export * from "./models/api/IAuditQueryRequest.js"; -export * from "./models/api/IAuditQueryResponse.js"; -export * from "./models/api/IAuditRemoveRequest.js"; -export * from "./models/api/IAuditUpdateRequest.js"; export * from "./models/api/ILoginRequest.js"; export * from "./models/api/ILoginResponse.js"; export * from "./models/api/ILogoutRequest.js"; export * from "./models/api/IRefreshTokenRequest.js"; export * from "./models/api/IRefreshTokenResponse.js"; export * from "./models/api/IUpdatePasswordRequest.js"; -export * from "./models/authAuditEvent.js"; export * from "./models/IAuthenticationAdminComponent.js"; -export * from "./models/IAuthenticationAuditComponent.js"; -export * from "./models/IAuthenticationAuditEntry.js"; export * from "./models/IAuthenticationComponent.js"; -export * from "./models/IAuthenticationRateActionConfig.js"; -export * from "./models/IAuthenticationRateComponent.js"; export * from "./models/IAuthenticationUser.js"; export * from "./models/IAuthenticationUserSecure.js"; diff --git a/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts b/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts index 8880132..d54e9c2 100644 --- a/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts +++ b/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts @@ -21,7 +21,7 @@ export interface IAuthenticationUser { organizationIdentity: string; /** - * The scope assigned to the user, comma separated. + * The roles assigned to the user. */ - scope: string[]; + roles: string[]; } diff --git a/packages/api-auth-entity-storage-rest-client/docs/reference/index.md b/packages/api-auth-entity-storage-rest-client/docs/reference/index.md index ae36744..af3cd8b 100644 --- a/packages/api-auth-entity-storage-rest-client/docs/reference/index.md +++ b/packages/api-auth-entity-storage-rest-client/docs/reference/index.md @@ -3,7 +3,6 @@ ## Classes - [EntityStorageAuthenticationAdminRestClient](classes/EntityStorageAuthenticationAdminRestClient.md) -- [EntityStorageAuthenticationAuditRestClient](classes/EntityStorageAuthenticationAuditRestClient.md) - [EntityStorageAuthenticationRestClient](classes/EntityStorageAuthenticationRestClient.md) ## Interfaces diff --git a/packages/api-auth-entity-storage-rest-client/docs/reference/interfaces/IEntityStorageAuthenticationRestClientConstructorOptions.md b/packages/api-auth-entity-storage-rest-client/docs/reference/interfaces/IEntityStorageAuthenticationRestClientConstructorOptions.md index d8fd4ab..2bc5e41 100644 --- a/packages/api-auth-entity-storage-rest-client/docs/reference/interfaces/IEntityStorageAuthenticationRestClientConstructorOptions.md +++ b/packages/api-auth-entity-storage-rest-client/docs/reference/interfaces/IEntityStorageAuthenticationRestClientConstructorOptions.md @@ -82,6 +82,18 @@ Include credentials in the request, defaults to true. *** +### processorTypes? {#processortypes} + +> `optional` **processorTypes?**: `string`[] + +The types of the processors to run around each request, resolved from the `RestClientProcessorFactory`. + +#### Inherited from + +`IBaseRestClientConfig.processorTypes` + +*** + ### customHeaders? {#customheaders} > `optional` **customHeaders?**: () => `Promise`\<`IHttpHeaders`\> diff --git a/packages/api-auth-entity-storage-rest-client/package.json b/packages/api-auth-entity-storage-rest-client/package.json index 8935d19..de05ec9 100644 --- a/packages/api-auth-entity-storage-rest-client/package.json +++ b/packages/api-auth-entity-storage-rest-client/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.2-next.9", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-entity-storage-rest-client/src/index.ts b/packages/api-auth-entity-storage-rest-client/src/index.ts index c012288..49c8558 100644 --- a/packages/api-auth-entity-storage-rest-client/src/index.ts +++ b/packages/api-auth-entity-storage-rest-client/src/index.ts @@ -1,6 +1,5 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. export * from "./entityStorageAuthenticationAdminRestClient.js"; -export * from "./entityStorageAuthenticationAuditRestClient.js"; export * from "./entityStorageAuthenticationRestClient.js"; export * from "./models/entityStorageAuthenticationRestClientConstructorOptions.js"; diff --git a/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts b/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts index cc9c943..6f2d981 100644 --- a/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts +++ b/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts @@ -18,7 +18,7 @@ const TEST_USER = { email: "user@example.com", userIdentity: "did:example:123456789abcdefghi", organizationIdentity: "did:example:123456789abcdefghi", - scope: ["scope1", "scope2"] + roles: ["role1", "role2"] }; const fetchMock = vi.fn(); diff --git a/packages/api-auth-entity-storage-service/docs/open-api/spec.json b/packages/api-auth-entity-storage-service/docs/open-api/spec.json index e5e7e6c..8940500 100644 --- a/packages/api-auth-entity-storage-service/docs/open-api/spec.json +++ b/packages/api-auth-entity-storage-service/docs/open-api/spec.json @@ -22,10 +22,6 @@ { "name": "Authentication Admin", "description": "Authentication Admin endpoints for the REST server." - }, - { - "name": "Authentication Audit", - "description": "Authentication audit endpoints for the REST server." } ], "paths": { @@ -464,9 +460,9 @@ "password": "MyPassword123!", "userIdentity": "did:example:123456789abcdefghi", "organizationIdentity": "did:example:123456789abcdefghi", - "scope": [ - "scope1", - "scope2" + "roles": [ + "role1", + "role2" ] } } @@ -577,9 +573,9 @@ "value": { "userIdentity": "did:example:123456789abcdefghi", "organizationIdentity": "did:example:123456789abcdefghi", - "scope": [ - "scope1", - "scope2" + "roles": [ + "role1", + "role2" ] } } @@ -689,9 +685,9 @@ "email": "user@example.com", "userIdentity": "did:example:123456789abcdefghi", "organizationIdentity": "did:example:123456789abcdefghi", - "scope": [ - "scope1", - "scope2" + "roles": [ + "role1", + "role2" ] } } @@ -994,9 +990,9 @@ "email": "user@example.com", "userIdentity": "did:example:123456789abcdefghi", "organizationIdentity": "did:example:123456789abcdefghi", - "scope": [ - "scope1", - "scope2" + "roles": [ + "role1", + "role2" ] } } @@ -1063,905 +1059,64 @@ } } } - }, - "/authentication/audit": { - "post": { - "operationId": "authenticationAuditCreate", - "summary": "Create an authentication audit entry", - "tags": [ - "Authentication Audit" + } + }, + "components": { + "schemas": { + "AdminUserCreateRequest": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" ], - "security": [ + "allOf": [ { - "jwtBearerAuthScheme": [] + "$ref": "#/components/schemas/AuthenticationUser" } ], - "requestBody": { - "description": "Create an authentication audit entry.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditCreateRequest" - }, - "examples": { - "authenticationAuditCreateRequestExample": { - "summary": "The request to create an authentication audit entry.", - "value": { - "actorId": "user@example.com", - "event": "login-success", - "data": { - "organizationIdentity": "did:example:org1" - } - } - } - } - } - } - }, - "responses": { - "201": { - "description": "The rest request ended in created response.", - "headers": { - "location": { - "schema": { - "type": "string" - }, - "description": "e.g. 018f0b53d5d5704fa3a06d6ed2478575" - } - } - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } + "description": "The body of the request." + }, + "AdminUserUpdatePasswordRequest": { + "type": "object", + "properties": { + "newPassword": { + "type": "string", + "description": "The new password for the user." }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } + "currentPassword": { + "type": "string", + "description": "The current password for the user." } - } - }, - "get": { - "operationId": "authenticationAuditQuery", - "summary": "Query authentication audit entries", - "tags": [ - "Authentication Audit" + }, + "required": [ + "newPassword" ], - "parameters": [ - { - "name": "actorId", - "description": "The actor identifier to filter by.", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "example": "user@example.com" - }, - { - "name": "organizationId", - "description": "The organization identifier to filter by.", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "tenantId", - "description": "The tenant identifier to filter by.", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "nodeId", - "description": "The node identifier to filter by.", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "event", - "description": "The event to filter by.", - "in": "query", - "required": false, - "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/AuthAuditEvent" - }, - { - "type": "string" - } - ] - }, - "example": "login-success" - }, - { - "name": "startDate", - "description": "The inclusive start date for filtering, in ISO 8601 format.", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "example": "2026-01-01T00:00:00.000Z" - }, - { - "name": "endDate", - "description": "The inclusive end date for filtering, in ISO 8601 format.", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "example": "2026-01-31T23:59:59.999Z" + "description": "The body of the request." + }, + "AdminUserUpdateRequest": { + "description": "The body of the request.", + "type": "object", + "properties": { + "userIdentity": { + "type": "string", + "description": "The user identity." }, - { - "name": "cursor", - "description": "The pagination cursor.", - "in": "query", - "required": false, - "schema": { - "type": "string" - } + "organizationIdentity": { + "type": "string", + "description": "The users organization." }, - { - "name": "limit", - "description": "The maximum number of results to return.", - "in": "query", - "required": false, - "schema": { + "roles": { + "type": "array", + "items": { "type": "string" }, - "example": "50" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "responses": { - "200": { - "description": "Response from querying authentication audit entries.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditQueryResponse" - }, - "examples": { - "authenticationAuditQueryResponseExample": { - "summary": "The response for querying authentication audit entries.", - "value": { - "entries": [ - { - "id": "018f0b53d5d5704fa3a06d6ed2478575", - "actorId": "user@example.com", - "dateCreated": "2026-01-12T09:05:23.123Z", - "event": "login-success", - "data": { - "organizationIdentity": "did:example:org1" - } - } - ], - "cursor": "next-cursor" - } - } - } - } - } - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } + "description": "The roles assigned to the user." } } - } - }, - "/authentication/audit/{id}": { - "get": { - "operationId": "authenticationAuditGet", - "summary": "Get an authentication audit entry", - "tags": [ - "Authentication Audit" - ], - "parameters": [ - { - "name": "id", - "description": "The audit entry id.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "018f0b53d5d5704fa3a06d6ed2478575" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "responses": { - "200": { - "description": "Response from getting an authentication audit entry.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthenticationAuditEntry" - }, - "examples": { - "authenticationAuditGetResponseExample": { - "summary": "The response for getting an authentication audit entry.", - "value": { - "id": "018f0b53d5d5704fa3a06d6ed2478575", - "actorId": "user@example.com", - "dateCreated": "2026-01-12T09:05:23.123Z", - "event": "login-success", - "data": { - "organizationIdentity": "did:example:org1" - } - } - } - } - } - } - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - }, - "put": { - "operationId": "authenticationAuditUpdate", - "summary": "Update an authentication audit entry", - "tags": [ - "Authentication Audit" - ], - "parameters": [ - { - "name": "id", - "description": "The audit entry id.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "018f0b53d5d5704fa3a06d6ed2478575" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "requestBody": { - "description": "Update an authentication audit entry.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditUpdateRequest" - }, - "examples": { - "authenticationAuditUpdateRequestExample": { - "summary": "The request to update an authentication audit entry.", - "value": { - "data": { - "organizationIdentity": "did:example:org1" - } - } - } - } - } - } - }, - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - }, - "delete": { - "operationId": "authenticationAuditRemove", - "summary": "Remove an authentication audit entry", - "tags": [ - "Authentication Audit" - ], - "parameters": [ - { - "name": "id", - "description": "The audit entry id.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "018f0b53d5d5704fa3a06d6ed2478575" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "AdminUserCreateRequest": { - "type": "object", - "properties": { - "password": { - "type": "string" - } - }, - "required": [ - "password" - ], - "allOf": [ - { - "$ref": "#/components/schemas/AuthenticationUser" - } - ], - "description": "The body of the request." - }, - "AdminUserUpdatePasswordRequest": { - "type": "object", - "properties": { - "newPassword": { - "type": "string", - "description": "The new password for the user." - }, - "currentPassword": { - "type": "string", - "description": "The current password for the user." - } - }, - "required": [ - "newPassword" - ], - "description": "The body of the request." - }, - "AdminUserUpdateRequest": { - "description": "The body of the request.", - "type": "object", - "properties": { - "userIdentity": { - "type": "string", - "description": "The user identity." - }, - "organizationIdentity": { - "type": "string", - "description": "The users organization." - }, - "scope": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The scope assigned to the user, comma separated." - } - } - }, - "AuditCreateRequest": { - "description": "The body of the request.", - "type": "object", - "properties": { - "event": { - "anyOf": [ - { - "$ref": "#/components/schemas/AuthAuditEvent" - }, - { - "type": "string" - } - ], - "description": "The audit event that occurred." - }, - "actorId": { - "type": "string", - "description": "The actor identifier, could be e-mail, username, or other unique identifier." - }, - "nodeId": { - "type": "string", - "description": "The node identifier associated with the audit entry, if applicable." - }, - "organizationId": { - "type": "string", - "description": "The organization identifier associated with the audit entry, if applicable." - }, - "tenantId": { - "type": "string", - "description": "The tenant identifier associated with the audit entry, if applicable." - }, - "ipAddressHashes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The hashed IP addresses of the client." - }, - "userAgent": { - "type": "string", - "description": "The user agent string of the client." - }, - "correlationId": { - "type": "string", - "description": "The correlation ID for request tracing." - }, - "data": { - "description": "Additional data related to the audit entry, such as IP address, user agent, etc." - } - }, - "required": [ - "event" - ] - }, - "AuditQueryResponse": { - "type": "object", - "properties": { - "entries": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AuthenticationAuditEntry" - }, - "description": "The returned audit entries." - }, - "cursor": { - "type": "string", - "description": "The cursor to retrieve the next page, if any." - } - }, - "required": [ - "entries" - ], - "description": "The response body." - }, - "AuditUpdateRequest": { - "description": "The body of the request.", - "type": "object", - "properties": { - "event": { - "anyOf": [ - { - "$ref": "#/components/schemas/AuthAuditEvent" - }, - { - "type": "string" - } - ], - "description": "The audit event that occurred." - }, - "actorId": { - "type": "string", - "description": "The actor identifier, could be e-mail, username, or other unique identifier." - }, - "nodeId": { - "type": "string", - "description": "The node identifier associated with the audit entry, if applicable." - }, - "organizationId": { - "type": "string", - "description": "The organization identifier associated with the audit entry, if applicable." - }, - "tenantId": { - "type": "string", - "description": "The tenant identifier associated with the audit entry, if applicable." - }, - "ipAddressHashes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The hashed IP addresses of the client." - }, - "userAgent": { - "type": "string", - "description": "The user agent string of the client." - }, - "correlationId": { - "type": "string", - "description": "The correlation ID for request tracing." - }, - "data": { - "description": "Additional data related to the audit entry, such as IP address, user agent, etc." - } - } - }, - "AuthAuditEvent": { - "description": "Supported authentication audit event values.", - "anyOf": [ - { - "const": "login-success", - "description": "Login success." - }, - { - "const": "login-failure", - "description": "Login failure." - }, - { - "const": "logout", - "description": "Logout." - }, - { - "const": "token-refreshed", - "description": "Token refreshed." - }, - { - "const": "account-created", - "description": "Account created." - }, - { - "const": "account-deleted", - "description": "Account deleted." - }, - { - "const": "account-updated", - "description": "Account updated." - }, - { - "const": "account-locked", - "description": "Account locked." - }, - { - "const": "account-unlocked", - "description": "Account unlocked." - }, - { - "const": "password-changed", - "description": "Password changed." - } - ] - }, - "AuthenticationAuditEntry": { - "description": "Contract definition for authentication audit entry.", - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the audit entry." - }, - "event": { - "anyOf": [ - { - "$ref": "#/components/schemas/AuthAuditEvent" - }, - { - "type": "string" - } - ], - "description": "The audit event that occurred." - }, - "dateCreated": { - "type": "string", - "description": "The timestamp of the audit entry in ISO 8601 format." - }, - "actorId": { - "type": "string", - "description": "The actor identifier, could be e-mail, username, or other unique identifier." - }, - "nodeId": { - "type": "string", - "description": "The node identifier associated with the audit entry, if applicable." - }, - "organizationId": { - "type": "string", - "description": "The organization identifier associated with the audit entry, if applicable." - }, - "tenantId": { - "type": "string", - "description": "The tenant identifier associated with the audit entry, if applicable." - }, - "ipAddressHashes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The hashed IP addresses of the client." - }, - "userAgent": { - "type": "string", - "description": "The user agent string of the client." - }, - "correlationId": { - "type": "string", - "description": "The correlation ID for request tracing." - }, - "data": { - "description": "Additional data related to the audit entry, such as IP address, user agent, etc." - } - }, - "required": [ - "id", - "event", - "dateCreated" - ] }, "AuthenticationUser": { "description": "Contract definition for authentication user.", @@ -1979,19 +1134,19 @@ "type": "string", "description": "The users organization." }, - "scope": { + "roles": { "type": "array", "items": { "type": "string" }, - "description": "The scope assigned to the user, comma separated." + "description": "The roles assigned to the user." } }, "required": [ "email", "userIdentity", "organizationIdentity", - "scope" + "roles" ] }, "Error": { diff --git a/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md b/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md index 4bb1f9a..9d7c1f1 100644 --- a/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md +++ b/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md @@ -54,11 +54,11 @@ The users organization. *** -### scope {#scope} +### roles {#roles} -> **scope**: `string` +> **roles**: `string` -The scope assigned to the user, comma separated. +The roles assigned to the user, bar separated. *** diff --git a/packages/api-auth-entity-storage-service/docs/reference/classes/TokenHelper.md b/packages/api-auth-entity-storage-service/docs/reference/classes/TokenHelper.md index 6f86bbb..8ecfb1a 100644 --- a/packages/api-auth-entity-storage-service/docs/reference/classes/TokenHelper.md +++ b/packages/api-auth-entity-storage-service/docs/reference/classes/TokenHelper.md @@ -24,7 +24,7 @@ Runtime name for the class. ### createToken() {#createtoken} -> `static` **createToken**(`vaultConnector`, `nodeId`, `signingKeyName`, `userIdentity`, `organizationIdentity`, `tenantId`, `ttlMinutes`, `scope?`, `passwordVersion?`): `Promise`\<\{ `token`: `string`; `expiry`: `number`; \}\> +> `static` **createToken**(`vaultConnector`, `nodeId`, `signingKeyName`, `userIdentity`, `organizationIdentity`, `tenantId`, `ttlMinutes`, `passwordVersion?`): `Promise`\<\{ `token`: `string`; `expiry`: `number`; \}\> Create a new token. @@ -72,12 +72,6 @@ The tenant id for the token. The time to live for the token in minutes. -##### scope? - -`string` - -The scopes for the token. - ##### passwordVersion? `number` @@ -94,7 +88,7 @@ The new token and its expiry date. ### verify() {#verify} -> `static` **verify**(`vaultConnector`, `nodeId`, `signingKeyName`, `token`, `requiredScopes?`, `verifyUser?`): `Promise`\<\{ `header`: `JWTHeaderParameters`; `payload`: `JWTPayload`; \}\> +> `static` **verify**(`vaultConnector`, `nodeId`, `signingKeyName`, `token`, `verifyUser?`): `Promise`\<\{ `header`: `JWTHeaderParameters`; `payload`: `JWTPayload`; \}\> Verify the token. @@ -124,12 +118,6 @@ The signing key name, expected to match the JWT key identifier. The token to verify. -##### requiredScopes? - -`string`[] - -The required scopes. - ##### verifyUser? (`sub`, `org`, `tid`, `passwordVersion`) => `Promise`\<`string`[]\> diff --git a/packages/api-auth-entity-storage-service/docs/reference/index.md b/packages/api-auth-entity-storage-service/docs/reference/index.md index 0846d0a..a388493 100644 --- a/packages/api-auth-entity-storage-service/docs/reference/index.md +++ b/packages/api-auth-entity-storage-service/docs/reference/index.md @@ -2,13 +2,9 @@ ## Classes -- [AuthenticationAuditEntry](classes/AuthenticationAuditEntry.md) -- [AuthenticationRateEntry](classes/AuthenticationRateEntry.md) - [AuthenticationUser](classes/AuthenticationUser.md) - [AuthHeaderProcessor](classes/AuthHeaderProcessor.md) - [EntityStorageAuthenticationAdminService](classes/EntityStorageAuthenticationAdminService.md) -- [EntityStorageAuthenticationAuditService](classes/EntityStorageAuthenticationAuditService.md) -- [EntityStorageAuthenticationRateService](classes/EntityStorageAuthenticationRateService.md) - [EntityStorageAuthenticationService](classes/EntityStorageAuthenticationService.md) - [PasswordHelper](classes/PasswordHelper.md) - [TokenHelper](classes/TokenHelper.md) @@ -19,10 +15,6 @@ - [IAuthHeaderProcessorConstructorOptions](interfaces/IAuthHeaderProcessorConstructorOptions.md) - [IEntityStorageAuthenticationAdminServiceConfig](interfaces/IEntityStorageAuthenticationAdminServiceConfig.md) - [IEntityStorageAuthenticationAdminServiceConstructorOptions](interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md) -- [IEntityStorageAuthenticationAuditServiceConfig](interfaces/IEntityStorageAuthenticationAuditServiceConfig.md) -- [IEntityStorageAuthenticationAuditServiceConstructorOptions](interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md) -- [IEntityStorageAuthenticationRateServiceConfig](interfaces/IEntityStorageAuthenticationRateServiceConfig.md) -- [IEntityStorageAuthenticationRateServiceConstructorOptions](interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md) - [IEntityStorageAuthenticationServiceConfig](interfaces/IEntityStorageAuthenticationServiceConfig.md) - [IEntityStorageAuthenticationServiceConstructorOptions](interfaces/IEntityStorageAuthenticationServiceConstructorOptions.md) @@ -30,7 +22,6 @@ - [restEntryPoints](variables/restEntryPoints.md) - [tagsAuthenticationAdmin](variables/tagsAuthenticationAdmin.md) -- [tagsAuthenticationAudit](variables/tagsAuthenticationAudit.md) - [tagsAuthentication](variables/tagsAuthentication.md) ## Functions @@ -42,12 +33,6 @@ - [authenticationAdminGetUser](functions/authenticationAdminGetUser.md) - [authenticationAdminGetUserByIdentity](functions/authenticationAdminGetUserByIdentity.md) - [authenticationAdminRemoveUser](functions/authenticationAdminRemoveUser.md) -- [generateRestRoutesAuthenticationAudit](functions/generateRestRoutesAuthenticationAudit.md) -- [authenticationAuditCreate](functions/authenticationAuditCreate.md) -- [authenticationAuditGet](functions/authenticationAuditGet.md) -- [authenticationAuditUpdate](functions/authenticationAuditUpdate.md) -- [authenticationAuditRemove](functions/authenticationAuditRemove.md) -- [authenticationAuditQuery](functions/authenticationAuditQuery.md) - [generateRestRoutesAuthentication](functions/generateRestRoutesAuthentication.md) - [authenticationLogin](functions/authenticationLogin.md) - [authenticationLogout](functions/authenticationLogout.md) diff --git a/packages/api-auth-entity-storage-service/locales/en.json b/packages/api-auth-entity-storage-service/locales/en.json index eceae72..c1e1ed1 100644 --- a/packages/api-auth-entity-storage-service/locales/en.json +++ b/packages/api-auth-entity-storage-service/locales/en.json @@ -21,16 +21,6 @@ "passwordHelper": { "currentPasswordMismatch": "The current password is incorrect" }, - "entityStorageAuthenticationAuditService": { - "auditEntryNotFound": "The audit entry with the specified identifier could not be found \"{notFoundId}\"", - "getAuditEntryFailed": "Getting the audit entry failed", - "updateAuditEntryFailed": "Updating the audit entry failed", - "removeAuditEntryFailed": "Removing the audit entry failed" - }, - "entityStorageAuthenticationRateService": { - "actionConfigMissing": "No rate-limit configuration exists for action \"{action}\".", - "rateLimitExceeded": "The rate limit for action \"{action}\" has been exceeded. Retry after {retryAfterSeconds} seconds." - }, "tokenHelper": { "missing": "The JSON Web token could not be found in the authorization header", "headerKeyIdMismatch": "The JSON Web token key identifier does not match the expected signing key", @@ -41,9 +31,6 @@ "userNotVerified": "The user associated with the JSON Web token could not be verified", "organizationNotVerified": "The organization associated with the JSON Web token could not be verified", "tenantNotVerified": "The tenant associated with the JSON Web token could not be verified" - }, - "validation": { - "saltEntropyTooLow": "Salt must have at least 8 unique characters for sufficient entropy." } } } diff --git a/packages/api-auth-entity-storage-service/package.json b/packages/api-auth-entity-storage-service/package.json index 2acaad6..a94ee15 100644 --- a/packages/api-auth-entity-storage-service/package.json +++ b/packages/api-auth-entity-storage-service/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.2-next.9", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", diff --git a/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts b/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts index 37ddbe3..bdf8007 100644 --- a/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts +++ b/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts @@ -38,10 +38,10 @@ export class AuthenticationUser { public organization!: string; /** - * The scope assigned to the user, comma separated. + * The roles assigned to the user, bar separated. */ @property({ type: "string" }) - public scope!: string; + public roles!: string; /** * The password version counter, incremented on every password change to invalidate existing tokens. diff --git a/packages/api-auth-entity-storage-service/src/index.ts b/packages/api-auth-entity-storage-service/src/index.ts index 99186ef..9c701b5 100644 --- a/packages/api-auth-entity-storage-service/src/index.ts +++ b/packages/api-auth-entity-storage-service/src/index.ts @@ -1,27 +1,18 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. -export * from "./entities/authenticationAuditEntry.js"; -export * from "./entities/authenticationRateEntry.js"; export * from "./entities/authenticationUser.js"; export * from "./models/IAuthHeaderProcessorConfig.js"; export * from "./models/IAuthHeaderProcessorConstructorOptions.js"; export * from "./models/IEntityStorageAuthenticationAdminServiceConfig.js"; export * from "./models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js"; -export * from "./models/IEntityStorageAuthenticationAuditServiceConfig.js"; -export * from "./models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js"; -export * from "./models/IEntityStorageAuthenticationRateServiceConfig.js"; -export * from "./models/IEntityStorageAuthenticationRateServiceConstructorOptions.js"; export * from "./models/IEntityStorageAuthenticationServiceConfig.js"; export * from "./models/IEntityStorageAuthenticationServiceConstructorOptions.js"; export * from "./processors/authHeaderProcessor.js"; export * from "./restEntryPoints.js"; export * from "./routes/entityStorageAuthenticationAdminRoutes.js"; -export * from "./routes/entityStorageAuthenticationAuditRoutes.js"; export * from "./routes/entityStorageAuthenticationRoutes.js"; export * from "./schema.js"; export * from "./services/entityStorageAuthenticationAdminService.js"; -export * from "./services/entityStorageAuthenticationAuditService.js"; -export * from "./services/entityStorageAuthenticationRateService.js"; export * from "./services/entityStorageAuthenticationService.js"; export * from "./utils/tokenHelper.js"; export * from "./utils/passwordHelper.js"; diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts index 251e1ee..a462175 100644 --- a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts +++ b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts @@ -1,6 +1,6 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. -import type { IAuthenticationRateActionConfig } from "@twin.org/api-auth-entity-storage-models"; +import type { IAuthenticationRateActionConfig } from "@twin.org/api-auth-models"; /** * Configuration for the entity storage authentication service. diff --git a/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts b/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts index f470a5d..d1768f0 100644 --- a/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts +++ b/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts @@ -213,6 +213,7 @@ export class AuthHeaderProcessor implements IBaseRouteProcessor { contextIds[ContextIdKeys.Organization] = tenantOrganizationId ?? this._nodeOrganizationId; contextIds[ContextIdKeys.User] = user?.identity; contextIds[ContextIdKeys.UserOrganization] = user?.organization; + contextIds[HttpContextIdKeys.Roles] = user?.roles; // If the tenant has a custom public origin, we set it in the context for downstream processors to use. if (Is.stringValue(tenantPublicOrigin)) { diff --git a/packages/api-auth-entity-storage-service/src/restEntryPoints.ts b/packages/api-auth-entity-storage-service/src/restEntryPoints.ts index 9bc0360..280e54f 100644 --- a/packages/api-auth-entity-storage-service/src/restEntryPoints.ts +++ b/packages/api-auth-entity-storage-service/src/restEntryPoints.ts @@ -5,10 +5,6 @@ import { generateRestRoutesAuthenticationAdmin, tagsAuthenticationAdmin } from "./routes/entityStorageAuthenticationAdminRoutes.js"; -import { - generateRestRoutesAuthenticationAudit, - tagsAuthenticationAudit -} from "./routes/entityStorageAuthenticationAuditRoutes.js"; import { generateRestRoutesAuthentication, tagsAuthentication @@ -29,11 +25,5 @@ export const restEntryPoints: IRestRouteEntryPoint[] = [ defaultBaseRoute: "authentication/admin", tags: tagsAuthenticationAdmin, generateRoutes: generateRestRoutesAuthenticationAdmin - }, - { - name: "authenticationAudit", - defaultBaseRoute: "authentication/audit", - tags: tagsAuthenticationAudit, - generateRoutes: generateRestRoutesAuthenticationAudit } ]; diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts index 8816eb9..22de5c3 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts @@ -31,6 +31,15 @@ import { HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; */ const ROUTES_SOURCE = "authenticationAdminRoutes"; +/** + * The default authorization roles for the routes, use to populate authorization rules. + */ +const PERMISSIONS_DEFAULT_ROUTE_READER = "user-admin:read"; +const PERMISSIONS_DEFAULT_ROUTE_WRITER = { + permission: "user-admin:write", + inherits: [PERMISSIONS_DEFAULT_ROUTE_READER] +}; + /** * The tag to associate with the routes. */ @@ -71,7 +80,7 @@ export function generateRestRoutesAuthenticationAdmin( password: "MyPassword123!", userIdentity: "did:example:123456789abcdefghi", organizationIdentity: "did:example:123456789abcdefghi", - scope: ["scope1", "scope2"] + roles: ["role1", "role2"] } } } @@ -84,7 +93,8 @@ export function generateRestRoutesAuthenticationAdmin( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; const updateUserRoute: IRestRoute = { @@ -108,7 +118,7 @@ export function generateRestRoutesAuthenticationAdmin( body: { userIdentity: "did:example:123456789abcdefghi", organizationIdentity: "did:example:123456789abcdefghi", - scope: ["scope1", "scope2"] + roles: ["role1", "role2"] } } } @@ -121,7 +131,8 @@ export function generateRestRoutesAuthenticationAdmin( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; const updateUserPasswordRoute: IRestRoute = { @@ -156,7 +167,8 @@ export function generateRestRoutesAuthenticationAdmin( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; const getUserRoute: IRestRoute = { @@ -193,7 +205,7 @@ export function generateRestRoutesAuthenticationAdmin( email: "user@example.com", userIdentity: "did:example:123456789abcdefghi", organizationIdentity: "did:example:123456789abcdefghi", - scope: ["scope1", "scope2"] + roles: ["role1", "role2"] } } } @@ -202,7 +214,8 @@ export function generateRestRoutesAuthenticationAdmin( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] }; const getByIdentityUserRoute: IRestRoute = @@ -240,7 +253,7 @@ export function generateRestRoutesAuthenticationAdmin( email: "user@example.com", userIdentity: "did:example:123456789abcdefghi", organizationIdentity: "did:example:123456789abcdefghi", - scope: ["scope1", "scope2"] + roles: ["role1", "role2"] } } } @@ -249,7 +262,8 @@ export function generateRestRoutesAuthenticationAdmin( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] }; const removeUserRoute: IRestRoute = { @@ -281,7 +295,8 @@ export function generateRestRoutesAuthenticationAdmin( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; return [ diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index 0ac00a7..ee00351 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -26,6 +26,15 @@ import { HttpStatusCode } from "@twin.org/web"; */ const ROUTES_SOURCE = "authenticationRoutes"; +/** + * The default authorization roles for the routes, use to populate authorization rules. + */ +const PERMISSIONS_DEFAULT_ROUTE_READER = "user:read"; +const PERMISSIONS_DEFAULT_ROUTE_WRITER = { + permission: "user:write", + inherits: [PERMISSIONS_DEFAULT_ROUTE_READER] +}; + /** * The tag to associate with the routes. */ @@ -88,7 +97,8 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - skipAuth: true + skipAuth: true, + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] }; const logoutRoute: IRestRoute = { @@ -117,7 +127,8 @@ export function generateRestRoutesAuthentication( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] }; const refreshTokenRoute: IRestRoute = { @@ -160,7 +171,8 @@ export function generateRestRoutesAuthentication( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] }; const updatePasswordRoute: IRestRoute = { @@ -193,7 +205,8 @@ export function generateRestRoutesAuthentication( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; return [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute]; diff --git a/packages/api-auth-entity-storage-service/src/schema.ts b/packages/api-auth-entity-storage-service/src/schema.ts index cd3cbb2..e87141c 100644 --- a/packages/api-auth-entity-storage-service/src/schema.ts +++ b/packages/api-auth-entity-storage-service/src/schema.ts @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0. import { EntitySchemaFactory, EntitySchemaHelper } from "@twin.org/entity"; import { nameof } from "@twin.org/nameof"; -import { AuthenticationAuditEntry } from "./entities/authenticationAuditEntry.js"; -import { AuthenticationRateEntry } from "./entities/authenticationRateEntry.js"; import { AuthenticationUser } from "./entities/authenticationUser.js"; /** @@ -13,10 +11,4 @@ export function initSchema(): void { EntitySchemaFactory.register(nameof(), () => EntitySchemaHelper.getSchema(AuthenticationUser) ); - EntitySchemaFactory.register(nameof(), () => - EntitySchemaHelper.getSchema(AuthenticationAuditEntry) - ); - EntitySchemaFactory.register(nameof(), () => - EntitySchemaHelper.getSchema(AuthenticationRateEntry) - ); } diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts index b5f1573..15b1c36 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts +++ b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0. import type { IAuthenticationAdminComponent, - IAuthenticationAuditComponent, IAuthenticationUser } from "@twin.org/api-auth-entity-storage-models"; -import { AuthAuditEvent } from "@twin.org/api-auth-entity-storage-models"; +import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-models"; +import { AuthAuditEvent } from "@twin.org/api-auth-models"; +import { RolesHelper } from "@twin.org/api-models"; import { ContextIdKeys, ContextIdStore } from "@twin.org/context"; import { ComponentFactory, @@ -110,8 +111,8 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA ); Guards.array( EntityStorageAuthenticationAdminService.CLASS_NAME, - nameof(user.scope), - user.scope + nameof(user.roles), + user.roles ); try { @@ -135,7 +136,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA password: hashedPassword, identity: user.userIdentity, organization: user.organizationIdentity, - scope: user.scope.map(s => s.trim().toLocaleLowerCase()).join(","), + roles: RolesHelper.toString(user.roles), passwordVersion: 0 }; @@ -150,7 +151,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA userIdentity: user.userIdentity, organizationIdentity: user.organizationIdentity, tenantId: requestorTenantId, - scope: user.scope + roles: user.roles } }); } catch (error) { @@ -194,11 +195,11 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA user.organizationIdentity ); } - if (!Is.empty(user.scope)) { + if (!Is.empty(user.roles)) { Guards.array( EntityStorageAuthenticationAdminService.CLASS_NAME, - nameof(user.scope), - user.scope + nameof(user.roles), + user.roles ); } @@ -213,9 +214,9 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA } const updatedFields: string[] = []; - const updatedScope = Is.array(user.scope) - ? user.scope.map(s => s.trim().toLocaleLowerCase()).join(",") - : existingUser.scope; + const updatedRoles = Is.array(user.roles) + ? RolesHelper.toString(user.roles) + : existingUser.roles; if (user.userIdentity !== undefined && user.userIdentity !== existingUser.identity) { updatedFields.push("userIdentity"); @@ -226,13 +227,13 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA ) { updatedFields.push("organizationIdentity"); } - if (Is.array(user.scope) && updatedScope !== existingUser.scope) { - updatedFields.push("scope"); + if (Is.array(user.roles) && updatedRoles !== existingUser.roles) { + updatedFields.push("roles"); } existingUser.identity = user.userIdentity ?? existingUser.identity; existingUser.organization = user.organizationIdentity ?? existingUser.organization; - existingUser.scope = Is.array(user.scope) ? updatedScope : existingUser.scope; + existingUser.roles = Is.array(user.roles) ? updatedRoles : existingUser.roles; await this._userEntityStorage.set(existingUser); @@ -246,7 +247,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA userIdentity: existingUser.identity, organizationIdentity: existingUser.organization, tenantId: requestorTenantId, - scope: existingUser.scope.split(",") + roles: RolesHelper.toArray(existingUser.roles) } }); } catch (error) { @@ -281,7 +282,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA email: user.email, userIdentity: user.identity, organizationIdentity: user.organization, - scope: user.scope.split(",") + roles: RolesHelper.toArray(user.roles) }; } catch (error) { throw new GeneralError( @@ -319,7 +320,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA email: user.email, userIdentity: user.identity, organizationIdentity: user.organization, - scope: user.scope.split(",") + roles: RolesHelper.toArray(user.roles) }; } catch (error) { throw new GeneralError( @@ -360,7 +361,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA userIdentity: user.identity, organizationIdentity: user.organization, tenantId: requestorTenantId, - scope: user.scope.split(",") + roles: RolesHelper.toArray(user.roles) } }); } catch (error) { diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts index 1424ab7..9bfd3a7 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts +++ b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts @@ -1,13 +1,13 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. +import type { IAuthenticationComponent } from "@twin.org/api-auth-entity-storage-models"; import type { IAuthenticationRateActionConfig, IAuthenticationRateComponent, - IAuthenticationAuditComponent, - IAuthenticationComponent -} from "@twin.org/api-auth-entity-storage-models"; -import { AuthAuditEvent } from "@twin.org/api-auth-entity-storage-models"; -import type { ITenantAdminComponent } from "@twin.org/api-models"; + IAuthenticationAuditComponent +} from "@twin.org/api-auth-models"; +import { AuthAuditEvent } from "@twin.org/api-auth-models"; +import { RolesHelper, type ITenantAdminComponent } from "@twin.org/api-models"; import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context"; import { Coerce, @@ -302,7 +302,6 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon user.organization, loginTenantId, this._defaultTtlMinutes, - user.scope, user.passwordVersion ?? 0 ); loginUser = user; @@ -329,7 +328,7 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon userIdentity: loginUser.identity, organizationIdentity: loginUser.organization, tenantId: loginTenantId, - scope: loginUser.scope.split(",") + roles: RolesHelper.toArray(loginUser.roles) } }); @@ -367,8 +366,8 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon throw new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, "nodeIdNotSet"); } - let refreshPasswordVersion: number | undefined; let tenantId: string | undefined; + let refreshUser: AuthenticationUser | undefined; // If the verify fails on the current token then it will throw an exception. const headerAndPayload = await TokenHelper.verify( @@ -401,8 +400,9 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon this._userEntityStorage.get(sub, "identity") ); - refreshPasswordVersion = user?.passwordVersion; - if (user?.identity === sub && (passwordVersion ?? 0) === (refreshPasswordVersion ?? 0)) { + refreshUser = user; + + if (user?.identity === sub && (passwordVersion ?? 0) === (user?.passwordVersion ?? 0)) { validParts.push("user"); } if (user?.organization === org) { @@ -416,7 +416,6 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon await this._authenticationRateService.check("token-refresh", refreshSub); const payloadOrg = Coerce.string(headerAndPayload.payload.org); - const payloadScope = Coerce.string(headerAndPayload.payload?.scope); const refreshTokenAndExpiry = await TokenHelper.createToken( this._vaultConnector, @@ -426,8 +425,7 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon payloadOrg, tenantId, this._defaultTtlMinutes, - payloadScope, - refreshPasswordVersion ?? 0 + refreshUser?.passwordVersion ?? 0 ); await this._authenticationAuditService?.create({ @@ -436,8 +434,8 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon data: { organizationIdentity: payloadOrg, tenantId, - scope: payloadScope?.split(",").filter(scope => scope.length > 0), - version: refreshPasswordVersion ?? 0 + roles: RolesHelper.toArray(refreshUser?.roles), + version: refreshUser?.passwordVersion ?? 0 } }); diff --git a/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts b/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts index 83b1dba..54e108c 100644 --- a/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts +++ b/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts @@ -1,7 +1,7 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. -import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-entity-storage-models"; -import { AuthAuditEvent } from "@twin.org/api-auth-entity-storage-models"; +import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-models"; +import { AuthAuditEvent } from "@twin.org/api-auth-models"; import { Converter, GeneralError, Is, RandomHelper } from "@twin.org/core"; import { PasswordGenerator, PasswordValidator } from "@twin.org/crypto"; import type { IEntityStorageConnector } from "@twin.org/entity-storage-models"; @@ -59,7 +59,7 @@ export class PasswordHelper { password: hashedPassword, identity: user.identity, organization: user.organization, - scope: user.scope, + roles: user.roles, passwordVersion: (user.passwordVersion ?? 0) + 1 }; diff --git a/packages/api-auth-entity-storage-service/src/utils/tokenHelper.ts b/packages/api-auth-entity-storage-service/src/utils/tokenHelper.ts index c05ca67..4b1cde6 100644 --- a/packages/api-auth-entity-storage-service/src/utils/tokenHelper.ts +++ b/packages/api-auth-entity-storage-service/src/utils/tokenHelper.ts @@ -31,7 +31,6 @@ export class TokenHelper { * @param organizationIdentity The organization for the token. * @param tenantId The tenant id for the token. * @param ttlMinutes The time to live for the token in minutes. - * @param scope The scopes for the token. * @param passwordVersion The user's current password version counter, embedded in the token so that a password change invalidates existing tokens. * @returns The new token and its expiry date. */ @@ -43,7 +42,6 @@ export class TokenHelper { organizationIdentity: string | undefined, tenantId: string | undefined, ttlMinutes: number, - scope?: string, passwordVersion?: number ): Promise<{ token: string; @@ -61,7 +59,6 @@ export class TokenHelper { org: organizationIdentity, tid: tenantId, exp: nowSeconds + ttlSeconds, - scope, pver: passwordVersion }, async (header, payload) => diff --git a/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts b/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts index f060d12..fa78c12 100644 --- a/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts @@ -183,89 +183,6 @@ describe("TokenHelper", () => { }); }); - describe("verify with scopes", () => { - const mockVaultConnector: IVaultConnector = { - get: vi.fn(), - set: vi.fn(), - remove: vi.fn() - } as unknown as IVaultConnector; - - const nodeId = "test-node"; - const signingKeyName = "test-key"; - - it("should verify token with matching required scopes", async () => { - const payload = { - iss: nodeId, - sub: "user123", - org: "org123", - exp: Math.trunc(Date.now() / 1000) + 3600, - scope: "read,write,admin" - }; - - const token = "mock.jwt.token"; - vi.spyOn(Jwt, "verifyWithVerifier").mockResolvedValue({ - header: { alg: "EdDSA", kid: signingKeyName }, - payload - }); - - const result = await TokenHelper.verify(mockVaultConnector, nodeId, signingKeyName, token); - - expect(result.payload).toEqual(payload); - }); - - it("should verify token when no scopes are required", async () => { - const payload = { - iss: nodeId, - sub: "user123", - org: "org123", - exp: Math.trunc(Date.now() / 1000) + 3600, - scope: "read,write" - }; - - const token = "mock.jwt.token"; - vi.spyOn(Jwt, "verifyWithVerifier").mockResolvedValue({ - header: { alg: "EdDSA", kid: signingKeyName }, - payload - }); - - const result = await TokenHelper.verify(mockVaultConnector, nodeId, signingKeyName, token); - - expect(result.payload).toEqual(payload); - }); - - it("should verify token when required scopes is empty array", async () => { - const payload = { - iss: nodeId, - sub: "user123", - org: "org123", - exp: Math.trunc(Date.now() / 1000) + 3600, - scope: "read" - }; - - const token = "mock.jwt.token"; - vi.spyOn(Jwt, "verifyWithVerifier").mockResolvedValue({ - header: { alg: "EdDSA", kid: signingKeyName }, - payload - }); - - const result = await TokenHelper.verify(mockVaultConnector, nodeId, signingKeyName, token); - - expect(result.payload).toEqual(payload); - }); - - it("should throw UnauthorizedError for missing token", async () => { - await expect( - TokenHelper.verify(mockVaultConnector, nodeId, signingKeyName, undefined) - ).rejects.toThrow(UnauthorizedError); - }); - - it("should throw UnauthorizedError for empty token", async () => { - await expect( - TokenHelper.verify(mockVaultConnector, nodeId, signingKeyName, "") - ).rejects.toThrow(UnauthorizedError); - }); - }); - describe("verify token validation", () => { const mockVaultConnector: IVaultConnector = { get: vi.fn(), diff --git a/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts b/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts index e73f717..f4b2a18 100644 --- a/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts @@ -57,7 +57,7 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - scope: "user-admin", + roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -111,7 +111,7 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - scope: "user-admin", + roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -210,7 +210,7 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - scope: "user-admin", + roles: "user-admin", passwordVersion: 1 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -258,7 +258,7 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - scope: "user-admin" + roles: "user-admin" }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ token: "jwt", @@ -299,7 +299,7 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - scope: "user-admin", + roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -354,7 +354,7 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - scope: "user-admin", + roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -399,7 +399,7 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - scope: "user-admin", + roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -452,7 +452,7 @@ describe("AuthHeaderProcessor", () => { organization: USER_ORG, password: "hashed", salt: "salt", - scope: "user-admin", + roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ diff --git a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts index a96b0c9..2460d14 100644 --- a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts @@ -1,6 +1,6 @@ // Copyright 2026 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. -import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-entity-storage-models"; +import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-models"; import { ContextIdKeys, ContextIdStore } from "@twin.org/context"; import { ComponentFactory, GeneralError, RandomHelper } from "@twin.org/core"; import { PasswordGenerator, PasswordValidator } from "@twin.org/crypto"; @@ -58,7 +58,7 @@ describe("EntityStorageAuthenticationAdminService", () => { expect(service.className()).toBe(EntityStorageAuthenticationAdminService.CLASS_NAME); }); - it("should create a new user with normalised scopes", async () => { + it("should create a new user with normalised roles", async () => { vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); vi.spyOn(RandomHelper, "generate").mockReturnValue(new Uint8Array([1, 2, 3, 4])); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("hashed-password"); @@ -68,7 +68,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "correct-horse-battery", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: [" Read ", "WRITE"] + roles: [" Read ", "WRITE"] }); expect(PasswordValidator.validatePassword).toHaveBeenCalledWith("correct-horse-battery", { @@ -80,7 +80,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "hashed-password", identity: "did:user:123", organization: "did:org:456", - scope: "read,write", + roles: "read,write", passwordVersion: 0 }); expect(mockAuthenticationAuditService.create).toHaveBeenCalledWith({ @@ -89,7 +89,7 @@ describe("EntityStorageAuthenticationAdminService", () => { data: { userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: [" Read ", "WRITE"] + roles: [" Read ", "WRITE"] } }); }); @@ -101,7 +101,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); @@ -111,7 +111,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "correct-horse-battery", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read"] + roles: ["read"] }) ).rejects.toThrow(GeneralError); expect(await userEntityStorage.get("user@example.com")).toMatchObject({ @@ -130,26 +130,26 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "short", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read"] + roles: ["read"] }) ).rejects.toThrow(GeneralError); expect(await userEntityStorage.get("user@example.com")).toBeUndefined(); }); - it("should update an existing user and normalise scopes", async () => { + it("should update an existing user and normalise roles", async () => { await userEntityStorage.set({ email: "user@example.com", password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); await service.update({ email: "user@example.com", organizationIdentity: "did:org:999", - scope: [" Admin ", "WRITE"] + roles: [" Admin ", "WRITE"] }); expect(await userEntityStorage.get("user@example.com")).toEqual({ @@ -158,16 +158,16 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:999", - scope: "admin,write" + roles: "admin,write" }); expect(mockAuthenticationAuditService.create).toHaveBeenCalledWith({ actorId: "user@example.com", event: "account-updated", data: { - updatedFields: ["organizationIdentity", "scope"], + updatedFields: ["organizationIdentity", "roles"], userIdentity: "did:user:123", organizationIdentity: "did:org:999", - scope: ["admin", "write"] + roles: ["admin", "write"] } }); }); @@ -189,7 +189,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); const result = await service.get("user@example.com"); @@ -198,7 +198,7 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read", "write"] + roles: ["read", "write"] }); }); @@ -209,7 +209,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); const result = await service.getByIdentity("did:user:123"); @@ -218,7 +218,7 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read", "write"] + roles: ["read", "write"] }); }); @@ -237,7 +237,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); await service.remove("user@example.com"); @@ -249,7 +249,7 @@ describe("EntityStorageAuthenticationAdminService", () => { data: { userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read"] + roles: ["read"] } }); }); @@ -268,7 +268,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); vi.spyOn(PasswordValidator, "comparePasswordHashes").mockReturnValue(true); @@ -285,7 +285,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "new-password-hash", identity: "did:user:123", organization: "did:org:456", - scope: "read,write", + roles: "read,write", passwordVersion: 1 }); expect(mockAuthenticationAuditService.create).toHaveBeenCalledWith({ @@ -305,7 +305,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); const comparePasswordHashesSpy = vi.spyOn(PasswordValidator, "comparePasswordHashes"); @@ -321,7 +321,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "new-password-hash", identity: "did:user:123", organization: "did:org:456", - scope: "read,write", + roles: "read,write", passwordVersion: 1 }); }); @@ -333,7 +333,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("current-password-hash"); @@ -397,7 +397,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "correct-horse-battery", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read"] + roles: ["read"] }); expect(await userEntityStorage.get("user@example.com")).toEqual({ @@ -406,7 +406,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "hashed-password", identity: "did:user:123", organization: "did:org:456", - scope: "read", + roles: "read", passwordVersion: 0 }); }); @@ -420,7 +420,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); const result = await service.get("user@example.com"); @@ -429,7 +429,7 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read", "write"] + roles: ["read", "write"] }); }); }); @@ -442,7 +442,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); const result = await service.getByIdentity("did:user:123"); @@ -451,7 +451,7 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", userIdentity: "did:user:123", organizationIdentity: "did:org:456", - scope: ["read", "write"] + roles: ["read", "write"] }); }); }); @@ -464,18 +464,18 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); await service.update({ email: "user@example.com", organizationIdentity: "did:org:999", - scope: ["admin"] + roles: ["admin"] }); expect(await userEntityStorage.get("user@example.com")).toMatchObject({ organization: "did:org:999", - scope: "admin" + roles: "admin" }); }); }); @@ -488,7 +488,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); await service.remove("user@example.com"); @@ -509,7 +509,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read,write" + roles: "read,write" }); await service.updatePassword("user@example.com", "better-password-value"); @@ -533,7 +533,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "correct-horse-battery", userIdentity: "did:user:tenant-a", organizationIdentity: "did:org:456", - scope: ["read"] + roles: ["read"] }); }); @@ -543,18 +543,18 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "correct-horse-battery", userIdentity: "did:user:tenant-b", organizationIdentity: "did:org:456", - scope: ["write"] + roles: ["write"] }); }); await ContextIdStore.run({ [ContextIdKeys.Tenant]: TENANT_A }, async () => { const userA = await service.get("user@example.com"); - expect(userA).toMatchObject({ userIdentity: "did:user:tenant-a", scope: ["read"] }); + expect(userA).toMatchObject({ userIdentity: "did:user:tenant-a", roles: ["read"] }); }); await ContextIdStore.run({ [ContextIdKeys.Tenant]: TENANT_B }, async () => { const userB = await service.get("user@example.com"); - expect(userB).toMatchObject({ userIdentity: "did:user:tenant-b", scope: ["write"] }); + expect(userB).toMatchObject({ userIdentity: "did:user:tenant-b", roles: ["write"] }); }); }); @@ -566,7 +566,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); }); @@ -583,7 +583,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); }); @@ -600,7 +600,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); }); @@ -625,7 +625,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); }); @@ -648,7 +648,7 @@ describe("EntityStorageAuthenticationAdminService", () => { salt: "AQIDBA==", identity: "did:user:123", organization: "did:org:456", - scope: "read" + roles: "read" }); }); diff --git a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts index de053e1..2348344 100644 --- a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts @@ -3,7 +3,7 @@ import type { IAuthenticationAuditComponent, IAuthenticationRateComponent -} from "@twin.org/api-auth-entity-storage-models"; +} from "@twin.org/api-auth-models"; import { TooManyRequestsError } from "@twin.org/api-models"; import { ContextIdKeys, ContextIdStore } from "@twin.org/context"; import { ComponentFactory, NotFoundError, UnauthorizedError } from "@twin.org/core"; @@ -99,7 +99,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read,write" + roles: "read,write" }); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("generated-password-hash"); vi.spyOn(PasswordValidator, "comparePasswordHashes").mockReturnValue(true); @@ -121,7 +121,6 @@ describe("EntityStorageAuthenticationService", () => { "did:org:456", "tenant-1", 60, - "read,write", 0 ); expect(mockAuthenticationRateService.clear).toHaveBeenCalledWith("login", "user@example.com"); @@ -130,9 +129,9 @@ describe("EntityStorageAuthenticationService", () => { event: "login-success", data: { userIdentity: "did:user:123", + roles: ["read", "write"], organizationIdentity: "did:org:456", - tenantId: "tenant-1", - scope: ["read", "write"] + tenantId: "tenant-1" } }); }); @@ -146,8 +145,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - scope: "read" + salt: "c2FsdA==" }); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("generated-password-hash"); vi.spyOn(PasswordValidator, "comparePasswordHashes").mockReturnValue(false); @@ -187,8 +185,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - scope: "read" + salt: "c2FsdA==" }); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("generated-password-hash"); vi.spyOn(PasswordValidator, "comparePasswordHashes").mockReturnValue(true); @@ -268,8 +265,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - scope: "read" + salt: "c2FsdA==" }); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("generated-password-hash"); vi.spyOn(PasswordValidator, "comparePasswordHashes").mockReturnValue(false); @@ -305,7 +301,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read,write" + roles: "read,write" }); vi.spyOn(TokenHelper, "verify").mockImplementation( async (vaultConnector, nodeId, signingKeyName, token, verifyUser) => { @@ -315,8 +311,7 @@ describe("EntityStorageAuthenticationService", () => { header: { alg: "EdDSA" }, payload: { sub: "did:user:123", - org: "did:org:456", - scope: "read,write" + org: "did:org:456" } }; } @@ -339,7 +334,6 @@ describe("EntityStorageAuthenticationService", () => { "did:org:456", undefined, 60, - "read,write", 0 ); expect(mockAuthenticationRateService.check).toHaveBeenCalledWith( @@ -353,7 +347,7 @@ describe("EntityStorageAuthenticationService", () => { data: { organizationIdentity: "did:org:456", tenantId: undefined, - scope: ["read", "write"], + roles: ["read", "write"], version: 0 } }); @@ -371,8 +365,7 @@ describe("EntityStorageAuthenticationService", () => { payload: { sub: "did:user:123", org: "did:org:456", - tid: "tenant-1", - scope: "read,write" + tid: "tenant-1" } }; } @@ -419,7 +412,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read,write" + roles: "read,write" }); const createTokenSpy = vi.spyOn(TokenHelper, "createToken"); vi.spyOn(TokenHelper, "verify").mockImplementation( @@ -456,7 +449,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:other", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read,write" + roles: "read,write" }); const createTokenSpy = vi.spyOn(TokenHelper, "createToken"); vi.spyOn(TokenHelper, "verify").mockImplementation( @@ -493,8 +486,8 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read,write", - passwordVersion: 2 + passwordVersion: 2, + roles: "read,write" }); const createTokenSpy = vi.spyOn(TokenHelper, "createToken"); vi.spyOn(TokenHelper, "verify").mockImplementation( @@ -528,7 +521,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read" + roles: "read,write" }); vi.spyOn(PasswordHelper, "updatePassword").mockResolvedValue(undefined); @@ -635,7 +628,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read,write" + roles: "read,write" }); const result = await service.login("user@example.com", "correct-password"); @@ -652,7 +645,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read" + roles: "read" }); }); @@ -680,7 +673,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read" + roles: "read" }); }); @@ -691,7 +684,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "write" + roles: "write" }); }); @@ -727,7 +720,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read" + roles: "read" }); await service.updatePassword("current-password", "new-password"); @@ -752,7 +745,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read" + roles: "read" }); }); @@ -774,7 +767,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - scope: "read" + roles: "read" }); }); @@ -786,7 +779,7 @@ describe("EntityStorageAuthenticationService", () => { } return { header: { alg: "EdDSA" }, - payload: { sub: "did:user:123", org: "did:org:456", tid: TENANT_B, scope: "read" } + payload: { sub: "did:user:123", org: "did:org:456", tid: TENANT_B } }; } ); @@ -809,8 +802,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - scope: "read,write" + salt: "c2FsdA==" }); vi.spyOn(TokenHelper, "verify").mockImplementation( async (vaultConnector, nodeId, signingKeyName, token, verifyUser) => { @@ -823,8 +815,7 @@ describe("EntityStorageAuthenticationService", () => { payload: { sub: "did:user:123", org: "did:org:456", - tid: "tenant-a", - scope: "read,write" + tid: "tenant-a" } }; } @@ -844,8 +835,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - scope: "read,write" + salt: "c2FsdA==" }); // Provide a tenantAdminComponent that can't find the tenant. @@ -875,8 +865,7 @@ describe("EntityStorageAuthenticationService", () => { payload: { sub: "did:user:123", org: "did:org:456", - tid: "tenant-a", - scope: "read,write" + tid: "tenant-a" } }; } @@ -897,8 +886,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - scope: "read,write" + salt: "c2FsdA==" }); const mockTenantAdminComponent = { @@ -925,8 +913,7 @@ describe("EntityStorageAuthenticationService", () => { payload: { sub: "did:user:123", org: "did:org:456", - tid: "tenant-a", - scope: "read,write" + tid: "tenant-a" } }; } @@ -949,7 +936,6 @@ describe("EntityStorageAuthenticationService", () => { "did:org:456", "tenant-a", 60, - "read,write", 0 ); }); diff --git a/packages/api-auth-models/LICENSE b/packages/api-auth-models/LICENSE new file mode 100644 index 0000000..677c34c --- /dev/null +++ b/packages/api-auth-models/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 IOTA Stiftung. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/packages/api-auth-models/README.md b/packages/api-auth-models/README.md new file mode 100644 index 0000000..ec7f5b3 --- /dev/null +++ b/packages/api-auth-models/README.md @@ -0,0 +1,21 @@ +# TWIN API Auth Models + +This package provides contracts for authentication audit and rate limiting. + +## Installation + +```shell +npm install @twin.org/api-auth-models +``` + +## Examples + +Usage of the APIs is shown in the examples [docs/examples.md](docs/examples.md) + +## Reference + +Detailed reference documentation for the API can be found in [docs/reference/index.md](docs/reference/index.md) + +## Changelog + +The changes between each version can be found in [docs/changelog.md](docs/changelog.md) diff --git a/packages/api-auth-models/docs/changelog.md b/packages/api-auth-models/docs/changelog.md new file mode 100644 index 0000000..825c32f --- /dev/null +++ b/packages/api-auth-models/docs/changelog.md @@ -0,0 +1 @@ +# Changelog diff --git a/packages/api-auth-models/docs/examples.md b/packages/api-auth-models/docs/examples.md new file mode 100644 index 0000000..f2ab084 --- /dev/null +++ b/packages/api-auth-models/docs/examples.md @@ -0,0 +1 @@ +# Auth Models Examples diff --git a/packages/api-auth-models/docs/reference/index.md b/packages/api-auth-models/docs/reference/index.md new file mode 100644 index 0000000..4c42231 --- /dev/null +++ b/packages/api-auth-models/docs/reference/index.md @@ -0,0 +1,23 @@ +# @twin.org/api-auth-models + +## Interfaces + +- [IAuthenticationAuditComponent](interfaces/IAuthenticationAuditComponent.md) +- [IAuthenticationAuditEntry](interfaces/IAuthenticationAuditEntry.md) +- [IAuthenticationRateActionConfig](interfaces/IAuthenticationRateActionConfig.md) +- [IAuthenticationRateComponent](interfaces/IAuthenticationRateComponent.md) +- [IAuditCreateRequest](interfaces/IAuditCreateRequest.md) +- [IAuditGetRequest](interfaces/IAuditGetRequest.md) +- [IAuditGetResponse](interfaces/IAuditGetResponse.md) +- [IAuditQueryRequest](interfaces/IAuditQueryRequest.md) +- [IAuditQueryResponse](interfaces/IAuditQueryResponse.md) +- [IAuditRemoveRequest](interfaces/IAuditRemoveRequest.md) +- [IAuditUpdateRequest](interfaces/IAuditUpdateRequest.md) + +## Type Aliases + +- [AuthAuditEvent](type-aliases/AuthAuditEvent.md) + +## Variables + +- [AuthAuditEvent](variables/AuthAuditEvent.md) diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditCreateRequest.md b/packages/api-auth-models/docs/reference/interfaces/IAuditCreateRequest.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditCreateRequest.md rename to packages/api-auth-models/docs/reference/interfaces/IAuditCreateRequest.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditGetRequest.md b/packages/api-auth-models/docs/reference/interfaces/IAuditGetRequest.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditGetRequest.md rename to packages/api-auth-models/docs/reference/interfaces/IAuditGetRequest.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditGetResponse.md b/packages/api-auth-models/docs/reference/interfaces/IAuditGetResponse.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditGetResponse.md rename to packages/api-auth-models/docs/reference/interfaces/IAuditGetResponse.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditQueryRequest.md b/packages/api-auth-models/docs/reference/interfaces/IAuditQueryRequest.md similarity index 95% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditQueryRequest.md rename to packages/api-auth-models/docs/reference/interfaces/IAuditQueryRequest.md index 310edbf..8bc5ed9 100644 --- a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditQueryRequest.md +++ b/packages/api-auth-models/docs/reference/interfaces/IAuditQueryRequest.md @@ -20,7 +20,7 @@ The actor identifier to filter by. > `optional` **organizationId?**: `string` -The organization identifier to filter by. +The organisation identifier to filter by. #### tenantId? diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditQueryResponse.md b/packages/api-auth-models/docs/reference/interfaces/IAuditQueryResponse.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditQueryResponse.md rename to packages/api-auth-models/docs/reference/interfaces/IAuditQueryResponse.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditRemoveRequest.md b/packages/api-auth-models/docs/reference/interfaces/IAuditRemoveRequest.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditRemoveRequest.md rename to packages/api-auth-models/docs/reference/interfaces/IAuditRemoveRequest.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditUpdateRequest.md b/packages/api-auth-models/docs/reference/interfaces/IAuditUpdateRequest.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuditUpdateRequest.md rename to packages/api-auth-models/docs/reference/interfaces/IAuditUpdateRequest.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationAuditComponent.md b/packages/api-auth-models/docs/reference/interfaces/IAuthenticationAuditComponent.md similarity index 97% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationAuditComponent.md rename to packages/api-auth-models/docs/reference/interfaces/IAuthenticationAuditComponent.md index 080aacb..15c55fb 100644 --- a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationAuditComponent.md +++ b/packages/api-auth-models/docs/reference/interfaces/IAuthenticationAuditComponent.md @@ -124,7 +124,7 @@ The actor identifier to filter the audit entries, optional. `string` -The organization identifier to filter the audit entries, optional. +The organisation identifier to filter the audit entries, optional. ###### tenantId? diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationAuditEntry.md b/packages/api-auth-models/docs/reference/interfaces/IAuthenticationAuditEntry.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationAuditEntry.md rename to packages/api-auth-models/docs/reference/interfaces/IAuthenticationAuditEntry.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationRateActionConfig.md b/packages/api-auth-models/docs/reference/interfaces/IAuthenticationRateActionConfig.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationRateActionConfig.md rename to packages/api-auth-models/docs/reference/interfaces/IAuthenticationRateActionConfig.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationRateComponent.md b/packages/api-auth-models/docs/reference/interfaces/IAuthenticationRateComponent.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationRateComponent.md rename to packages/api-auth-models/docs/reference/interfaces/IAuthenticationRateComponent.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/type-aliases/AuthAuditEvent.md b/packages/api-auth-models/docs/reference/type-aliases/AuthAuditEvent.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/type-aliases/AuthAuditEvent.md rename to packages/api-auth-models/docs/reference/type-aliases/AuthAuditEvent.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/variables/AuthAuditEvent.md b/packages/api-auth-models/docs/reference/variables/AuthAuditEvent.md similarity index 100% rename from packages/api-auth-entity-storage-models/docs/reference/variables/AuthAuditEvent.md rename to packages/api-auth-models/docs/reference/variables/AuthAuditEvent.md diff --git a/packages/api-auth-models/locales/en.json b/packages/api-auth-models/locales/en.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/packages/api-auth-models/locales/en.json @@ -0,0 +1 @@ +{} diff --git a/packages/api-auth-models/package.json b/packages/api-auth-models/package.json new file mode 100644 index 0000000..a3efb8f --- /dev/null +++ b/packages/api-auth-models/package.json @@ -0,0 +1,82 @@ +{ + "name": "@twin.org/api-auth-models", + "version": "0.9.2-next.9", + "description": "Contracts for authentication audit and rate limiting.", + "repository": { + "type": "git", + "url": "git+https://github.com/iotaledger/twin-api-auth.git", + "directory": "packages/api-auth-models" + }, + "author": "martyn.janes@iota.org", + "license": "Apache-2.0", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "scripts": { + "clean": "rimraf dist coverage", + "build": "tspc", + "dev": "nodemon --watch src --ext ts --exec \"npm run build\"", + "test": "vitest --run --config ./vitest.config.ts --no-cache", + "test:build": "tspc -p ./tests/tsconfig.json --noEmit", + "validate-locales": "validate-locales", + "test:coverage": "vitest --run --coverage --config ./vitest.config.ts --no-cache", + "docs:clean": "rimraf docs/reference", + "docs:generate": "typedoc --options typedoc.config.mjs", + "docs": "npm run docs:clean && npm run docs:generate", + "dist": "npm run clean && npm run build && npm run validate-locales && npm run test:build && npm run test", + "dist:no-test": "npm run clean && npm run build && npm run test:build" + }, + "dependencies": { + "@twin.org/core": "next", + "@twin.org/nameof": "next" + }, + "devDependencies": { + "@twin.org/nameof-transformer": "next", + "@twin.org/nameof-vitest-plugin": "next", + "@twin.org/validate-locales": "next", + "@types/node": "26.1.2", + "@vitest/coverage-v8": "4.1.10", + "copyfiles": "2.4.1", + "nodemon": "3.1.14", + "rimraf": "6.1.3", + "ts-patch": "4.0.1", + "typedoc": "0.28.20", + "typedoc-plugin-markdown": "4.12.0", + "typescript": "6.0.3", + "vitest": "4.1.10" + }, + "main": "./dist/es/index.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/es/index.js", + "default": "./dist/es/index.js" + } + }, + "files": [ + "dist/es", + "dist/types", + "locales", + "docs" + ], + "keywords": [ + "twin", + "trade", + "iota", + "framework", + "blockchain", + "api", + "auth", + "audit", + "rate", + "models", + "types", + "schemas" + ], + "bugs": { + "url": "git+https://github.com/iotaledger/twin-api-auth/issues" + }, + "homepage": "https://twindev.org" +} diff --git a/packages/api-auth-models/src/index.ts b/packages/api-auth-models/src/index.ts new file mode 100644 index 0000000..208bef1 --- /dev/null +++ b/packages/api-auth-models/src/index.ts @@ -0,0 +1,14 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +export * from "./models/api/IAuditCreateRequest.js"; +export * from "./models/api/IAuditGetRequest.js"; +export * from "./models/api/IAuditGetResponse.js"; +export * from "./models/api/IAuditQueryRequest.js"; +export * from "./models/api/IAuditQueryResponse.js"; +export * from "./models/api/IAuditRemoveRequest.js"; +export * from "./models/api/IAuditUpdateRequest.js"; +export * from "./models/authAuditEvent.js"; +export * from "./models/IAuthenticationAuditComponent.js"; +export * from "./models/IAuthenticationAuditEntry.js"; +export * from "./models/IAuthenticationRateActionConfig.js"; +export * from "./models/IAuthenticationRateComponent.js"; diff --git a/packages/api-auth-entity-storage-models/src/models/IAuthenticationAuditComponent.ts b/packages/api-auth-models/src/models/IAuthenticationAuditComponent.ts similarity index 97% rename from packages/api-auth-entity-storage-models/src/models/IAuthenticationAuditComponent.ts rename to packages/api-auth-models/src/models/IAuthenticationAuditComponent.ts index 11ea12f..878c2f0 100644 --- a/packages/api-auth-entity-storage-models/src/models/IAuthenticationAuditComponent.ts +++ b/packages/api-auth-models/src/models/IAuthenticationAuditComponent.ts @@ -44,7 +44,7 @@ export interface IAuthenticationAuditComponent extends IComponent { * Query the audit entries. * @param options The query options. * @param options.actorId The actor identifier to filter the audit entries, optional. - * @param options.organizationId The organization identifier to filter the audit entries, optional. + * @param options.organizationId The organisation identifier to filter the audit entries, optional. * @param options.tenantId The tenant identifier to filter the audit entries, optional. * @param options.nodeId The node identifier to filter the audit entries, optional. * @param options.event The audit event to filter the audit entries, optional. diff --git a/packages/api-auth-entity-storage-models/src/models/IAuthenticationAuditEntry.ts b/packages/api-auth-models/src/models/IAuthenticationAuditEntry.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/IAuthenticationAuditEntry.ts rename to packages/api-auth-models/src/models/IAuthenticationAuditEntry.ts diff --git a/packages/api-auth-entity-storage-models/src/models/IAuthenticationRateActionConfig.ts b/packages/api-auth-models/src/models/IAuthenticationRateActionConfig.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/IAuthenticationRateActionConfig.ts rename to packages/api-auth-models/src/models/IAuthenticationRateActionConfig.ts diff --git a/packages/api-auth-entity-storage-models/src/models/IAuthenticationRateComponent.ts b/packages/api-auth-models/src/models/IAuthenticationRateComponent.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/IAuthenticationRateComponent.ts rename to packages/api-auth-models/src/models/IAuthenticationRateComponent.ts diff --git a/packages/api-auth-entity-storage-models/src/models/api/IAuditCreateRequest.ts b/packages/api-auth-models/src/models/api/IAuditCreateRequest.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/api/IAuditCreateRequest.ts rename to packages/api-auth-models/src/models/api/IAuditCreateRequest.ts diff --git a/packages/api-auth-entity-storage-models/src/models/api/IAuditGetRequest.ts b/packages/api-auth-models/src/models/api/IAuditGetRequest.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/api/IAuditGetRequest.ts rename to packages/api-auth-models/src/models/api/IAuditGetRequest.ts diff --git a/packages/api-auth-entity-storage-models/src/models/api/IAuditGetResponse.ts b/packages/api-auth-models/src/models/api/IAuditGetResponse.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/api/IAuditGetResponse.ts rename to packages/api-auth-models/src/models/api/IAuditGetResponse.ts diff --git a/packages/api-auth-entity-storage-models/src/models/api/IAuditQueryRequest.ts b/packages/api-auth-models/src/models/api/IAuditQueryRequest.ts similarity index 95% rename from packages/api-auth-entity-storage-models/src/models/api/IAuditQueryRequest.ts rename to packages/api-auth-models/src/models/api/IAuditQueryRequest.ts index faacff0..2b2490c 100644 --- a/packages/api-auth-entity-storage-models/src/models/api/IAuditQueryRequest.ts +++ b/packages/api-auth-models/src/models/api/IAuditQueryRequest.ts @@ -16,7 +16,7 @@ export interface IAuditQueryRequest { actorId?: string; /** - * The organization identifier to filter by. + * The organisation identifier to filter by. */ organizationId?: string; diff --git a/packages/api-auth-entity-storage-models/src/models/api/IAuditQueryResponse.ts b/packages/api-auth-models/src/models/api/IAuditQueryResponse.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/api/IAuditQueryResponse.ts rename to packages/api-auth-models/src/models/api/IAuditQueryResponse.ts diff --git a/packages/api-auth-entity-storage-models/src/models/api/IAuditRemoveRequest.ts b/packages/api-auth-models/src/models/api/IAuditRemoveRequest.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/api/IAuditRemoveRequest.ts rename to packages/api-auth-models/src/models/api/IAuditRemoveRequest.ts diff --git a/packages/api-auth-entity-storage-models/src/models/api/IAuditUpdateRequest.ts b/packages/api-auth-models/src/models/api/IAuditUpdateRequest.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/api/IAuditUpdateRequest.ts rename to packages/api-auth-models/src/models/api/IAuditUpdateRequest.ts diff --git a/packages/api-auth-entity-storage-models/src/models/authAuditEvent.ts b/packages/api-auth-models/src/models/authAuditEvent.ts similarity index 100% rename from packages/api-auth-entity-storage-models/src/models/authAuditEvent.ts rename to packages/api-auth-models/src/models/authAuditEvent.ts diff --git a/packages/api-auth-models/tests/index.spec.ts b/packages/api-auth-models/tests/index.spec.ts new file mode 100644 index 0000000..64daa0f --- /dev/null +++ b/packages/api-auth-models/tests/index.spec.ts @@ -0,0 +1,6 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. + +describe("api-auth-models", () => { + test("This package currently has no tests", () => {}); +}); diff --git a/packages/api-auth-models/tests/tsconfig.json b/packages/api-auth-models/tests/tsconfig.json new file mode 100644 index 0000000..44d8c8f --- /dev/null +++ b/packages/api-auth-models/tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "rootDir": "..", + "types": ["vitest/globals"] + }, + "include": ["./**/*.ts"] +} diff --git a/packages/api-auth-models/tsconfig.json b/packages/api-auth-models/tsconfig.json new file mode 100644 index 0000000..a6d6759 --- /dev/null +++ b/packages/api-auth-models/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext", + "target": "ES2022", + "rootDir": "./src", + "outDir": "dist/es", + "strict": true, + "forceConsistentCasingInFileNames": true, + "stripInternal": true, + "noEmitHelpers": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "erasableSyntaxOnly": true, + "sourceMap": true, + "inlineSources": true, + "declaration": true, + "declarationDir": "dist/types", + "types": ["node"], + "plugins": [{ "transform": "@twin.org/nameof-transformer" }], + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/api-auth-models/typedoc.config.mjs b/packages/api-auth-models/typedoc.config.mjs new file mode 100644 index 0000000..aad5b59 --- /dev/null +++ b/packages/api-auth-models/typedoc.config.mjs @@ -0,0 +1,24 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { OptionDefaults } from 'typedoc'; + +const customSchemaTags = ['@json-ld', '@json-schema']; + +export default { + entryPoints: ['src/index.ts'], + out: './docs/reference', + hideGenerator: true, + hideBreadcrumbs: true, + hidePageHeader: true, + excludeExternals: true, + githubPages: false, + disableSources: true, + sort: ['source-order'], + treatWarningsAsErrors: true, + readme: 'none', + entryFileName: 'index', + plugin: ['typedoc-plugin-markdown'], + blockTags: [...new Set([...OptionDefaults.blockTags, ...customSchemaTags])], + excludeTags: customSchemaTags, + useCustomAnchors: true +}; diff --git a/packages/api-auth-models/vitest.config.ts b/packages/api-auth-models/vitest.config.ts new file mode 100644 index 0000000..648b91d --- /dev/null +++ b/packages/api-auth-models/vitest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2025 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { NameOfPlugin } from "@twin.org/nameof-vitest-plugin"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [NameOfPlugin], + test: { + include: ["./tests/**/*.spec.ts"], + globals: true, + testTimeout: 300000, + hookTimeout: 300000, + bail: 1, + reporters: ["verbose"], + disableConsoleIntercept: true, + coverage: { + reporter: ["text", "lcov"], + include: ["src/**/*.ts"], + exclude: ["**/index.ts", "**/models/**/*.ts"] + }, + fileParallelism: false + } +}); diff --git a/packages/api-auth-rest-client/LICENSE b/packages/api-auth-rest-client/LICENSE new file mode 100644 index 0000000..677c34c --- /dev/null +++ b/packages/api-auth-rest-client/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 IOTA Stiftung. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/packages/api-auth-rest-client/README.md b/packages/api-auth-rest-client/README.md new file mode 100644 index 0000000..a545369 --- /dev/null +++ b/packages/api-auth-rest-client/README.md @@ -0,0 +1,21 @@ +# TWIN API Auth Entity Storage REST Client + +This package provides REST clients for authentication and admin operations against entity storage endpoints. + +## Installation + +```shell +npm install @twin.org/api-auth-entity-storage-rest-client +``` + +## Examples + +Usage of the APIs is shown in the examples [docs/examples.md](docs/examples.md) + +## Reference + +Detailed reference documentation for the API can be found in [docs/reference/index.md](docs/reference/index.md) + +## Changelog + +The changes between each version can be found in [docs/changelog.md](docs/changelog.md) diff --git a/packages/api-auth-rest-client/docs/changelog.md b/packages/api-auth-rest-client/docs/changelog.md new file mode 100644 index 0000000..8ea399e --- /dev/null +++ b/packages/api-auth-rest-client/docs/changelog.md @@ -0,0 +1,1553 @@ +# Changelog + +## [0.9.2-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.2-next.6...api-auth-entity-storage-rest-client-v0.9.2-next.7) (2026-08-12) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.6 to 0.9.2-next.7 + * @twin.org/api-core bumped from 0.9.2-next.6 to 0.9.2-next.7 + * @twin.org/api-models bumped from 0.9.2-next.6 to 0.9.2-next.7 + +## [0.9.2-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.2-next.5...api-auth-entity-storage-rest-client-v0.9.2-next.6) (2026-08-07) + + +### Features + +* health updates ([#243](https://github.com/iotaledger/twin-api/issues/243)) ([8360945](https://github.com/iotaledger/twin-api/commit/836094547839f63f2309d4bcd8c4d19128c73883)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.5 to 0.9.2-next.6 + * @twin.org/api-core bumped from 0.9.2-next.5 to 0.9.2-next.6 + * @twin.org/api-models bumped from 0.9.2-next.5 to 0.9.2-next.6 + +## [0.9.2-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.2-next.4...api-auth-entity-storage-rest-client-v0.9.2-next.5) (2026-08-03) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.4 to 0.9.2-next.5 + * @twin.org/api-core bumped from 0.9.2-next.4 to 0.9.2-next.5 + * @twin.org/api-models bumped from 0.9.2-next.4 to 0.9.2-next.5 + +## [0.9.2-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.2-next.3...api-auth-entity-storage-rest-client-v0.9.2-next.4) (2026-08-02) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.3 to 0.9.2-next.4 + * @twin.org/api-core bumped from 0.9.2-next.3 to 0.9.2-next.4 + * @twin.org/api-models bumped from 0.9.2-next.3 to 0.9.2-next.4 + +## [0.9.2-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.2-next.2...api-auth-entity-storage-rest-client-v0.9.2-next.3) (2026-07-30) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.2 to 0.9.2-next.3 + * @twin.org/api-core bumped from 0.9.2-next.2 to 0.9.2-next.3 + * @twin.org/api-models bumped from 0.9.2-next.2 to 0.9.2-next.3 + +## [0.9.2-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.2-next.1...api-auth-entity-storage-rest-client-v0.9.2-next.2) (2026-07-29) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.1 to 0.9.2-next.2 + * @twin.org/api-core bumped from 0.9.2-next.1 to 0.9.2-next.2 + * @twin.org/api-models bumped from 0.9.2-next.1 to 0.9.2-next.2 + +## [0.9.2-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.2-next.0...api-auth-entity-storage-rest-client-v0.9.2-next.1) (2026-07-28) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) +* enhanced rest handling ([#208](https://github.com/iotaledger/twin-api/issues/208)) ([99d5f3f](https://github.com/iotaledger/twin-api/commit/99d5f3f96d262e57828d98d3f3b1e3da8a863378)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* improved rest handling ([#211](https://github.com/iotaledger/twin-api/issues/211)) ([12c50ea](https://github.com/iotaledger/twin-api/commit/12c50ea7655276eed7a73d311ac5286476c98201)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* remove authentication generators ([#66](https://github.com/iotaledger/twin-api/issues/66)) ([adaa169](https://github.com/iotaledger/twin-api/commit/adaa1698df1c5ccb0ad645a7a7c0d3ef82ef6ac1)) +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) +* use correct format for log messaging ([44bc2a4](https://github.com/iotaledger/twin-api/commit/44bc2a4f7cf1f9c38a7e8c6f90ccb2424c958de9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.0 to 0.9.2-next.1 + * @twin.org/api-core bumped from 0.9.2-next.0 to 0.9.2-next.1 + * @twin.org/api-models bumped from 0.9.2-next.0 to 0.9.2-next.1 + +## [0.9.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1...api-auth-entity-storage-rest-client-v0.9.1) (2026-07-27) + + +### Features + +* release to production ([70ee2d5](https://github.com/iotaledger/twin-api/commit/70ee2d56a1dc9537d7c9c154d4cb78a235678a3a)) +* release to production ([#195](https://github.com/iotaledger/twin-api/issues/195)) ([a3f5c1f](https://github.com/iotaledger/twin-api/commit/a3f5c1fc35a748762af7efa4f7f95776004d1309)) +* release to production ([#197](https://github.com/iotaledger/twin-api/issues/197)) ([f04c156](https://github.com/iotaledger/twin-api/commit/f04c1567f801cde36c5ec8595f9b9369109d9e42)) +* release to production ([#201](https://github.com/iotaledger/twin-api/issues/201)) ([e1c46fd](https://github.com/iotaledger/twin-api/commit/e1c46fd02c1f4d44d5393e2f49a24f1e4468f240)) +* release to production ([#224](https://github.com/iotaledger/twin-api/issues/224)) ([dffaf08](https://github.com/iotaledger/twin-api/commit/dffaf082b7dccc6f57a5b7cd20b95d24bb8ec2f3)) + +## [0.9.1-next.9](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.8...api-auth-entity-storage-rest-client-v0.9.1-next.9) (2026-07-23) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.8 to 0.9.1-next.9 + * @twin.org/api-core bumped from 0.9.1-next.8 to 0.9.1-next.9 + * @twin.org/api-models bumped from 0.9.1-next.8 to 0.9.1-next.9 + +## [0.9.1-next.8](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.7...api-auth-entity-storage-rest-client-v0.9.1-next.8) (2026-07-03) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.7 to 0.9.1-next.8 + * @twin.org/api-core bumped from 0.9.1-next.7 to 0.9.1-next.8 + * @twin.org/api-models bumped from 0.9.1-next.7 to 0.9.1-next.8 + +## [0.9.1-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.6...api-auth-entity-storage-rest-client-v0.9.1-next.7) (2026-07-02) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.6 to 0.9.1-next.7 + * @twin.org/api-core bumped from 0.9.1-next.6 to 0.9.1-next.7 + * @twin.org/api-models bumped from 0.9.1-next.6 to 0.9.1-next.7 + +## [0.9.1-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.5...api-auth-entity-storage-rest-client-v0.9.1-next.6) (2026-06-30) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.5 to 0.9.1-next.6 + * @twin.org/api-core bumped from 0.9.1-next.5 to 0.9.1-next.6 + * @twin.org/api-models bumped from 0.9.1-next.5 to 0.9.1-next.6 + +## [0.9.1-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.4...api-auth-entity-storage-rest-client-v0.9.1-next.5) (2026-06-30) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.4 to 0.9.1-next.5 + * @twin.org/api-core bumped from 0.9.1-next.4 to 0.9.1-next.5 + * @twin.org/api-models bumped from 0.9.1-next.4 to 0.9.1-next.5 + +## [0.9.1-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.3...api-auth-entity-storage-rest-client-v0.9.1-next.4) (2026-06-30) + + +### Features + +* improved rest handling ([#211](https://github.com/iotaledger/twin-api/issues/211)) ([12c50ea](https://github.com/iotaledger/twin-api/commit/12c50ea7655276eed7a73d311ac5286476c98201)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.3 to 0.9.1-next.4 + * @twin.org/api-core bumped from 0.9.1-next.3 to 0.9.1-next.4 + * @twin.org/api-models bumped from 0.9.1-next.3 to 0.9.1-next.4 + +## [0.9.1-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.2...api-auth-entity-storage-rest-client-v0.9.1-next.3) (2026-06-29) + + +### Features + +* enhanced rest handling ([#208](https://github.com/iotaledger/twin-api/issues/208)) ([99d5f3f](https://github.com/iotaledger/twin-api/commit/99d5f3f96d262e57828d98d3f3b1e3da8a863378)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.2 to 0.9.1-next.3 + * @twin.org/api-core bumped from 0.9.1-next.2 to 0.9.1-next.3 + * @twin.org/api-models bumped from 0.9.1-next.2 to 0.9.1-next.3 + +## [0.9.1-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.1...api-auth-entity-storage-rest-client-v0.9.1-next.2) (2026-06-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.1 to 0.9.1-next.2 + * @twin.org/api-core bumped from 0.9.1-next.1 to 0.9.1-next.2 + * @twin.org/api-models bumped from 0.9.1-next.1 to 0.9.1-next.2 + +## [0.9.1-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.1-next.0...api-auth-entity-storage-rest-client-v0.9.1-next.1) (2026-06-26) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* remove authentication generators ([#66](https://github.com/iotaledger/twin-api/issues/66)) ([adaa169](https://github.com/iotaledger/twin-api/commit/adaa1698df1c5ccb0ad645a7a7c0d3ef82ef6ac1)) +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) +* use correct format for log messaging ([44bc2a4](https://github.com/iotaledger/twin-api/commit/44bc2a4f7cf1f9c38a7e8c6f90ccb2424c958de9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.0 to 0.9.1-next.1 + * @twin.org/api-core bumped from 0.9.1-next.0 to 0.9.1-next.1 + * @twin.org/api-models bumped from 0.9.1-next.0 to 0.9.1-next.1 + +## [0.9.0](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.0...api-auth-entity-storage-rest-client-v0.9.0) (2026-06-24) + + +### Features + +* release to production ([70ee2d5](https://github.com/iotaledger/twin-api/commit/70ee2d56a1dc9537d7c9c154d4cb78a235678a3a)) +* release to production ([#195](https://github.com/iotaledger/twin-api/issues/195)) ([a3f5c1f](https://github.com/iotaledger/twin-api/commit/a3f5c1fc35a748762af7efa4f7f95776004d1309)) +* release to production ([#197](https://github.com/iotaledger/twin-api/issues/197)) ([f04c156](https://github.com/iotaledger/twin-api/commit/f04c1567f801cde36c5ec8595f9b9369109d9e42)) +* release to production ([#201](https://github.com/iotaledger/twin-api/issues/201)) ([e1c46fd](https://github.com/iotaledger/twin-api/commit/e1c46fd02c1f4d44d5393e2f49a24f1e4468f240)) + +## [0.9.0-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.9.0-next.0...api-auth-entity-storage-rest-client-v0.9.0-next.1) (2026-06-23) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* remove authentication generators ([#66](https://github.com/iotaledger/twin-api/issues/66)) ([adaa169](https://github.com/iotaledger/twin-api/commit/adaa1698df1c5ccb0ad645a7a7c0d3ef82ef6ac1)) +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) +* use correct format for log messaging ([44bc2a4](https://github.com/iotaledger/twin-api/commit/44bc2a4f7cf1f9c38a7e8c6f90ccb2424c958de9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.0-next.0 to 0.9.0-next.1 + * @twin.org/api-core bumped from 0.9.0-next.0 to 0.9.0-next.1 + * @twin.org/api-models bumped from 0.9.0-next.0 to 0.9.0-next.1 + +## [0.0.3-next.53](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.52...api-auth-entity-storage-rest-client-v0.0.3-next.53) (2026-06-23) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.52 to 0.0.3-next.53 + * @twin.org/api-core bumped from 0.0.3-next.52 to 0.0.3-next.53 + * @twin.org/api-models bumped from 0.0.3-next.52 to 0.0.3-next.53 + +## [0.0.3-next.52](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.51...api-auth-entity-storage-rest-client-v0.0.3-next.52) (2026-06-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.51 to 0.0.3-next.52 + * @twin.org/api-core bumped from 0.0.3-next.51 to 0.0.3-next.52 + * @twin.org/api-models bumped from 0.0.3-next.51 to 0.0.3-next.52 + +## [0.0.3-next.51](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.50...api-auth-entity-storage-rest-client-v0.0.3-next.51) (2026-06-20) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.50 to 0.0.3-next.51 + * @twin.org/api-core bumped from 0.0.3-next.50 to 0.0.3-next.51 + * @twin.org/api-models bumped from 0.0.3-next.50 to 0.0.3-next.51 + +## [0.0.3-next.50](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.49...api-auth-entity-storage-rest-client-v0.0.3-next.50) (2026-06-19) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.49 to 0.0.3-next.50 + * @twin.org/api-core bumped from 0.0.3-next.49 to 0.0.3-next.50 + * @twin.org/api-models bumped from 0.0.3-next.49 to 0.0.3-next.50 + +## [0.0.3-next.49](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.48...api-auth-entity-storage-rest-client-v0.0.3-next.49) (2026-06-19) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.48 to 0.0.3-next.49 + * @twin.org/api-core bumped from 0.0.3-next.48 to 0.0.3-next.49 + * @twin.org/api-models bumped from 0.0.3-next.48 to 0.0.3-next.49 + +## [0.0.3-next.48](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.47...api-auth-entity-storage-rest-client-v0.0.3-next.48) (2026-06-19) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.47 to 0.0.3-next.48 + * @twin.org/api-core bumped from 0.0.3-next.47 to 0.0.3-next.48 + * @twin.org/api-models bumped from 0.0.3-next.47 to 0.0.3-next.48 + +## [0.0.3-next.47](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.46...api-auth-entity-storage-rest-client-v0.0.3-next.47) (2026-06-18) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.46 to 0.0.3-next.47 + * @twin.org/api-core bumped from 0.0.3-next.46 to 0.0.3-next.47 + * @twin.org/api-models bumped from 0.0.3-next.46 to 0.0.3-next.47 + +## [0.0.3-next.46](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.45...api-auth-entity-storage-rest-client-v0.0.3-next.46) (2026-06-17) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.45 to 0.0.3-next.46 + * @twin.org/api-core bumped from 0.0.3-next.45 to 0.0.3-next.46 + * @twin.org/api-models bumped from 0.0.3-next.45 to 0.0.3-next.46 + +## [0.0.3-next.45](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.44...api-auth-entity-storage-rest-client-v0.0.3-next.45) (2026-06-15) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.44 to 0.0.3-next.45 + * @twin.org/api-core bumped from 0.0.3-next.44 to 0.0.3-next.45 + * @twin.org/api-models bumped from 0.0.3-next.44 to 0.0.3-next.45 + +## [0.0.3-next.44](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.43...api-auth-entity-storage-rest-client-v0.0.3-next.44) (2026-06-11) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.43 to 0.0.3-next.44 + * @twin.org/api-core bumped from 0.0.3-next.43 to 0.0.3-next.44 + * @twin.org/api-models bumped from 0.0.3-next.43 to 0.0.3-next.44 + +## [0.0.3-next.43](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.42...api-auth-entity-storage-rest-client-v0.0.3-next.43) (2026-06-10) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.42 to 0.0.3-next.43 + * @twin.org/api-core bumped from 0.0.3-next.42 to 0.0.3-next.43 + * @twin.org/api-models bumped from 0.0.3-next.42 to 0.0.3-next.43 + +## [0.0.3-next.42](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.41...api-auth-entity-storage-rest-client-v0.0.3-next.42) (2026-06-08) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.41 to 0.0.3-next.42 + * @twin.org/api-core bumped from 0.0.3-next.41 to 0.0.3-next.42 + * @twin.org/api-models bumped from 0.0.3-next.41 to 0.0.3-next.42 + +## [0.0.3-next.41](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.40...api-auth-entity-storage-rest-client-v0.0.3-next.41) (2026-06-05) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.40 to 0.0.3-next.41 + * @twin.org/api-core bumped from 0.0.3-next.40 to 0.0.3-next.41 + * @twin.org/api-models bumped from 0.0.3-next.40 to 0.0.3-next.41 + +## [0.0.3-next.40](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.39...api-auth-entity-storage-rest-client-v0.0.3-next.40) (2026-06-04) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.39 to 0.0.3-next.40 + * @twin.org/api-core bumped from 0.0.3-next.39 to 0.0.3-next.40 + * @twin.org/api-models bumped from 0.0.3-next.39 to 0.0.3-next.40 + +## [0.0.3-next.39](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.38...api-auth-entity-storage-rest-client-v0.0.3-next.39) (2026-06-02) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.38 to 0.0.3-next.39 + * @twin.org/api-core bumped from 0.0.3-next.38 to 0.0.3-next.39 + * @twin.org/api-models bumped from 0.0.3-next.38 to 0.0.3-next.39 + +## [0.0.3-next.38](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.37...api-auth-entity-storage-rest-client-v0.0.3-next.38) (2026-05-28) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.37 to 0.0.3-next.38 + * @twin.org/api-core bumped from 0.0.3-next.37 to 0.0.3-next.38 + * @twin.org/api-models bumped from 0.0.3-next.37 to 0.0.3-next.38 + +## [0.0.3-next.37](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.36...api-auth-entity-storage-rest-client-v0.0.3-next.37) (2026-05-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.36 to 0.0.3-next.37 + * @twin.org/api-core bumped from 0.0.3-next.36 to 0.0.3-next.37 + * @twin.org/api-models bumped from 0.0.3-next.36 to 0.0.3-next.37 + +## [0.0.3-next.36](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.35...api-auth-entity-storage-rest-client-v0.0.3-next.36) (2026-05-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.35 to 0.0.3-next.36 + * @twin.org/api-core bumped from 0.0.3-next.35 to 0.0.3-next.36 + * @twin.org/api-models bumped from 0.0.3-next.35 to 0.0.3-next.36 + +## [0.0.3-next.35](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.34...api-auth-entity-storage-rest-client-v0.0.3-next.35) (2026-05-21) + + +### Features + +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.34 to 0.0.3-next.35 + * @twin.org/api-core bumped from 0.0.3-next.34 to 0.0.3-next.35 + * @twin.org/api-models bumped from 0.0.3-next.34 to 0.0.3-next.35 + +## [0.0.3-next.34](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.33...api-auth-entity-storage-rest-client-v0.0.3-next.34) (2026-05-19) + + +### Features + +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.33 to 0.0.3-next.34 + * @twin.org/api-core bumped from 0.0.3-next.33 to 0.0.3-next.34 + * @twin.org/api-models bumped from 0.0.3-next.33 to 0.0.3-next.34 + +## [0.0.3-next.33](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.32...api-auth-entity-storage-rest-client-v0.0.3-next.33) (2026-05-11) + + +### Features + +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.32 to 0.0.3-next.33 + * @twin.org/api-core bumped from 0.0.3-next.32 to 0.0.3-next.33 + * @twin.org/api-models bumped from 0.0.3-next.32 to 0.0.3-next.33 + +## [0.0.3-next.32](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.31...api-auth-entity-storage-rest-client-v0.0.3-next.32) (2026-05-07) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.31 to 0.0.3-next.32 + * @twin.org/api-core bumped from 0.0.3-next.31 to 0.0.3-next.32 + * @twin.org/api-models bumped from 0.0.3-next.31 to 0.0.3-next.32 + +## [0.0.3-next.31](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.30...api-auth-entity-storage-rest-client-v0.0.3-next.31) (2026-05-06) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.30 to 0.0.3-next.31 + * @twin.org/api-core bumped from 0.0.3-next.30 to 0.0.3-next.31 + * @twin.org/api-models bumped from 0.0.3-next.30 to 0.0.3-next.31 + +## [0.0.3-next.30](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.29...api-auth-entity-storage-rest-client-v0.0.3-next.30) (2026-05-05) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.29 to 0.0.3-next.30 + * @twin.org/api-core bumped from 0.0.3-next.29 to 0.0.3-next.30 + * @twin.org/api-models bumped from 0.0.3-next.29 to 0.0.3-next.30 + +## [0.0.3-next.29](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.28...api-auth-entity-storage-rest-client-v0.0.3-next.29) (2026-05-01) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.28 to 0.0.3-next.29 + * @twin.org/api-core bumped from 0.0.3-next.28 to 0.0.3-next.29 + * @twin.org/api-models bumped from 0.0.3-next.28 to 0.0.3-next.29 + +## [0.0.3-next.28](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.27...api-auth-entity-storage-rest-client-v0.0.3-next.28) (2026-04-30) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.27 to 0.0.3-next.28 + * @twin.org/api-core bumped from 0.0.3-next.27 to 0.0.3-next.28 + * @twin.org/api-models bumped from 0.0.3-next.27 to 0.0.3-next.28 + +## [0.0.3-next.27](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.26...api-auth-entity-storage-rest-client-v0.0.3-next.27) (2026-04-23) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.26 to 0.0.3-next.27 + * @twin.org/api-core bumped from 0.0.3-next.26 to 0.0.3-next.27 + * @twin.org/api-models bumped from 0.0.3-next.26 to 0.0.3-next.27 + +## [0.0.3-next.26](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.25...api-auth-entity-storage-rest-client-v0.0.3-next.26) (2026-04-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.25 to 0.0.3-next.26 + * @twin.org/api-core bumped from 0.0.3-next.25 to 0.0.3-next.26 + * @twin.org/api-models bumped from 0.0.3-next.25 to 0.0.3-next.26 + +## [0.0.3-next.25](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.24...api-auth-entity-storage-rest-client-v0.0.3-next.25) (2026-04-14) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.24 to 0.0.3-next.25 + * @twin.org/api-core bumped from 0.0.3-next.24 to 0.0.3-next.25 + * @twin.org/api-models bumped from 0.0.3-next.24 to 0.0.3-next.25 + +## [0.0.3-next.24](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.23...api-auth-entity-storage-rest-client-v0.0.3-next.24) (2026-04-14) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.23 to 0.0.3-next.24 + * @twin.org/api-core bumped from 0.0.3-next.23 to 0.0.3-next.24 + * @twin.org/api-models bumped from 0.0.3-next.23 to 0.0.3-next.24 + +## [0.0.3-next.23](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.22...api-auth-entity-storage-rest-client-v0.0.3-next.23) (2026-04-14) + + +### Features + +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.22 to 0.0.3-next.23 + * @twin.org/api-core bumped from 0.0.3-next.22 to 0.0.3-next.23 + * @twin.org/api-models bumped from 0.0.3-next.22 to 0.0.3-next.23 + +## [0.0.3-next.22](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.21...api-auth-entity-storage-rest-client-v0.0.3-next.22) (2026-03-27) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.21 to 0.0.3-next.22 + * @twin.org/api-core bumped from 0.0.3-next.21 to 0.0.3-next.22 + * @twin.org/api-models bumped from 0.0.3-next.21 to 0.0.3-next.22 + +## [0.0.3-next.21](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.20...api-auth-entity-storage-rest-client-v0.0.3-next.21) (2026-03-11) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.20 to 0.0.3-next.21 + * @twin.org/api-core bumped from 0.0.3-next.20 to 0.0.3-next.21 + * @twin.org/api-models bumped from 0.0.3-next.20 to 0.0.3-next.21 + +## [0.0.3-next.20](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.19...api-auth-entity-storage-rest-client-v0.0.3-next.20) (2026-02-09) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.19 to 0.0.3-next.20 + * @twin.org/api-core bumped from 0.0.3-next.19 to 0.0.3-next.20 + * @twin.org/api-models bumped from 0.0.3-next.19 to 0.0.3-next.20 + +## [0.0.3-next.19](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.18...api-auth-entity-storage-rest-client-v0.0.3-next.19) (2026-02-06) + + +### Features + +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.18 to 0.0.3-next.19 + * @twin.org/api-core bumped from 0.0.3-next.18 to 0.0.3-next.19 + * @twin.org/api-models bumped from 0.0.3-next.18 to 0.0.3-next.19 + +## [0.0.3-next.18](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.17...api-auth-entity-storage-rest-client-v0.0.3-next.18) (2026-02-04) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.17 to 0.0.3-next.18 + * @twin.org/api-core bumped from 0.0.3-next.17 to 0.0.3-next.18 + * @twin.org/api-models bumped from 0.0.3-next.17 to 0.0.3-next.18 + +## [0.0.3-next.17](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.16...api-auth-entity-storage-rest-client-v0.0.3-next.17) (2026-01-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.16 to 0.0.3-next.17 + * @twin.org/api-core bumped from 0.0.3-next.16 to 0.0.3-next.17 + * @twin.org/api-models bumped from 0.0.3-next.16 to 0.0.3-next.17 + +## [0.0.3-next.16](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.15...api-auth-entity-storage-rest-client-v0.0.3-next.16) (2026-01-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.15 to 0.0.3-next.16 + * @twin.org/api-core bumped from 0.0.3-next.15 to 0.0.3-next.16 + * @twin.org/api-models bumped from 0.0.3-next.15 to 0.0.3-next.16 + +## [0.0.3-next.15](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.14...api-auth-entity-storage-rest-client-v0.0.3-next.15) (2026-01-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.14 to 0.0.3-next.15 + * @twin.org/api-core bumped from 0.0.3-next.14 to 0.0.3-next.15 + * @twin.org/api-models bumped from 0.0.3-next.14 to 0.0.3-next.15 + +## [0.0.3-next.14](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.13...api-auth-entity-storage-rest-client-v0.0.3-next.14) (2026-01-20) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.13 to 0.0.3-next.14 + * @twin.org/api-core bumped from 0.0.3-next.13 to 0.0.3-next.14 + * @twin.org/api-models bumped from 0.0.3-next.13 to 0.0.3-next.14 + +## [0.0.3-next.13](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.12...api-auth-entity-storage-rest-client-v0.0.3-next.13) (2026-01-19) + + +### Features + +* remove authentication generators ([#66](https://github.com/iotaledger/twin-api/issues/66)) ([adaa169](https://github.com/iotaledger/twin-api/commit/adaa1698df1c5ccb0ad645a7a7c0d3ef82ef6ac1)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.12 to 0.0.3-next.13 + * @twin.org/api-core bumped from 0.0.3-next.12 to 0.0.3-next.13 + * @twin.org/api-models bumped from 0.0.3-next.12 to 0.0.3-next.13 + +## [0.0.3-next.12](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.11...api-auth-entity-storage-rest-client-v0.0.3-next.12) (2026-01-12) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.11 to 0.0.3-next.12 + * @twin.org/api-core bumped from 0.0.3-next.11 to 0.0.3-next.12 + * @twin.org/api-models bumped from 0.0.3-next.11 to 0.0.3-next.12 + +## [0.0.3-next.11](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.10...api-auth-entity-storage-rest-client-v0.0.3-next.11) (2026-01-08) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.10 to 0.0.3-next.11 + * @twin.org/api-core bumped from 0.0.3-next.10 to 0.0.3-next.11 + * @twin.org/api-models bumped from 0.0.3-next.10 to 0.0.3-next.11 + +## [0.0.3-next.10](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.9...api-auth-entity-storage-rest-client-v0.0.3-next.10) (2026-01-05) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.9 to 0.0.3-next.10 + * @twin.org/api-core bumped from 0.0.3-next.9 to 0.0.3-next.10 + * @twin.org/api-models bumped from 0.0.3-next.9 to 0.0.3-next.10 + +## [0.0.3-next.9](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.8...api-auth-entity-storage-rest-client-v0.0.3-next.9) (2026-01-05) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Bug Fixes + +* use correct format for log messaging ([44bc2a4](https://github.com/iotaledger/twin-api/commit/44bc2a4f7cf1f9c38a7e8c6f90ccb2424c958de9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.8 to 0.0.3-next.9 + * @twin.org/api-core bumped from 0.0.3-next.8 to 0.0.3-next.9 + * @twin.org/api-models bumped from 0.0.3-next.8 to 0.0.3-next.9 + +## [0.0.3-next.8](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.7...api-auth-entity-storage-rest-client-v0.0.3-next.8) (2025-12-17) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.7 to 0.0.3-next.8 + * @twin.org/api-core bumped from 0.0.3-next.7 to 0.0.3-next.8 + * @twin.org/api-models bumped from 0.0.3-next.7 to 0.0.3-next.8 + +## [0.0.3-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.6...api-auth-entity-storage-rest-client-v0.0.3-next.7) (2025-11-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.6 to 0.0.3-next.7 + * @twin.org/api-core bumped from 0.0.3-next.6 to 0.0.3-next.7 + * @twin.org/api-models bumped from 0.0.3-next.6 to 0.0.3-next.7 + +## [0.0.3-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.5...api-auth-entity-storage-rest-client-v0.0.3-next.6) (2025-11-20) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.5 to 0.0.3-next.6 + * @twin.org/api-core bumped from 0.0.3-next.5 to 0.0.3-next.6 + * @twin.org/api-models bumped from 0.0.3-next.5 to 0.0.3-next.6 + +## [0.0.3-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.4...api-auth-entity-storage-rest-client-v0.0.3-next.5) (2025-11-14) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.4 to 0.0.3-next.5 + * @twin.org/api-core bumped from 0.0.3-next.4 to 0.0.3-next.5 + * @twin.org/api-models bumped from 0.0.3-next.4 to 0.0.3-next.5 + +## [0.0.3-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.3...api-auth-entity-storage-rest-client-v0.0.3-next.4) (2025-11-14) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Bug Fixes + +* use correct format for log messaging ([44bc2a4](https://github.com/iotaledger/twin-api/commit/44bc2a4f7cf1f9c38a7e8c6f90ccb2424c958de9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.3 to 0.0.3-next.4 + * @twin.org/api-core bumped from 0.0.3-next.3 to 0.0.3-next.4 + * @twin.org/api-models bumped from 0.0.3-next.3 to 0.0.3-next.4 + +## [0.0.3-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.2...api-auth-entity-storage-rest-client-v0.0.3-next.3) (2025-11-14) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.2 to 0.0.3-next.3 + * @twin.org/api-core bumped from 0.0.3-next.2 to 0.0.3-next.3 + * @twin.org/api-models bumped from 0.0.3-next.2 to 0.0.3-next.3 + +## [0.0.3-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.1...api-auth-entity-storage-rest-client-v0.0.3-next.2) (2025-11-12) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.1 to 0.0.3-next.2 + * @twin.org/api-core bumped from 0.0.3-next.1 to 0.0.3-next.2 + * @twin.org/api-models bumped from 0.0.3-next.1 to 0.0.3-next.2 + +## [0.0.3-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.3-next.0...api-auth-entity-storage-rest-client-v0.0.3-next.1) (2025-11-10) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Bug Fixes + +* use correct format for log messaging ([44bc2a4](https://github.com/iotaledger/twin-api/commit/44bc2a4f7cf1f9c38a7e8c6f90ccb2424c958de9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.0 to 0.0.3-next.1 + * @twin.org/api-core bumped from 0.0.3-next.0 to 0.0.3-next.1 + * @twin.org/api-models bumped from 0.0.3-next.0 to 0.0.3-next.1 + +## [0.0.2-next.13](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.12...api-auth-entity-storage-rest-client-v0.0.2-next.13) (2025-10-09) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.12 to 0.0.2-next.13 + * @twin.org/api-core bumped from 0.0.2-next.12 to 0.0.2-next.13 + * @twin.org/api-models bumped from 0.0.2-next.12 to 0.0.2-next.13 + +## [0.0.2-next.12](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.11...api-auth-entity-storage-rest-client-v0.0.2-next.12) (2025-10-09) + + +### Features + +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.11 to 0.0.2-next.12 + * @twin.org/api-core bumped from 0.0.2-next.11 to 0.0.2-next.12 + * @twin.org/api-models bumped from 0.0.2-next.11 to 0.0.2-next.12 + +## [0.0.2-next.11](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.10...api-auth-entity-storage-rest-client-v0.0.2-next.11) (2025-09-29) + + +### Features + +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.10 to 0.0.2-next.11 + * @twin.org/api-core bumped from 0.0.2-next.10 to 0.0.2-next.11 + * @twin.org/api-models bumped from 0.0.2-next.10 to 0.0.2-next.11 + +## [0.0.2-next.10](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.9...api-auth-entity-storage-rest-client-v0.0.2-next.10) (2025-09-23) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.9 to 0.0.2-next.10 + * @twin.org/api-core bumped from 0.0.2-next.9 to 0.0.2-next.10 + * @twin.org/api-models bumped from 0.0.2-next.9 to 0.0.2-next.10 + +## [0.0.2-next.9](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.8...api-auth-entity-storage-rest-client-v0.0.2-next.9) (2025-08-29) + + +### Features + +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.8 to 0.0.2-next.9 + * @twin.org/api-core bumped from 0.0.2-next.8 to 0.0.2-next.9 + * @twin.org/api-models bumped from 0.0.2-next.8 to 0.0.2-next.9 + +## [0.0.2-next.8](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.7...api-auth-entity-storage-rest-client-v0.0.2-next.8) (2025-08-21) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.7 to 0.0.2-next.8 + * @twin.org/api-core bumped from 0.0.2-next.7 to 0.0.2-next.8 + * @twin.org/api-models bumped from 0.0.2-next.7 to 0.0.2-next.8 + +## [0.0.2-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.6...api-auth-entity-storage-rest-client-v0.0.2-next.7) (2025-08-20) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.6 to 0.0.2-next.7 + * @twin.org/api-core bumped from 0.0.2-next.6 to 0.0.2-next.7 + * @twin.org/api-models bumped from 0.0.2-next.6 to 0.0.2-next.7 + +## [0.0.2-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.5...api-auth-entity-storage-rest-client-v0.0.2-next.6) (2025-08-19) + + +### Features + +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.5 to 0.0.2-next.6 + * @twin.org/api-core bumped from 0.0.2-next.5 to 0.0.2-next.6 + * @twin.org/api-models bumped from 0.0.2-next.5 to 0.0.2-next.6 + +## [0.0.2-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.4...api-auth-entity-storage-rest-client-v0.0.2-next.5) (2025-07-25) + + +### Features + +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.4 to 0.0.2-next.5 + * @twin.org/api-core bumped from 0.0.2-next.4 to 0.0.2-next.5 + * @twin.org/api-models bumped from 0.0.2-next.4 to 0.0.2-next.5 + +## [0.0.2-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.3...api-auth-entity-storage-rest-client-v0.0.2-next.4) (2025-07-25) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.3 to 0.0.2-next.4 + * @twin.org/api-core bumped from 0.0.2-next.3 to 0.0.2-next.4 + * @twin.org/api-models bumped from 0.0.2-next.3 to 0.0.2-next.4 + +## [0.0.2-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.2...api-auth-entity-storage-rest-client-v0.0.2-next.3) (2025-07-24) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.2 to 0.0.2-next.3 + * @twin.org/api-core bumped from 0.0.2-next.2 to 0.0.2-next.3 + * @twin.org/api-models bumped from 0.0.2-next.2 to 0.0.2-next.3 + +## [0.0.2-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.1...api-auth-entity-storage-rest-client-v0.0.2-next.2) (2025-07-17) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.1 to 0.0.2-next.2 + * @twin.org/api-core bumped from 0.0.2-next.1 to 0.0.2-next.2 + * @twin.org/api-models bumped from 0.0.2-next.1 to 0.0.2-next.2 + +## [0.0.2-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.2-next.0...api-auth-entity-storage-rest-client-v0.0.2-next.1) (2025-07-08) + + +### Features + +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.0 to 0.0.2-next.1 + * @twin.org/api-core bumped from 0.0.2-next.0 to 0.0.2-next.1 + * @twin.org/api-models bumped from 0.0.2-next.0 to 0.0.2-next.1 + +## 0.0.1 (2025-07-03) + + +### Features + +* release to production ([70ee2d5](https://github.com/iotaledger/twin-api/commit/70ee2d56a1dc9537d7c9c154d4cb78a235678a3a)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from ^0.0.0 to ^0.0.1 + * @twin.org/api-core bumped from ^0.0.0 to ^0.0.1 + * @twin.org/api-models bumped from ^0.0.0 to ^0.0.1 + +## [0.0.1-next.36](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.1-next.35...api-auth-entity-storage-rest-client-v0.0.1-next.36) (2025-06-17) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.35 to 0.0.1-next.36 + * @twin.org/api-core bumped from 0.0.1-next.35 to 0.0.1-next.36 + * @twin.org/api-models bumped from 0.0.1-next.35 to 0.0.1-next.36 + +## [0.0.1-next.35](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.1-next.34...api-auth-entity-storage-rest-client-v0.0.1-next.35) (2025-06-11) + + +### Features + +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.34 to 0.0.1-next.35 + * @twin.org/api-core bumped from 0.0.1-next.34 to 0.0.1-next.35 + * @twin.org/api-models bumped from 0.0.1-next.34 to 0.0.1-next.35 + +## [0.0.1-next.34](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.1-next.33...api-auth-entity-storage-rest-client-v0.0.1-next.34) (2025-05-27) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.33 to 0.0.1-next.34 + * @twin.org/api-core bumped from 0.0.1-next.33 to 0.0.1-next.34 + * @twin.org/api-models bumped from 0.0.1-next.33 to 0.0.1-next.34 + +## [0.0.1-next.33](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.1-next.32...api-auth-entity-storage-rest-client-v0.0.1-next.33) (2025-04-17) + + +### Features + +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.32 to 0.0.1-next.33 + * @twin.org/api-core bumped from 0.0.1-next.32 to 0.0.1-next.33 + * @twin.org/api-models bumped from 0.0.1-next.32 to 0.0.1-next.33 + +## [0.0.1-next.32](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-rest-client-v0.0.1-next.31...api-auth-entity-storage-rest-client-v0.0.1-next.32) (2025-03-28) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-rest-client:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.31 to 0.0.1-next.32 + * @twin.org/api-core bumped from 0.0.1-next.31 to 0.0.1-next.32 + * @twin.org/api-models bumped from 0.0.1-next.31 to 0.0.1-next.32 + +## v0.0.1-next.31 + +- Initial Release diff --git a/packages/api-auth-rest-client/docs/examples.md b/packages/api-auth-rest-client/docs/examples.md new file mode 100644 index 0000000..9480fbb --- /dev/null +++ b/packages/api-auth-rest-client/docs/examples.md @@ -0,0 +1,111 @@ +# Auth Entity Storage Rest Client Examples + +Use these snippets to integrate sign-in and user administration flows from browser or server-side TypeScript applications. + +## EntityStorageAuthenticationAdminRestClient + +```typescript +import { EntityStorageAuthenticationAdminRestClient } from '@twin.org/api-auth-entity-storage-rest-client'; + +const adminClient = new EntityStorageAuthenticationAdminRestClient({ + endpoint: 'https://api.example.org', + pathPrefix: 'v1' +}); + +console.log(adminClient.className()); // EntityStorageAuthenticationAdminRestClient + +await adminClient.create({ + email: 'ops@example.org', + password: 'StrongPassword123', + userIdentity: 'did:example:ops', + organizationIdentity: 'did:example:core', + scope: ['admin'] +}); + +await adminClient.update({ + email: 'ops@example.org', + userIdentity: 'did:example:ops:team', + scope: ['admin', 'support'] +}); + +await adminClient.updatePassword('ops@example.org', 'StrongPassword124', 'StrongPassword123'); +const adminUser = await adminClient.get('ops@example.org'); +console.log(adminUser.email); // ops@example.org +``` + +```typescript +import { EntityStorageAuthenticationAdminRestClient } from '@twin.org/api-auth-entity-storage-rest-client'; + +const adminClient = new EntityStorageAuthenticationAdminRestClient({ + endpoint: 'https://api.example.org', + pathPrefix: 'v1' +}); + +const byEmail = await adminClient.get('ops@example.org'); +const byIdentity = await adminClient.getByIdentity('ops-team@example.org'); +await adminClient.remove('ops@example.org'); + +console.log(byEmail.email); // ops@example.org +console.log(byIdentity.userIdentity); // did:example:ops:team +``` + +## EntityStorageAuthenticationRestClient + +```typescript +import { EntityStorageAuthenticationRestClient } from '@twin.org/api-auth-entity-storage-rest-client'; + +const authClient = new EntityStorageAuthenticationRestClient({ + endpoint: 'https://api.example.org', + pathPrefix: 'v1', + cookieName: 'access_token' +}); + +console.log(authClient.className()); // EntityStorageAuthenticationRestClient + +const loginResponse = await authClient.login('alice@example.org', 'correct-horse-battery-staple'); + +await authClient.updatePassword('correct-horse-battery-staple', 'correct-horse-battery-staple-2'); + +const refreshResponse = await authClient.refresh(loginResponse.token); + +await authClient.logout(refreshResponse.token); +console.log(refreshResponse.expiry > 0); // true +``` + +## EntityStorageAuthenticationAuditRestClient + +```typescript +import { EntityStorageAuthenticationAuditRestClient } from '@twin.org/api-auth-entity-storage-rest-client'; + +const auditClient = new EntityStorageAuthenticationAuditRestClient({ + endpoint: 'https://api.example.org', + pathPrefix: 'v1' +}); + +console.log(auditClient.className()); // EntityStorageAuthenticationAuditRestClient + +const auditEntryId = await auditClient.create({ + event: 'login-success', + actorId: 'did:example:user:alice', + organizationId: 'did:example:org:core', + tenantId: 'did:example:tenant:alpha', + data: { + channel: 'web' + } +}); + +const auditResult = await auditClient.query( + { + actorId: 'did:example:user:alice', + event: 'login-success', + startDate: '2026-04-01T00:00:00.000Z', + endDate: '2026-04-30T23:59:59.999Z' + }, + undefined, + 25 +); + +console.log(auditEntryId); // /authentication/audit/018f2f67bb9d4a0caad8386f56df85ce +console.log(auditResult.entries.length); // 1 +console.log(auditResult.cursor); // eyJpZCI6IjAxOGYyZjY3YmI5ZDRhMGNhYWQ4Mzg2ZjU2ZGY4NWNlIn0= +``` diff --git a/packages/api-auth-entity-storage-rest-client/docs/reference/classes/EntityStorageAuthenticationAuditRestClient.md b/packages/api-auth-rest-client/docs/reference/classes/EntityStorageAuthenticationAuditRestClient.md similarity index 100% rename from packages/api-auth-entity-storage-rest-client/docs/reference/classes/EntityStorageAuthenticationAuditRestClient.md rename to packages/api-auth-rest-client/docs/reference/classes/EntityStorageAuthenticationAuditRestClient.md diff --git a/packages/api-auth-rest-client/docs/reference/index.md b/packages/api-auth-rest-client/docs/reference/index.md new file mode 100644 index 0000000..a1d1d94 --- /dev/null +++ b/packages/api-auth-rest-client/docs/reference/index.md @@ -0,0 +1,5 @@ +# @twin.org/api-auth-rest-client + +## Classes + +- [EntityStorageAuthenticationAuditRestClient](classes/EntityStorageAuthenticationAuditRestClient.md) diff --git a/packages/api-auth-rest-client/locales/en.json b/packages/api-auth-rest-client/locales/en.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/packages/api-auth-rest-client/locales/en.json @@ -0,0 +1 @@ +{} diff --git a/packages/api-auth-rest-client/package.json b/packages/api-auth-rest-client/package.json new file mode 100644 index 0000000..88cc27c --- /dev/null +++ b/packages/api-auth-rest-client/package.json @@ -0,0 +1,84 @@ +{ + "name": "@twin.org/api-auth-rest-client", + "version": "0.9.2-next.9", + "description": "REST clients for authentication and admin operations against entity storage endpoints.", + "repository": { + "type": "git", + "url": "git+https://github.com/iotaledger/twin-api-auth.git", + "directory": "packages/api-auth-rest-client" + }, + "author": "martyn.janes@iota.org", + "license": "Apache-2.0", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "scripts": { + "clean": "rimraf dist coverage", + "build": "tspc", + "dev": "nodemon --watch src --ext ts --exec \"npm run build\"", + "test": "vitest --run --config ./vitest.config.ts --no-cache", + "test:build": "tspc -p ./tests/tsconfig.json --noEmit", + "validate-locales": "validate-locales", + "test:coverage": "vitest --run --coverage --config ./vitest.config.ts --no-cache", + "docs:clean": "rimraf docs/reference", + "docs:generate": "typedoc --options typedoc.config.mjs", + "docs": "npm run docs:clean && npm run docs:generate", + "dist": "npm run clean && npm run build && npm run validate-locales && npm run test:build && npm run test", + "dist:no-test": "npm run clean && npm run build && npm run test:build" + }, + "dependencies": { + "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-core": "next", + "@twin.org/api-models": "next", + "@twin.org/core": "next", + "@twin.org/nameof": "next", + "@twin.org/web": "next" + }, + "devDependencies": { + "@twin.org/nameof-transformer": "next", + "@twin.org/nameof-vitest-plugin": "next", + "@twin.org/validate-locales": "next", + "@types/node": "26.1.2", + "@vitest/coverage-v8": "4.1.10", + "copyfiles": "2.4.1", + "nodemon": "3.1.14", + "rimraf": "6.1.3", + "ts-patch": "4.0.1", + "typedoc": "0.28.20", + "typedoc-plugin-markdown": "4.12.0", + "typescript": "6.0.3", + "vitest": "4.1.10" + }, + "main": "./dist/es/index.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/es/index.js", + "default": "./dist/es/index.js" + } + }, + "files": [ + "dist/es", + "dist/types", + "locales", + "docs" + ], + "keywords": [ + "twin", + "trade", + "iota", + "framework", + "blockchain", + "api", + "entity", + "storage", + "persistence", + "database" + ], + "bugs": { + "url": "git+https://github.com/iotaledger/twin-api-auth/issues" + }, + "homepage": "https://twindev.org" +} diff --git a/packages/api-auth-entity-storage-rest-client/src/entityStorageAuthenticationAuditRestClient.ts b/packages/api-auth-rest-client/src/entityStorageAuthenticationAuditRestClient.ts similarity index 99% rename from packages/api-auth-entity-storage-rest-client/src/entityStorageAuthenticationAuditRestClient.ts rename to packages/api-auth-rest-client/src/entityStorageAuthenticationAuditRestClient.ts index e0361f7..06f82c7 100644 --- a/packages/api-auth-entity-storage-rest-client/src/entityStorageAuthenticationAuditRestClient.ts +++ b/packages/api-auth-rest-client/src/entityStorageAuthenticationAuditRestClient.ts @@ -11,7 +11,7 @@ import type { IAuditUpdateRequest, IAuthenticationAuditComponent, IAuthenticationAuditEntry -} from "@twin.org/api-auth-entity-storage-models"; +} from "@twin.org/api-auth-models"; import { BaseRestClient } from "@twin.org/api-core"; import { HttpHeaderHelper } from "@twin.org/api-models"; import type { diff --git a/packages/api-auth-rest-client/src/index.ts b/packages/api-auth-rest-client/src/index.ts new file mode 100644 index 0000000..b8d3197 --- /dev/null +++ b/packages/api-auth-rest-client/src/index.ts @@ -0,0 +1,3 @@ +// Copyright 2024 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +export * from "./entityStorageAuthenticationAuditRestClient.js"; diff --git a/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAuditRestClient.spec.ts b/packages/api-auth-rest-client/tests/entityStorageAuthenticationAuditRestClient.spec.ts similarity index 100% rename from packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAuditRestClient.spec.ts rename to packages/api-auth-rest-client/tests/entityStorageAuthenticationAuditRestClient.spec.ts diff --git a/packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts b/packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts new file mode 100644 index 0000000..5bae838 --- /dev/null +++ b/packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts @@ -0,0 +1,93 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { HeaderTypes, HttpStatusCode, MimeTypes } from "@twin.org/web"; + +/** + * Minimal 204 No-Content response accepted by BaseRestClient. + * Use for operations that return void. + * @returns A fake 204 response object. + */ +export function noContentResponse(): object { + return { + ok: true, + status: HttpStatusCode.noContent, + headers: new Headers() + }; +} + +/** + * Minimal 200 OK JSON response accepted by BaseRestClient. + * @param jsonBody The value returned by response.json() - this becomes response.body in the client. + * @returns A fake 200 JSON response object. + */ +export function jsonResponse(jsonBody: unknown): object { + return { + ok: true, + status: HttpStatusCode.ok, + headers: new Headers({ [HeaderTypes.ContentType]: MimeTypes.Json }), + json: async () => jsonBody + }; +} + +/** + * Minimal 200 OK plain-text response accepted by BaseRestClient. + * @param text The value returned by response.text() - this becomes response.body in the client. + * @returns A fake 200 plain-text response object. + */ +export function textResponse(text: string): object { + return { + ok: true, + status: HttpStatusCode.ok, + headers: new Headers({ [HeaderTypes.ContentType]: MimeTypes.PlainText }), + text: async () => text + }; +} + +/** + * Minimal 200 OK binary response accepted by BaseRestClient. + * Body is stored as Uint8Array; an empty buffer causes body to be undefined. + * @param data The bytes to return via response.arrayBuffer(). + * @returns A fake 200 binary response object. + */ +export function binaryResponse(data: Uint8Array): object { + return { + ok: true, + status: HttpStatusCode.ok, + headers: new Headers(), + arrayBuffer: async () => data.buffer as ArrayBuffer + }; +} + +/** + * Minimal 201 Created response accepted by BaseRestClient. + * The location is surfaced as response.headers[HeaderTypes.Location] in the client. + * @param location The value to set in the Location header. + * @returns A fake 201 Created response object. + */ +export function createdResponse(location: string): object { + return { + ok: true, + status: HttpStatusCode.created, + headers: new Headers({ [HeaderTypes.Location]: location }), + arrayBuffer: async () => new ArrayBuffer(0) + }; +} + +/** + * Assigns the given mock as the global fetch implementation. + * Call in beforeEach alongside any client construction. + * @param mock The vi.fn() mock to install as globalThis.fetch. + */ +export function setupFetchMock(mock: object): void { + globalThis.fetch = mock as typeof fetch; +} + +/** + * Resets the mock and removes it as the global fetch. + * Call in afterEach to prevent state leaking between tests. + * @param mock The same mock passed to setupFetchMock. + * @param mock.mockReset Vitest method that clears call history and return values. + */ +export function teardownFetchMock(mock: { mockReset(): void }): void { + mock.mockReset(); +} diff --git a/packages/api-auth-rest-client/tests/index.spec.ts b/packages/api-auth-rest-client/tests/index.spec.ts new file mode 100644 index 0000000..e2d623a --- /dev/null +++ b/packages/api-auth-rest-client/tests/index.spec.ts @@ -0,0 +1,6 @@ +// Copyright 2024 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. + +describe("api-auth-entity-storage-rest-client", () => { + test("This package currently has no tests", () => {}); +}); diff --git a/packages/api-auth-rest-client/tests/tsconfig.json b/packages/api-auth-rest-client/tests/tsconfig.json new file mode 100644 index 0000000..44d8c8f --- /dev/null +++ b/packages/api-auth-rest-client/tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "rootDir": "..", + "types": ["vitest/globals"] + }, + "include": ["./**/*.ts"] +} diff --git a/packages/api-auth-rest-client/tsconfig.json b/packages/api-auth-rest-client/tsconfig.json new file mode 100644 index 0000000..a6d6759 --- /dev/null +++ b/packages/api-auth-rest-client/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext", + "target": "ES2022", + "rootDir": "./src", + "outDir": "dist/es", + "strict": true, + "forceConsistentCasingInFileNames": true, + "stripInternal": true, + "noEmitHelpers": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "erasableSyntaxOnly": true, + "sourceMap": true, + "inlineSources": true, + "declaration": true, + "declarationDir": "dist/types", + "types": ["node"], + "plugins": [{ "transform": "@twin.org/nameof-transformer" }], + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/api-auth-rest-client/typedoc.config.mjs b/packages/api-auth-rest-client/typedoc.config.mjs new file mode 100644 index 0000000..aad5b59 --- /dev/null +++ b/packages/api-auth-rest-client/typedoc.config.mjs @@ -0,0 +1,24 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { OptionDefaults } from 'typedoc'; + +const customSchemaTags = ['@json-ld', '@json-schema']; + +export default { + entryPoints: ['src/index.ts'], + out: './docs/reference', + hideGenerator: true, + hideBreadcrumbs: true, + hidePageHeader: true, + excludeExternals: true, + githubPages: false, + disableSources: true, + sort: ['source-order'], + treatWarningsAsErrors: true, + readme: 'none', + entryFileName: 'index', + plugin: ['typedoc-plugin-markdown'], + blockTags: [...new Set([...OptionDefaults.blockTags, ...customSchemaTags])], + excludeTags: customSchemaTags, + useCustomAnchors: true +}; diff --git a/packages/api-auth-rest-client/vitest.config.ts b/packages/api-auth-rest-client/vitest.config.ts new file mode 100644 index 0000000..648b91d --- /dev/null +++ b/packages/api-auth-rest-client/vitest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2025 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { NameOfPlugin } from "@twin.org/nameof-vitest-plugin"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [NameOfPlugin], + test: { + include: ["./tests/**/*.spec.ts"], + globals: true, + testTimeout: 300000, + hookTimeout: 300000, + bail: 1, + reporters: ["verbose"], + disableConsoleIntercept: true, + coverage: { + reporter: ["text", "lcov"], + include: ["src/**/*.ts"], + exclude: ["**/index.ts", "**/models/**/*.ts"] + }, + fileParallelism: false + } +}); diff --git a/packages/api-auth-service/LICENSE b/packages/api-auth-service/LICENSE new file mode 100644 index 0000000..677c34c --- /dev/null +++ b/packages/api-auth-service/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 IOTA Stiftung. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/packages/api-auth-service/README.md b/packages/api-auth-service/README.md new file mode 100644 index 0000000..550aa5b --- /dev/null +++ b/packages/api-auth-service/README.md @@ -0,0 +1,21 @@ +# TWIN API Auth Entity Storage Service + +This package provides an authentication service implementation and REST routes backed by entity storage. + +## Installation + +```shell +npm install @twin.org/api-auth-entity-storage-service +``` + +## Examples + +Usage of the APIs is shown in the examples [docs/examples.md](docs/examples.md) + +## Reference + +Detailed reference documentation for the API can be found in [docs/reference/index.md](docs/reference/index.md) + +## Changelog + +The changes between each version can be found in [docs/changelog.md](docs/changelog.md) diff --git a/packages/api-auth-service/docs/changelog.md b/packages/api-auth-service/docs/changelog.md new file mode 100644 index 0000000..100b7da --- /dev/null +++ b/packages/api-auth-service/docs/changelog.md @@ -0,0 +1,1619 @@ +# Changelog + +## [0.9.2-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.2-next.6...api-auth-entity-storage-service-v0.9.2-next.7) (2026-08-12) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.6 to 0.9.2-next.7 + * @twin.org/api-core bumped from 0.9.2-next.6 to 0.9.2-next.7 + * @twin.org/api-models bumped from 0.9.2-next.6 to 0.9.2-next.7 + +## [0.9.2-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.2-next.5...api-auth-entity-storage-service-v0.9.2-next.6) (2026-08-07) + + +### Features + +* health updates ([#243](https://github.com/iotaledger/twin-api/issues/243)) ([8360945](https://github.com/iotaledger/twin-api/commit/836094547839f63f2309d4bcd8c4d19128c73883)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.5 to 0.9.2-next.6 + * @twin.org/api-core bumped from 0.9.2-next.5 to 0.9.2-next.6 + * @twin.org/api-models bumped from 0.9.2-next.5 to 0.9.2-next.6 + +## [0.9.2-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.2-next.4...api-auth-entity-storage-service-v0.9.2-next.5) (2026-08-03) + + +### Features + +* health application checks ([#240](https://github.com/iotaledger/twin-api/issues/240)) ([68ec8b2](https://github.com/iotaledger/twin-api/commit/68ec8b29dae396928aa874826565efa6ad5d18c9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.4 to 0.9.2-next.5 + * @twin.org/api-core bumped from 0.9.2-next.4 to 0.9.2-next.5 + * @twin.org/api-models bumped from 0.9.2-next.4 to 0.9.2-next.5 + +## [0.9.2-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.2-next.3...api-auth-entity-storage-service-v0.9.2-next.4) (2026-08-02) + + +### Bug Fixes + +* preserve error stack in error responses when includeErrorStack is enabled ([#238](https://github.com/iotaledger/twin-api/issues/238)) ([7abc9b7](https://github.com/iotaledger/twin-api/commit/7abc9b79d07724abb68b2c06467cbdb54c880083)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.3 to 0.9.2-next.4 + * @twin.org/api-core bumped from 0.9.2-next.3 to 0.9.2-next.4 + * @twin.org/api-models bumped from 0.9.2-next.3 to 0.9.2-next.4 + +## [0.9.2-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.2-next.2...api-auth-entity-storage-service-v0.9.2-next.3) (2026-07-30) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.2 to 0.9.2-next.3 + * @twin.org/api-core bumped from 0.9.2-next.2 to 0.9.2-next.3 + * @twin.org/api-models bumped from 0.9.2-next.2 to 0.9.2-next.3 + +## [0.9.2-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.2-next.1...api-auth-entity-storage-service-v0.9.2-next.2) (2026-07-29) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.1 to 0.9.2-next.2 + * @twin.org/api-core bumped from 0.9.2-next.1 to 0.9.2-next.2 + * @twin.org/api-models bumped from 0.9.2-next.1 to 0.9.2-next.2 + +## [0.9.2-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.2-next.0...api-auth-entity-storage-service-v0.9.2-next.1) (2026-07-28) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add livez endpoint ([#57](https://github.com/iotaledger/twin-api/issues/57)) ([ef007db](https://github.com/iotaledger/twin-api/commit/ef007db8201736dd3053211f849ffd03baaa485e)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) +* auth header processor use user entity storage directly ([93084cf](https://github.com/iotaledger/twin-api/commit/93084cfed8f3ab16c80a1ab1bc90b1d9fc14bd25)) +* check tenant id in auth if set ([66f7337](https://github.com/iotaledger/twin-api/commit/66f73374d3cf4c1c85ea96ec74bb30712fb84dd7)) +* enhanced rest handling ([#208](https://github.com/iotaledger/twin-api/issues/208)) ([99d5f3f](https://github.com/iotaledger/twin-api/commit/99d5f3f96d262e57828d98d3f3b1e3da8a863378)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* hashed tenant id ([#135](https://github.com/iotaledger/twin-api/issues/135)) ([573c9b0](https://github.com/iotaledger/twin-api/commit/573c9b0714c281d69d5626c7576e93db826d266d)) +* improved rest handling ([#211](https://github.com/iotaledger/twin-api/issues/211)) ([12c50ea](https://github.com/iotaledger/twin-api/commit/12c50ea7655276eed7a73d311ac5286476c98201)) +* include iss and kid in jwt ([#189](https://github.com/iotaledger/twin-api/issues/189)) ([9df072a](https://github.com/iotaledger/twin-api/commit/9df072a1ecfc60dec611f59001449089264bbb1c)) +* jwt password version claim ([#129](https://github.com/iotaledger/twin-api/issues/129)) ([e15487b](https://github.com/iotaledger/twin-api/commit/e15487baafd3b1f3fd58f138db531d58053ce049)) +* location encoding ([#79](https://github.com/iotaledger/twin-api/issues/79)) ([c684465](https://github.com/iotaledger/twin-api/commit/c684465f2a871376152472bdecb6aa230b1101a1)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* organization identifiers ([#158](https://github.com/iotaledger/twin-api/issues/158)) ([ce13244](https://github.com/iotaledger/twin-api/commit/ce13244aaacbf82d9e5f87d905e283b36ad63bbf)) +* remove auth service dependency on admin service ([4aeb211](https://github.com/iotaledger/twin-api/commit/4aeb211a5ffcf2eaea89a6841faea3ac3069ef89)) +* remove hosting component ([#170](https://github.com/iotaledger/twin-api/issues/170)) ([e78c1e8](https://github.com/iotaledger/twin-api/commit/e78c1e87d2747bf58da02b6b77680708ff681122)) +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) +* tenant api and scopes ([#75](https://github.com/iotaledger/twin-api/issues/75)) ([c663141](https://github.com/iotaledger/twin-api/commit/c663141091e8974d769f8f9904ecdab009ebd083)) +* tenant id in jwt ([#140](https://github.com/iotaledger/twin-api/issues/140)) ([8d37a7b](https://github.com/iotaledger/twin-api/commit/8d37a7b98b45fde53df1e909cffc0869aa758655)) +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use new extractBearer method ([3e0cc54](https://github.com/iotaledger/twin-api/commit/3e0cc5462c06f59a6b744386eeff8326e5abbc95)) +* use new extractBearerToken method ([df654e9](https://github.com/iotaledger/twin-api/commit/df654e9caee5bd62f0be36f7be9902c8fab6ead6)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) +* handling the tid as undefined at refresh when empty ([#104](https://github.com/iotaledger/twin-api/issues/104)) ([d874a4a](https://github.com/iotaledger/twin-api/commit/d874a4a542c5fd45bb1bd56650247f844d056643)) +* include org in context ids from jwt ([a12cfdd](https://github.com/iotaledger/twin-api/commit/a12cfdddb05e2ed0300b26f3d7c0cfc033e59bd3)) +* remove token from return payload ([eaa4266](https://github.com/iotaledger/twin-api/commit/eaa42661b8540881b0751f5d2513258b3413f3ef)) +* run authentication rate cleanup per tenant ([#186](https://github.com/iotaledger/twin-api/issues/186)) ([d69c7aa](https://github.com/iotaledger/twin-api/commit/d69c7aa4ade77f1a52f1f554d8f7a3bd20f1b330)) +* use async getStore in tests ([0d25633](https://github.com/iotaledger/twin-api/commit/0d2563364de06a54ba487c700b601a883402d292)) +* use async getStore in tests ([63493dd](https://github.com/iotaledger/twin-api/commit/63493ddf3cf394c0d1e4cfcddae9a39f7ac697c4)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.2-next.0 to 0.9.2-next.1 + * @twin.org/api-core bumped from 0.9.2-next.0 to 0.9.2-next.1 + * @twin.org/api-models bumped from 0.9.2-next.0 to 0.9.2-next.1 + +## [0.9.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1...api-auth-entity-storage-service-v0.9.1) (2026-07-27) + + +### Features + +* release to production ([70ee2d5](https://github.com/iotaledger/twin-api/commit/70ee2d56a1dc9537d7c9c154d4cb78a235678a3a)) +* release to production ([#195](https://github.com/iotaledger/twin-api/issues/195)) ([a3f5c1f](https://github.com/iotaledger/twin-api/commit/a3f5c1fc35a748762af7efa4f7f95776004d1309)) +* release to production ([#197](https://github.com/iotaledger/twin-api/issues/197)) ([f04c156](https://github.com/iotaledger/twin-api/commit/f04c1567f801cde36c5ec8595f9b9369109d9e42)) +* release to production ([#201](https://github.com/iotaledger/twin-api/issues/201)) ([e1c46fd](https://github.com/iotaledger/twin-api/commit/e1c46fd02c1f4d44d5393e2f49a24f1e4468f240)) +* release to production ([#224](https://github.com/iotaledger/twin-api/issues/224)) ([dffaf08](https://github.com/iotaledger/twin-api/commit/dffaf082b7dccc6f57a5b7cd20b95d24bb8ec2f3)) + +## [0.9.1-next.9](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.8...api-auth-entity-storage-service-v0.9.1-next.9) (2026-07-23) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.8 to 0.9.1-next.9 + * @twin.org/api-core bumped from 0.9.1-next.8 to 0.9.1-next.9 + * @twin.org/api-models bumped from 0.9.1-next.8 to 0.9.1-next.9 + +## [0.9.1-next.8](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.7...api-auth-entity-storage-service-v0.9.1-next.8) (2026-07-03) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.7 to 0.9.1-next.8 + * @twin.org/api-core bumped from 0.9.1-next.7 to 0.9.1-next.8 + * @twin.org/api-models bumped from 0.9.1-next.7 to 0.9.1-next.8 + +## [0.9.1-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.6...api-auth-entity-storage-service-v0.9.1-next.7) (2026-07-02) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.6 to 0.9.1-next.7 + * @twin.org/api-core bumped from 0.9.1-next.6 to 0.9.1-next.7 + * @twin.org/api-models bumped from 0.9.1-next.6 to 0.9.1-next.7 + +## [0.9.1-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.5...api-auth-entity-storage-service-v0.9.1-next.6) (2026-06-30) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.5 to 0.9.1-next.6 + * @twin.org/api-core bumped from 0.9.1-next.5 to 0.9.1-next.6 + * @twin.org/api-models bumped from 0.9.1-next.5 to 0.9.1-next.6 + +## [0.9.1-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.4...api-auth-entity-storage-service-v0.9.1-next.5) (2026-06-30) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.4 to 0.9.1-next.5 + * @twin.org/api-core bumped from 0.9.1-next.4 to 0.9.1-next.5 + * @twin.org/api-models bumped from 0.9.1-next.4 to 0.9.1-next.5 + +## [0.9.1-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.3...api-auth-entity-storage-service-v0.9.1-next.4) (2026-06-30) + + +### Features + +* improved rest handling ([#211](https://github.com/iotaledger/twin-api/issues/211)) ([12c50ea](https://github.com/iotaledger/twin-api/commit/12c50ea7655276eed7a73d311ac5286476c98201)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.3 to 0.9.1-next.4 + * @twin.org/api-core bumped from 0.9.1-next.3 to 0.9.1-next.4 + * @twin.org/api-models bumped from 0.9.1-next.3 to 0.9.1-next.4 + +## [0.9.1-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.2...api-auth-entity-storage-service-v0.9.1-next.3) (2026-06-29) + + +### Features + +* enhanced rest handling ([#208](https://github.com/iotaledger/twin-api/issues/208)) ([99d5f3f](https://github.com/iotaledger/twin-api/commit/99d5f3f96d262e57828d98d3f3b1e3da8a863378)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.2 to 0.9.1-next.3 + * @twin.org/api-core bumped from 0.9.1-next.2 to 0.9.1-next.3 + * @twin.org/api-models bumped from 0.9.1-next.2 to 0.9.1-next.3 + +## [0.9.1-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.1...api-auth-entity-storage-service-v0.9.1-next.2) (2026-06-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.1 to 0.9.1-next.2 + * @twin.org/api-core bumped from 0.9.1-next.1 to 0.9.1-next.2 + * @twin.org/api-models bumped from 0.9.1-next.1 to 0.9.1-next.2 + +## [0.9.1-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.1-next.0...api-auth-entity-storage-service-v0.9.1-next.1) (2026-06-26) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add livez endpoint ([#57](https://github.com/iotaledger/twin-api/issues/57)) ([ef007db](https://github.com/iotaledger/twin-api/commit/ef007db8201736dd3053211f849ffd03baaa485e)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) +* auth header processor use user entity storage directly ([93084cf](https://github.com/iotaledger/twin-api/commit/93084cfed8f3ab16c80a1ab1bc90b1d9fc14bd25)) +* check tenant id in auth if set ([66f7337](https://github.com/iotaledger/twin-api/commit/66f73374d3cf4c1c85ea96ec74bb30712fb84dd7)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* hashed tenant id ([#135](https://github.com/iotaledger/twin-api/issues/135)) ([573c9b0](https://github.com/iotaledger/twin-api/commit/573c9b0714c281d69d5626c7576e93db826d266d)) +* include iss and kid in jwt ([#189](https://github.com/iotaledger/twin-api/issues/189)) ([9df072a](https://github.com/iotaledger/twin-api/commit/9df072a1ecfc60dec611f59001449089264bbb1c)) +* jwt password version claim ([#129](https://github.com/iotaledger/twin-api/issues/129)) ([e15487b](https://github.com/iotaledger/twin-api/commit/e15487baafd3b1f3fd58f138db531d58053ce049)) +* location encoding ([#79](https://github.com/iotaledger/twin-api/issues/79)) ([c684465](https://github.com/iotaledger/twin-api/commit/c684465f2a871376152472bdecb6aa230b1101a1)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* organization identifiers ([#158](https://github.com/iotaledger/twin-api/issues/158)) ([ce13244](https://github.com/iotaledger/twin-api/commit/ce13244aaacbf82d9e5f87d905e283b36ad63bbf)) +* remove auth service dependency on admin service ([4aeb211](https://github.com/iotaledger/twin-api/commit/4aeb211a5ffcf2eaea89a6841faea3ac3069ef89)) +* remove hosting component ([#170](https://github.com/iotaledger/twin-api/issues/170)) ([e78c1e8](https://github.com/iotaledger/twin-api/commit/e78c1e87d2747bf58da02b6b77680708ff681122)) +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) +* tenant api and scopes ([#75](https://github.com/iotaledger/twin-api/issues/75)) ([c663141](https://github.com/iotaledger/twin-api/commit/c663141091e8974d769f8f9904ecdab009ebd083)) +* tenant id in jwt ([#140](https://github.com/iotaledger/twin-api/issues/140)) ([8d37a7b](https://github.com/iotaledger/twin-api/commit/8d37a7b98b45fde53df1e909cffc0869aa758655)) +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use new extractBearer method ([3e0cc54](https://github.com/iotaledger/twin-api/commit/3e0cc5462c06f59a6b744386eeff8326e5abbc95)) +* use new extractBearerToken method ([df654e9](https://github.com/iotaledger/twin-api/commit/df654e9caee5bd62f0be36f7be9902c8fab6ead6)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) +* handling the tid as undefined at refresh when empty ([#104](https://github.com/iotaledger/twin-api/issues/104)) ([d874a4a](https://github.com/iotaledger/twin-api/commit/d874a4a542c5fd45bb1bd56650247f844d056643)) +* include org in context ids from jwt ([a12cfdd](https://github.com/iotaledger/twin-api/commit/a12cfdddb05e2ed0300b26f3d7c0cfc033e59bd3)) +* remove token from return payload ([eaa4266](https://github.com/iotaledger/twin-api/commit/eaa42661b8540881b0751f5d2513258b3413f3ef)) +* run authentication rate cleanup per tenant ([#186](https://github.com/iotaledger/twin-api/issues/186)) ([d69c7aa](https://github.com/iotaledger/twin-api/commit/d69c7aa4ade77f1a52f1f554d8f7a3bd20f1b330)) +* use async getStore in tests ([0d25633](https://github.com/iotaledger/twin-api/commit/0d2563364de06a54ba487c700b601a883402d292)) +* use async getStore in tests ([63493dd](https://github.com/iotaledger/twin-api/commit/63493ddf3cf394c0d1e4cfcddae9a39f7ac697c4)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.1-next.0 to 0.9.1-next.1 + * @twin.org/api-core bumped from 0.9.1-next.0 to 0.9.1-next.1 + * @twin.org/api-models bumped from 0.9.1-next.0 to 0.9.1-next.1 + +## [0.9.0](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.0...api-auth-entity-storage-service-v0.9.0) (2026-06-24) + + +### Features + +* release to production ([70ee2d5](https://github.com/iotaledger/twin-api/commit/70ee2d56a1dc9537d7c9c154d4cb78a235678a3a)) +* release to production ([#195](https://github.com/iotaledger/twin-api/issues/195)) ([a3f5c1f](https://github.com/iotaledger/twin-api/commit/a3f5c1fc35a748762af7efa4f7f95776004d1309)) +* release to production ([#197](https://github.com/iotaledger/twin-api/issues/197)) ([f04c156](https://github.com/iotaledger/twin-api/commit/f04c1567f801cde36c5ec8595f9b9369109d9e42)) +* release to production ([#201](https://github.com/iotaledger/twin-api/issues/201)) ([e1c46fd](https://github.com/iotaledger/twin-api/commit/e1c46fd02c1f4d44d5393e2f49a24f1e4468f240)) + +## [0.9.0-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.9.0-next.0...api-auth-entity-storage-service-v0.9.0-next.1) (2026-06-23) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add livez endpoint ([#57](https://github.com/iotaledger/twin-api/issues/57)) ([ef007db](https://github.com/iotaledger/twin-api/commit/ef007db8201736dd3053211f849ffd03baaa485e)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) +* auth header processor use user entity storage directly ([93084cf](https://github.com/iotaledger/twin-api/commit/93084cfed8f3ab16c80a1ab1bc90b1d9fc14bd25)) +* check tenant id in auth if set ([66f7337](https://github.com/iotaledger/twin-api/commit/66f73374d3cf4c1c85ea96ec74bb30712fb84dd7)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* hashed tenant id ([#135](https://github.com/iotaledger/twin-api/issues/135)) ([573c9b0](https://github.com/iotaledger/twin-api/commit/573c9b0714c281d69d5626c7576e93db826d266d)) +* include iss and kid in jwt ([#189](https://github.com/iotaledger/twin-api/issues/189)) ([9df072a](https://github.com/iotaledger/twin-api/commit/9df072a1ecfc60dec611f59001449089264bbb1c)) +* jwt password version claim ([#129](https://github.com/iotaledger/twin-api/issues/129)) ([e15487b](https://github.com/iotaledger/twin-api/commit/e15487baafd3b1f3fd58f138db531d58053ce049)) +* location encoding ([#79](https://github.com/iotaledger/twin-api/issues/79)) ([c684465](https://github.com/iotaledger/twin-api/commit/c684465f2a871376152472bdecb6aa230b1101a1)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* organization identifiers ([#158](https://github.com/iotaledger/twin-api/issues/158)) ([ce13244](https://github.com/iotaledger/twin-api/commit/ce13244aaacbf82d9e5f87d905e283b36ad63bbf)) +* remove auth service dependency on admin service ([4aeb211](https://github.com/iotaledger/twin-api/commit/4aeb211a5ffcf2eaea89a6841faea3ac3069ef89)) +* remove hosting component ([#170](https://github.com/iotaledger/twin-api/issues/170)) ([e78c1e8](https://github.com/iotaledger/twin-api/commit/e78c1e87d2747bf58da02b6b77680708ff681122)) +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) +* tenant api and scopes ([#75](https://github.com/iotaledger/twin-api/issues/75)) ([c663141](https://github.com/iotaledger/twin-api/commit/c663141091e8974d769f8f9904ecdab009ebd083)) +* tenant id in jwt ([#140](https://github.com/iotaledger/twin-api/issues/140)) ([8d37a7b](https://github.com/iotaledger/twin-api/commit/8d37a7b98b45fde53df1e909cffc0869aa758655)) +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use new extractBearer method ([3e0cc54](https://github.com/iotaledger/twin-api/commit/3e0cc5462c06f59a6b744386eeff8326e5abbc95)) +* use new extractBearerToken method ([df654e9](https://github.com/iotaledger/twin-api/commit/df654e9caee5bd62f0be36f7be9902c8fab6ead6)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) +* handling the tid as undefined at refresh when empty ([#104](https://github.com/iotaledger/twin-api/issues/104)) ([d874a4a](https://github.com/iotaledger/twin-api/commit/d874a4a542c5fd45bb1bd56650247f844d056643)) +* include org in context ids from jwt ([a12cfdd](https://github.com/iotaledger/twin-api/commit/a12cfdddb05e2ed0300b26f3d7c0cfc033e59bd3)) +* remove token from return payload ([eaa4266](https://github.com/iotaledger/twin-api/commit/eaa42661b8540881b0751f5d2513258b3413f3ef)) +* run authentication rate cleanup per tenant ([#186](https://github.com/iotaledger/twin-api/issues/186)) ([d69c7aa](https://github.com/iotaledger/twin-api/commit/d69c7aa4ade77f1a52f1f554d8f7a3bd20f1b330)) +* use async getStore in tests ([0d25633](https://github.com/iotaledger/twin-api/commit/0d2563364de06a54ba487c700b601a883402d292)) +* use async getStore in tests ([63493dd](https://github.com/iotaledger/twin-api/commit/63493ddf3cf394c0d1e4cfcddae9a39f7ac697c4)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.9.0-next.0 to 0.9.0-next.1 + * @twin.org/api-core bumped from 0.9.0-next.0 to 0.9.0-next.1 + * @twin.org/api-models bumped from 0.9.0-next.0 to 0.9.0-next.1 + +## [0.0.3-next.53](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.52...api-auth-entity-storage-service-v0.0.3-next.53) (2026-06-23) + + +### Features + +* include iss and kid in jwt ([#189](https://github.com/iotaledger/twin-api/issues/189)) ([9df072a](https://github.com/iotaledger/twin-api/commit/9df072a1ecfc60dec611f59001449089264bbb1c)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.52 to 0.0.3-next.53 + * @twin.org/api-core bumped from 0.0.3-next.52 to 0.0.3-next.53 + * @twin.org/api-models bumped from 0.0.3-next.52 to 0.0.3-next.53 + +## [0.0.3-next.52](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.51...api-auth-entity-storage-service-v0.0.3-next.52) (2026-06-22) + + +### Bug Fixes + +* run authentication rate cleanup per tenant ([#186](https://github.com/iotaledger/twin-api/issues/186)) ([d69c7aa](https://github.com/iotaledger/twin-api/commit/d69c7aa4ade77f1a52f1f554d8f7a3bd20f1b330)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.51 to 0.0.3-next.52 + * @twin.org/api-core bumped from 0.0.3-next.51 to 0.0.3-next.52 + * @twin.org/api-models bumped from 0.0.3-next.51 to 0.0.3-next.52 + +## [0.0.3-next.51](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.50...api-auth-entity-storage-service-v0.0.3-next.51) (2026-06-20) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.50 to 0.0.3-next.51 + * @twin.org/api-core bumped from 0.0.3-next.50 to 0.0.3-next.51 + * @twin.org/api-models bumped from 0.0.3-next.50 to 0.0.3-next.51 + +## [0.0.3-next.50](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.49...api-auth-entity-storage-service-v0.0.3-next.50) (2026-06-19) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.49 to 0.0.3-next.50 + * @twin.org/api-core bumped from 0.0.3-next.49 to 0.0.3-next.50 + * @twin.org/api-models bumped from 0.0.3-next.49 to 0.0.3-next.50 + +## [0.0.3-next.49](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.48...api-auth-entity-storage-service-v0.0.3-next.49) (2026-06-19) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.48 to 0.0.3-next.49 + * @twin.org/api-core bumped from 0.0.3-next.48 to 0.0.3-next.49 + * @twin.org/api-models bumped from 0.0.3-next.48 to 0.0.3-next.49 + +## [0.0.3-next.48](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.47...api-auth-entity-storage-service-v0.0.3-next.48) (2026-06-19) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.47 to 0.0.3-next.48 + * @twin.org/api-core bumped from 0.0.3-next.47 to 0.0.3-next.48 + * @twin.org/api-models bumped from 0.0.3-next.47 to 0.0.3-next.48 + +## [0.0.3-next.47](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.46...api-auth-entity-storage-service-v0.0.3-next.47) (2026-06-18) + + +### Features + +* remove hosting component ([#170](https://github.com/iotaledger/twin-api/issues/170)) ([e78c1e8](https://github.com/iotaledger/twin-api/commit/e78c1e87d2747bf58da02b6b77680708ff681122)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.46 to 0.0.3-next.47 + * @twin.org/api-core bumped from 0.0.3-next.46 to 0.0.3-next.47 + * @twin.org/api-models bumped from 0.0.3-next.46 to 0.0.3-next.47 + +## [0.0.3-next.46](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.45...api-auth-entity-storage-service-v0.0.3-next.46) (2026-06-17) + + +### Bug Fixes + +* use async getStore in tests ([0d25633](https://github.com/iotaledger/twin-api/commit/0d2563364de06a54ba487c700b601a883402d292)) +* use async getStore in tests ([63493dd](https://github.com/iotaledger/twin-api/commit/63493ddf3cf394c0d1e4cfcddae9a39f7ac697c4)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.45 to 0.0.3-next.46 + * @twin.org/api-core bumped from 0.0.3-next.45 to 0.0.3-next.46 + * @twin.org/api-models bumped from 0.0.3-next.45 to 0.0.3-next.46 + +## [0.0.3-next.45](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.44...api-auth-entity-storage-service-v0.0.3-next.45) (2026-06-15) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.44 to 0.0.3-next.45 + * @twin.org/api-core bumped from 0.0.3-next.44 to 0.0.3-next.45 + * @twin.org/api-models bumped from 0.0.3-next.44 to 0.0.3-next.45 + +## [0.0.3-next.44](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.43...api-auth-entity-storage-service-v0.0.3-next.44) (2026-06-11) + + +### Features + +* organization identifiers ([#158](https://github.com/iotaledger/twin-api/issues/158)) ([ce13244](https://github.com/iotaledger/twin-api/commit/ce13244aaacbf82d9e5f87d905e283b36ad63bbf)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.43 to 0.0.3-next.44 + * @twin.org/api-core bumped from 0.0.3-next.43 to 0.0.3-next.44 + * @twin.org/api-models bumped from 0.0.3-next.43 to 0.0.3-next.44 + +## [0.0.3-next.43](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.42...api-auth-entity-storage-service-v0.0.3-next.43) (2026-06-10) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.42 to 0.0.3-next.43 + * @twin.org/api-core bumped from 0.0.3-next.42 to 0.0.3-next.43 + * @twin.org/api-models bumped from 0.0.3-next.42 to 0.0.3-next.43 + +## [0.0.3-next.42](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.41...api-auth-entity-storage-service-v0.0.3-next.42) (2026-06-08) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.41 to 0.0.3-next.42 + * @twin.org/api-core bumped from 0.0.3-next.41 to 0.0.3-next.42 + * @twin.org/api-models bumped from 0.0.3-next.41 to 0.0.3-next.42 + +## [0.0.3-next.41](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.40...api-auth-entity-storage-service-v0.0.3-next.41) (2026-06-05) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.40 to 0.0.3-next.41 + * @twin.org/api-core bumped from 0.0.3-next.40 to 0.0.3-next.41 + * @twin.org/api-models bumped from 0.0.3-next.40 to 0.0.3-next.41 + +## [0.0.3-next.40](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.39...api-auth-entity-storage-service-v0.0.3-next.40) (2026-06-04) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.39 to 0.0.3-next.40 + * @twin.org/api-core bumped from 0.0.3-next.39 to 0.0.3-next.40 + * @twin.org/api-models bumped from 0.0.3-next.39 to 0.0.3-next.40 + +## [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) + + +### Features + +* tenant id in jwt ([#140](https://github.com/iotaledger/twin-api/issues/140)) ([8d37a7b](https://github.com/iotaledger/twin-api/commit/8d37a7b98b45fde53df1e909cffc0869aa758655)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.38 to 0.0.3-next.39 + * @twin.org/api-core bumped from 0.0.3-next.38 to 0.0.3-next.39 + * @twin.org/api-models bumped from 0.0.3-next.38 to 0.0.3-next.39 + +## [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) + + +### Features + +* hashed tenant id ([#135](https://github.com/iotaledger/twin-api/issues/135)) ([573c9b0](https://github.com/iotaledger/twin-api/commit/573c9b0714c281d69d5626c7576e93db826d266d)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.37 to 0.0.3-next.38 + * @twin.org/api-core bumped from 0.0.3-next.37 to 0.0.3-next.38 + * @twin.org/api-models bumped from 0.0.3-next.37 to 0.0.3-next.38 + +## [0.0.3-next.37](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.36...api-auth-entity-storage-service-v0.0.3-next.37) (2026-05-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.36 to 0.0.3-next.37 + * @twin.org/api-core bumped from 0.0.3-next.36 to 0.0.3-next.37 + * @twin.org/api-models bumped from 0.0.3-next.36 to 0.0.3-next.37 + +## [0.0.3-next.36](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.35...api-auth-entity-storage-service-v0.0.3-next.36) (2026-05-22) + + +### Features + +* jwt password version claim ([#129](https://github.com/iotaledger/twin-api/issues/129)) ([e15487b](https://github.com/iotaledger/twin-api/commit/e15487baafd3b1f3fd58f138db531d58053ce049)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.35 to 0.0.3-next.36 + * @twin.org/api-core bumped from 0.0.3-next.35 to 0.0.3-next.36 + * @twin.org/api-models bumped from 0.0.3-next.35 to 0.0.3-next.36 + +## [0.0.3-next.35](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.34...api-auth-entity-storage-service-v0.0.3-next.35) (2026-05-21) + + +### Features + +* user partitioning ([#126](https://github.com/iotaledger/twin-api/issues/126)) ([6bf0da3](https://github.com/iotaledger/twin-api/commit/6bf0da3c42406c9838e80e0ddd6b21f5c64aac90)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.34 to 0.0.3-next.35 + * @twin.org/api-core bumped from 0.0.3-next.34 to 0.0.3-next.35 + * @twin.org/api-models bumped from 0.0.3-next.34 to 0.0.3-next.35 + +## [0.0.3-next.34](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.33...api-auth-entity-storage-service-v0.0.3-next.34) (2026-05-19) + + +### Features + +* update dependencies ([32b8cd2](https://github.com/iotaledger/twin-api/commit/32b8cd20353119dd1998e293d54063cf4d9ecc29)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.33 to 0.0.3-next.34 + * @twin.org/api-core bumped from 0.0.3-next.33 to 0.0.3-next.34 + * @twin.org/api-models bumped from 0.0.3-next.33 to 0.0.3-next.34 + +## [0.0.3-next.33](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.32...api-auth-entity-storage-service-v0.0.3-next.33) (2026-05-11) + + +### Features + +* typescript 6 update ([78d2aa0](https://github.com/iotaledger/twin-api/commit/78d2aa00902f79b61973079b798b87ec05f18a8b)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.32 to 0.0.3-next.33 + * @twin.org/api-core bumped from 0.0.3-next.32 to 0.0.3-next.33 + * @twin.org/api-models bumped from 0.0.3-next.32 to 0.0.3-next.33 + +## [0.0.3-next.32](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.31...api-auth-entity-storage-service-v0.0.3-next.32) (2026-05-07) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.31 to 0.0.3-next.32 + * @twin.org/api-core bumped from 0.0.3-next.31 to 0.0.3-next.32 + * @twin.org/api-models bumped from 0.0.3-next.31 to 0.0.3-next.32 + +## [0.0.3-next.31](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.30...api-auth-entity-storage-service-v0.0.3-next.31) (2026-05-06) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.30 to 0.0.3-next.31 + * @twin.org/api-core bumped from 0.0.3-next.30 to 0.0.3-next.31 + * @twin.org/api-models bumped from 0.0.3-next.30 to 0.0.3-next.31 + +## [0.0.3-next.30](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.29...api-auth-entity-storage-service-v0.0.3-next.30) (2026-05-05) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.29 to 0.0.3-next.30 + * @twin.org/api-core bumped from 0.0.3-next.29 to 0.0.3-next.30 + * @twin.org/api-models bumped from 0.0.3-next.29 to 0.0.3-next.30 + +## [0.0.3-next.29](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.28...api-auth-entity-storage-service-v0.0.3-next.29) (2026-05-01) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.28 to 0.0.3-next.29 + * @twin.org/api-core bumped from 0.0.3-next.28 to 0.0.3-next.29 + * @twin.org/api-models bumped from 0.0.3-next.28 to 0.0.3-next.29 + +## [0.0.3-next.28](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.27...api-auth-entity-storage-service-v0.0.3-next.28) (2026-04-30) + + +### Bug Fixes + +* change logout and refresh routes from GET to POST ([#111](https://github.com/iotaledger/twin-api/issues/111)) ([cb8b64b](https://github.com/iotaledger/twin-api/commit/cb8b64b6507f9991baa78a663de2e84269695c82)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.27 to 0.0.3-next.28 + * @twin.org/api-core bumped from 0.0.3-next.27 to 0.0.3-next.28 + * @twin.org/api-models bumped from 0.0.3-next.27 to 0.0.3-next.28 + +## [0.0.3-next.27](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.26...api-auth-entity-storage-service-v0.0.3-next.27) (2026-04-23) + + +### Bug Fixes + +* handling the tid as undefined at refresh when empty ([#104](https://github.com/iotaledger/twin-api/issues/104)) ([d874a4a](https://github.com/iotaledger/twin-api/commit/d874a4a542c5fd45bb1bd56650247f844d056643)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.26 to 0.0.3-next.27 + * @twin.org/api-core bumped from 0.0.3-next.26 to 0.0.3-next.27 + * @twin.org/api-models bumped from 0.0.3-next.26 to 0.0.3-next.27 + +## [0.0.3-next.26](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.25...api-auth-entity-storage-service-v0.0.3-next.26) (2026-04-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.25 to 0.0.3-next.26 + * @twin.org/api-core bumped from 0.0.3-next.25 to 0.0.3-next.26 + * @twin.org/api-models bumped from 0.0.3-next.25 to 0.0.3-next.26 + +## [0.0.3-next.25](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.24...api-auth-entity-storage-service-v0.0.3-next.25) (2026-04-14) + + +### Features + +* auth header processor use user entity storage directly ([93084cf](https://github.com/iotaledger/twin-api/commit/93084cfed8f3ab16c80a1ab1bc90b1d9fc14bd25)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.24 to 0.0.3-next.25 + * @twin.org/api-core bumped from 0.0.3-next.24 to 0.0.3-next.25 + * @twin.org/api-models bumped from 0.0.3-next.24 to 0.0.3-next.25 + +## [0.0.3-next.24](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.23...api-auth-entity-storage-service-v0.0.3-next.24) (2026-04-14) + + +### Features + +* remove auth service dependency on admin service ([4aeb211](https://github.com/iotaledger/twin-api/commit/4aeb211a5ffcf2eaea89a6841faea3ac3069ef89)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.23 to 0.0.3-next.24 + * @twin.org/api-core bumped from 0.0.3-next.23 to 0.0.3-next.24 + * @twin.org/api-models bumped from 0.0.3-next.23 to 0.0.3-next.24 + +## [0.0.3-next.23](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.22...api-auth-entity-storage-service-v0.0.3-next.23) (2026-04-14) + + +### Features + +* auth enhancements ([#93](https://github.com/iotaledger/twin-api/issues/93)) ([921a50c](https://github.com/iotaledger/twin-api/commit/921a50cd89d26e530a6be6174a5a803060fa0eb6)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.22 to 0.0.3-next.23 + * @twin.org/api-core bumped from 0.0.3-next.22 to 0.0.3-next.23 + * @twin.org/api-models bumped from 0.0.3-next.22 to 0.0.3-next.23 + +## [0.0.3-next.22](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.21...api-auth-entity-storage-service-v0.0.3-next.22) (2026-03-27) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.21 to 0.0.3-next.22 + * @twin.org/api-core bumped from 0.0.3-next.21 to 0.0.3-next.22 + * @twin.org/api-models bumped from 0.0.3-next.21 to 0.0.3-next.22 + +## [0.0.3-next.21](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.20...api-auth-entity-storage-service-v0.0.3-next.21) (2026-03-11) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.20 to 0.0.3-next.21 + * @twin.org/api-core bumped from 0.0.3-next.20 to 0.0.3-next.21 + * @twin.org/api-models bumped from 0.0.3-next.20 to 0.0.3-next.21 + +## [0.0.3-next.20](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.19...api-auth-entity-storage-service-v0.0.3-next.20) (2026-02-09) + + +### Features + +* location encoding ([#79](https://github.com/iotaledger/twin-api/issues/79)) ([c684465](https://github.com/iotaledger/twin-api/commit/c684465f2a871376152472bdecb6aa230b1101a1)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.19 to 0.0.3-next.20 + * @twin.org/api-core bumped from 0.0.3-next.19 to 0.0.3-next.20 + * @twin.org/api-models bumped from 0.0.3-next.19 to 0.0.3-next.20 + +## [0.0.3-next.19](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.18...api-auth-entity-storage-service-v0.0.3-next.19) (2026-02-06) + + +### Features + +* user admin service ([#77](https://github.com/iotaledger/twin-api/issues/77)) ([c8491df](https://github.com/iotaledger/twin-api/commit/c8491df7b07c1f45560c8a78c6adc806d0ececbb)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.18 to 0.0.3-next.19 + * @twin.org/api-core bumped from 0.0.3-next.18 to 0.0.3-next.19 + * @twin.org/api-models bumped from 0.0.3-next.18 to 0.0.3-next.19 + +## [0.0.3-next.18](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.17...api-auth-entity-storage-service-v0.0.3-next.18) (2026-02-04) + + +### Features + +* tenant api and scopes ([#75](https://github.com/iotaledger/twin-api/issues/75)) ([c663141](https://github.com/iotaledger/twin-api/commit/c663141091e8974d769f8f9904ecdab009ebd083)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.17 to 0.0.3-next.18 + * @twin.org/api-core bumped from 0.0.3-next.17 to 0.0.3-next.18 + * @twin.org/api-models bumped from 0.0.3-next.17 to 0.0.3-next.18 + +## [0.0.3-next.17](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.16...api-auth-entity-storage-service-v0.0.3-next.17) (2026-01-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.16 to 0.0.3-next.17 + * @twin.org/api-core bumped from 0.0.3-next.16 to 0.0.3-next.17 + * @twin.org/api-models bumped from 0.0.3-next.16 to 0.0.3-next.17 + +## [0.0.3-next.16](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.15...api-auth-entity-storage-service-v0.0.3-next.16) (2026-01-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.15 to 0.0.3-next.16 + * @twin.org/api-core bumped from 0.0.3-next.15 to 0.0.3-next.16 + * @twin.org/api-models bumped from 0.0.3-next.15 to 0.0.3-next.16 + +## [0.0.3-next.15](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.14...api-auth-entity-storage-service-v0.0.3-next.15) (2026-01-22) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.14 to 0.0.3-next.15 + * @twin.org/api-core bumped from 0.0.3-next.14 to 0.0.3-next.15 + * @twin.org/api-models bumped from 0.0.3-next.14 to 0.0.3-next.15 + +## [0.0.3-next.14](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.13...api-auth-entity-storage-service-v0.0.3-next.14) (2026-01-20) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.13 to 0.0.3-next.14 + * @twin.org/api-core bumped from 0.0.3-next.13 to 0.0.3-next.14 + * @twin.org/api-models bumped from 0.0.3-next.13 to 0.0.3-next.14 + +## [0.0.3-next.13](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.12...api-auth-entity-storage-service-v0.0.3-next.13) (2026-01-19) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.12 to 0.0.3-next.13 + * @twin.org/api-core bumped from 0.0.3-next.12 to 0.0.3-next.13 + * @twin.org/api-models bumped from 0.0.3-next.12 to 0.0.3-next.13 + +## [0.0.3-next.12](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.11...api-auth-entity-storage-service-v0.0.3-next.12) (2026-01-12) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.11 to 0.0.3-next.12 + * @twin.org/api-core bumped from 0.0.3-next.11 to 0.0.3-next.12 + * @twin.org/api-models bumped from 0.0.3-next.11 to 0.0.3-next.12 + +## [0.0.3-next.11](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.10...api-auth-entity-storage-service-v0.0.3-next.11) (2026-01-08) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.10 to 0.0.3-next.11 + * @twin.org/api-core bumped from 0.0.3-next.10 to 0.0.3-next.11 + * @twin.org/api-models bumped from 0.0.3-next.10 to 0.0.3-next.11 + +## [0.0.3-next.10](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.9...api-auth-entity-storage-service-v0.0.3-next.10) (2026-01-05) + + +### Bug Fixes + +* remove token from return payload ([eaa4266](https://github.com/iotaledger/twin-api/commit/eaa42661b8540881b0751f5d2513258b3413f3ef)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.9 to 0.0.3-next.10 + * @twin.org/api-core bumped from 0.0.3-next.9 to 0.0.3-next.10 + * @twin.org/api-models bumped from 0.0.3-next.9 to 0.0.3-next.10 + +## [0.0.3-next.9](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.8...api-auth-entity-storage-service-v0.0.3-next.9) (2026-01-05) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add livez endpoint ([#57](https://github.com/iotaledger/twin-api/issues/57)) ([ef007db](https://github.com/iotaledger/twin-api/commit/ef007db8201736dd3053211f849ffd03baaa485e)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* check tenant id in auth if set ([66f7337](https://github.com/iotaledger/twin-api/commit/66f73374d3cf4c1c85ea96ec74bb30712fb84dd7)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* modify authHeaderProcessor to retain token in response body ([#53](https://github.com/iotaledger/twin-api/issues/53)) ([5d9ae76](https://github.com/iotaledger/twin-api/commit/5d9ae76b5b52a8e10dac391b2d5784638a186583)) +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use new extractBearer method ([3e0cc54](https://github.com/iotaledger/twin-api/commit/3e0cc5462c06f59a6b744386eeff8326e5abbc95)) +* use new extractBearerToken method ([df654e9](https://github.com/iotaledger/twin-api/commit/df654e9caee5bd62f0be36f7be9902c8fab6ead6)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Bug Fixes + +* include org in context ids from jwt ([a12cfdd](https://github.com/iotaledger/twin-api/commit/a12cfdddb05e2ed0300b26f3d7c0cfc033e59bd3)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.8 to 0.0.3-next.9 + * @twin.org/api-core bumped from 0.0.3-next.8 to 0.0.3-next.9 + * @twin.org/api-models bumped from 0.0.3-next.8 to 0.0.3-next.9 + +## [0.0.3-next.8](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.7...api-auth-entity-storage-service-v0.0.3-next.8) (2025-12-17) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.7 to 0.0.3-next.8 + * @twin.org/api-core bumped from 0.0.3-next.7 to 0.0.3-next.8 + * @twin.org/api-models bumped from 0.0.3-next.7 to 0.0.3-next.8 + +## [0.0.3-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.6...api-auth-entity-storage-service-v0.0.3-next.7) (2025-11-26) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.6 to 0.0.3-next.7 + * @twin.org/api-core bumped from 0.0.3-next.6 to 0.0.3-next.7 + * @twin.org/api-models bumped from 0.0.3-next.6 to 0.0.3-next.7 + +## [0.0.3-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.5...api-auth-entity-storage-service-v0.0.3-next.6) (2025-11-20) + + +### Features + +* check tenant id in auth if set ([66f7337](https://github.com/iotaledger/twin-api/commit/66f73374d3cf4c1c85ea96ec74bb30712fb84dd7)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.5 to 0.0.3-next.6 + * @twin.org/api-core bumped from 0.0.3-next.5 to 0.0.3-next.6 + * @twin.org/api-models bumped from 0.0.3-next.5 to 0.0.3-next.6 + +## [0.0.3-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.4...api-auth-entity-storage-service-v0.0.3-next.5) (2025-11-14) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.4 to 0.0.3-next.5 + * @twin.org/api-core bumped from 0.0.3-next.4 to 0.0.3-next.5 + * @twin.org/api-models bumped from 0.0.3-next.4 to 0.0.3-next.5 + +## [0.0.3-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.3...api-auth-entity-storage-service-v0.0.3-next.4) (2025-11-14) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use new extractBearer method ([3e0cc54](https://github.com/iotaledger/twin-api/commit/3e0cc5462c06f59a6b744386eeff8326e5abbc95)) +* use new extractBearerToken method ([df654e9](https://github.com/iotaledger/twin-api/commit/df654e9caee5bd62f0be36f7be9902c8fab6ead6)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Bug Fixes + +* include org in context ids from jwt ([a12cfdd](https://github.com/iotaledger/twin-api/commit/a12cfdddb05e2ed0300b26f3d7c0cfc033e59bd3)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.3 to 0.0.3-next.4 + * @twin.org/api-core bumped from 0.0.3-next.3 to 0.0.3-next.4 + * @twin.org/api-models bumped from 0.0.3-next.3 to 0.0.3-next.4 + +## [0.0.3-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.2...api-auth-entity-storage-service-v0.0.3-next.3) (2025-11-14) + + +### Bug Fixes + +* include org in context ids from jwt ([a12cfdd](https://github.com/iotaledger/twin-api/commit/a12cfdddb05e2ed0300b26f3d7c0cfc033e59bd3)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.2 to 0.0.3-next.3 + * @twin.org/api-core bumped from 0.0.3-next.2 to 0.0.3-next.3 + * @twin.org/api-models bumped from 0.0.3-next.2 to 0.0.3-next.3 + +## [0.0.3-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.1...api-auth-entity-storage-service-v0.0.3-next.2) (2025-11-12) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.1 to 0.0.3-next.2 + * @twin.org/api-core bumped from 0.0.3-next.1 to 0.0.3-next.2 + * @twin.org/api-models bumped from 0.0.3-next.1 to 0.0.3-next.2 + +## [0.0.3-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.3-next.0...api-auth-entity-storage-service-v0.0.3-next.1) (2025-11-10) + + +### Features + +* add context id features ([#42](https://github.com/iotaledger/twin-api/issues/42)) ([0186055](https://github.com/iotaledger/twin-api/commit/0186055c48afde842a4254b4df9ac9249c40fe40)) +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) +* use new extractBearer method ([3e0cc54](https://github.com/iotaledger/twin-api/commit/3e0cc5462c06f59a6b744386eeff8326e5abbc95)) +* use new extractBearerToken method ([df654e9](https://github.com/iotaledger/twin-api/commit/df654e9caee5bd62f0be36f7be9902c8fab6ead6)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.3-next.0 to 0.0.3-next.1 + * @twin.org/api-core bumped from 0.0.3-next.0 to 0.0.3-next.1 + * @twin.org/api-models bumped from 0.0.3-next.0 to 0.0.3-next.1 + +## [0.0.2-next.13](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.12...api-auth-entity-storage-service-v0.0.2-next.13) (2025-10-09) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.12 to 0.0.2-next.13 + * @twin.org/api-core bumped from 0.0.2-next.12 to 0.0.2-next.13 + * @twin.org/api-models bumped from 0.0.2-next.12 to 0.0.2-next.13 + +## [0.0.2-next.12](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.11...api-auth-entity-storage-service-v0.0.2-next.12) (2025-10-09) + + +### Features + +* add validate-locales ([cdba610](https://github.com/iotaledger/twin-api/commit/cdba610a0acb5022d2e3ce729732e6646a297e5e)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.11 to 0.0.2-next.12 + * @twin.org/api-core bumped from 0.0.2-next.11 to 0.0.2-next.12 + * @twin.org/api-models bumped from 0.0.2-next.11 to 0.0.2-next.12 + +## [0.0.2-next.11](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.10...api-auth-entity-storage-service-v0.0.2-next.11) (2025-09-29) + + +### Features + +* update IComponent signatures ([915ce37](https://github.com/iotaledger/twin-api/commit/915ce37712326ab4aa6869c350eabaa4622e8430)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.10 to 0.0.2-next.11 + * @twin.org/api-core bumped from 0.0.2-next.10 to 0.0.2-next.11 + * @twin.org/api-models bumped from 0.0.2-next.10 to 0.0.2-next.11 + +## [0.0.2-next.10](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.9...api-auth-entity-storage-service-v0.0.2-next.10) (2025-09-23) + + +### Features + +* use new extractBearer method ([3e0cc54](https://github.com/iotaledger/twin-api/commit/3e0cc5462c06f59a6b744386eeff8326e5abbc95)) +* use new extractBearerToken method ([df654e9](https://github.com/iotaledger/twin-api/commit/df654e9caee5bd62f0be36f7be9902c8fab6ead6)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.9 to 0.0.2-next.10 + * @twin.org/api-core bumped from 0.0.2-next.9 to 0.0.2-next.10 + * @twin.org/api-models bumped from 0.0.2-next.9 to 0.0.2-next.10 + +## [0.0.2-next.9](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.8...api-auth-entity-storage-service-v0.0.2-next.9) (2025-08-29) + + +### Features + +* eslint migration to flat config ([0dd5820](https://github.com/iotaledger/twin-api/commit/0dd5820e3af97350fd08b8d226f4a6c1a9246805)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.8 to 0.0.2-next.9 + * @twin.org/api-core bumped from 0.0.2-next.8 to 0.0.2-next.9 + * @twin.org/api-models bumped from 0.0.2-next.8 to 0.0.2-next.9 + +## [0.0.2-next.8](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.7...api-auth-entity-storage-service-v0.0.2-next.8) (2025-08-21) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.7 to 0.0.2-next.8 + * @twin.org/api-core bumped from 0.0.2-next.7 to 0.0.2-next.8 + * @twin.org/api-models bumped from 0.0.2-next.7 to 0.0.2-next.8 + +## [0.0.2-next.7](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.6...api-auth-entity-storage-service-v0.0.2-next.7) (2025-08-20) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.6 to 0.0.2-next.7 + * @twin.org/api-core bumped from 0.0.2-next.6 to 0.0.2-next.7 + * @twin.org/api-models bumped from 0.0.2-next.6 to 0.0.2-next.7 + +## [0.0.2-next.6](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.5...api-auth-entity-storage-service-v0.0.2-next.6) (2025-08-19) + + +### Features + +* update framework core ([d8eebf2](https://github.com/iotaledger/twin-api/commit/d8eebf267fa2a0abaa84e58590496e9d20490cfa)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.5 to 0.0.2-next.6 + * @twin.org/api-core bumped from 0.0.2-next.5 to 0.0.2-next.6 + * @twin.org/api-models bumped from 0.0.2-next.5 to 0.0.2-next.6 + +## [0.0.2-next.5](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.4...api-auth-entity-storage-service-v0.0.2-next.5) (2025-07-25) + + +### Features + +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.4 to 0.0.2-next.5 + * @twin.org/api-core bumped from 0.0.2-next.4 to 0.0.2-next.5 + * @twin.org/api-models bumped from 0.0.2-next.4 to 0.0.2-next.5 + +## [0.0.2-next.4](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.3...api-auth-entity-storage-service-v0.0.2-next.4) (2025-07-25) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.3 to 0.0.2-next.4 + * @twin.org/api-core bumped from 0.0.2-next.3 to 0.0.2-next.4 + * @twin.org/api-models bumped from 0.0.2-next.3 to 0.0.2-next.4 + +## [0.0.2-next.3](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.2...api-auth-entity-storage-service-v0.0.2-next.3) (2025-07-24) + + +### Features + +* remove unused namespace ([08478f2](https://github.com/iotaledger/twin-api/commit/08478f27efda9beb0271fdb22f6972e918361965)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.2 to 0.0.2-next.3 + * @twin.org/api-core bumped from 0.0.2-next.2 to 0.0.2-next.3 + * @twin.org/api-models bumped from 0.0.2-next.2 to 0.0.2-next.3 + +## [0.0.2-next.2](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.1...api-auth-entity-storage-service-v0.0.2-next.2) (2025-07-17) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.1 to 0.0.2-next.2 + * @twin.org/api-core bumped from 0.0.2-next.1 to 0.0.2-next.2 + * @twin.org/api-models bumped from 0.0.2-next.1 to 0.0.2-next.2 + +## [0.0.2-next.1](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.2-next.0...api-auth-entity-storage-service-v0.0.2-next.1) (2025-07-08) + + +### Features + +* add json-ld mime type processor and auth admin component ([8861791](https://github.com/iotaledger/twin-api/commit/88617916e23bfbca023dbae1976fe421983a02ff)) +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.2-next.0 to 0.0.2-next.1 + * @twin.org/api-core bumped from 0.0.2-next.0 to 0.0.2-next.1 + * @twin.org/api-models bumped from 0.0.2-next.0 to 0.0.2-next.1 + +## 0.0.1 (2025-07-03) + + +### Features + +* release to production ([70ee2d5](https://github.com/iotaledger/twin-api/commit/70ee2d56a1dc9537d7c9c154d4cb78a235678a3a)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from ^0.0.0 to ^0.0.1 + * @twin.org/api-core bumped from ^0.0.0 to ^0.0.1 + * @twin.org/api-models bumped from ^0.0.0 to ^0.0.1 + +## [0.0.1-next.36](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.1-next.35...api-auth-entity-storage-service-v0.0.1-next.36) (2025-06-17) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.35 to 0.0.1-next.36 + * @twin.org/api-core bumped from 0.0.1-next.35 to 0.0.1-next.36 + * @twin.org/api-models bumped from 0.0.1-next.35 to 0.0.1-next.36 + +## [0.0.1-next.35](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.1-next.34...api-auth-entity-storage-service-v0.0.1-next.35) (2025-06-11) + + +### Features + +* update dependencies ([1171dc4](https://github.com/iotaledger/twin-api/commit/1171dc416a9481737f6a640e3cf30145768f37e9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.34 to 0.0.1-next.35 + * @twin.org/api-core bumped from 0.0.1-next.34 to 0.0.1-next.35 + * @twin.org/api-models bumped from 0.0.1-next.34 to 0.0.1-next.35 + +## [0.0.1-next.34](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.1-next.33...api-auth-entity-storage-service-v0.0.1-next.34) (2025-05-27) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.33 to 0.0.1-next.34 + * @twin.org/api-core bumped from 0.0.1-next.33 to 0.0.1-next.34 + * @twin.org/api-models bumped from 0.0.1-next.33 to 0.0.1-next.34 + +## [0.0.1-next.33](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.1-next.32...api-auth-entity-storage-service-v0.0.1-next.33) (2025-04-17) + + +### Features + +* use shared store mechanism ([#19](https://github.com/iotaledger/twin-api/issues/19)) ([32116df](https://github.com/iotaledger/twin-api/commit/32116df3b4380a30137f5056f242a5c99afa2df9)) + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.32 to 0.0.1-next.33 + * @twin.org/api-core bumped from 0.0.1-next.32 to 0.0.1-next.33 + * @twin.org/api-models bumped from 0.0.1-next.32 to 0.0.1-next.33 + +## [0.0.1-next.32](https://github.com/iotaledger/twin-api/compare/api-auth-entity-storage-service-v0.0.1-next.31...api-auth-entity-storage-service-v0.0.1-next.32) (2025-03-28) + + +### Miscellaneous Chores + +* **api-auth-entity-storage-service:** Synchronize repo versions + + +### Dependencies + +* The following workspace dependencies were updated + * dependencies + * @twin.org/api-auth-entity-storage-models bumped from 0.0.1-next.31 to 0.0.1-next.32 + * @twin.org/api-core bumped from 0.0.1-next.31 to 0.0.1-next.32 + * @twin.org/api-models bumped from 0.0.1-next.31 to 0.0.1-next.32 + +## v0.0.1-next.31 + +- Initial Release diff --git a/packages/api-auth-service/docs/examples.md b/packages/api-auth-service/docs/examples.md new file mode 100644 index 0000000..6b41616 --- /dev/null +++ b/packages/api-auth-service/docs/examples.md @@ -0,0 +1,178 @@ +# Auth Entity Storage Service Examples + +These snippets show how to wire authentication services into your component container and apply token processing in request pipelines. + +## EntityStorageAuthenticationAdminService + +```typescript +import { EntityStorageAuthenticationAdminService } from '@twin.org/api-auth-entity-storage-service'; + +const adminService = new EntityStorageAuthenticationAdminService(); + +console.log(adminService.className()); // EntityStorageAuthenticationAdminService + +await adminService.create({ + email: 'owner@example.org', + password: 'StartPassword123', + userIdentity: 'did:example:owner', + organizationIdentity: 'did:example:org', + scope: ['admin'] +}); + +await adminService.update({ + email: 'owner@example.org', + scope: ['admin', 'security'] +}); + +await adminService.updatePassword('owner@example.org', 'StartPassword124', 'StartPassword123'); + +const fromIdentity = await adminService.getByIdentity('did:example:owner'); +console.log(fromIdentity.scope.length); // 2 +``` + +```typescript +import { EntityStorageAuthenticationAdminService } from '@twin.org/api-auth-entity-storage-service'; + +const adminService = new EntityStorageAuthenticationAdminService(); +const user = await adminService.get('owner@example.org'); +await adminService.remove(user.email); + +console.log(user.email); // owner@example.org +``` + +## EntityStorageAuthenticationService + +```typescript +import { EntityStorageAuthenticationService } from '@twin.org/api-auth-entity-storage-service'; + +const authService = new EntityStorageAuthenticationService(); + +await authService.start('default'); +console.log(authService.className()); // EntityStorageAuthenticationService + +const loginResult = await authService.login('alice@example.org', 'correct-horse-battery-staple'); + +const refreshResult = await authService.refresh(loginResult.token); + +await authService.updatePassword('correct-horse-battery-staple', 'correct-horse-battery-staple-2'); + +await authService.logout(refreshResult.token); +console.log(refreshResult.expiry > 0); // true +``` + +## EntityStorageAuthenticationRateService + +```typescript +import { EntityStorageAuthenticationRateService } from '@twin.org/api-auth-entity-storage-service'; +import { BaseError, GeneralError } from '@twin.org/core'; +import { TooManyRequestsError } from '@twin.org/api-models'; + +const rateService = new EntityStorageAuthenticationRateService({ + config: { + cleanupIntervalMinutes: 5 + } +}); + +console.log(rateService.className()); // EntityStorageAuthenticationRateService + +await rateService.registerAction('login', { + maxAttempts: 3, + windowMinutes: 15 +}); + +await rateService.start('default'); + +await rateService.check('login', 'alice@example.org'); +await rateService.check('login', 'alice@example.org'); +await rateService.check('login', 'alice@example.org'); + +try { + await rateService.check('login', 'alice@example.org'); +} catch (error) { + if (BaseError.isErrorName(error, TooManyRequestsError.CLASS_NAME)) { + const tooMany = error as TooManyRequestsError; + console.log(tooMany.properties?.retryAfterSeconds); // 900 + console.log(tooMany.properties?.nextRequestTime); // 2026-04-13T10:15:00.000Z + } +} + +await rateService.clear('login', 'alice@example.org'); +await rateService.unregisterAction('login'); + +try { + await rateService.check('login', 'alice@example.org'); +} catch (error) { + if (BaseError.isErrorName(error, GeneralError.CLASS_NAME)) { + console.log((error as GeneralError).name); // GeneralError + } +} + +await rateService.stop('default'); +``` + +## EntityStorageAuthenticationAuditService + +```typescript +import { EntityStorageAuthenticationAuditService } from '@twin.org/api-auth-entity-storage-service'; + +const auditService = new EntityStorageAuthenticationAuditService({ + config: { + ipHashSalt: 'StrongServerSideSaltForAuditHashing123' + } +}); + +console.log(auditService.className()); // EntityStorageAuthenticationAuditService + +const createdAuditId = await auditService.create({ + event: 'login-failure', + actorId: 'did:example:user:alice', + nodeId: 'did:example:node:eu-west-1', + organizationId: 'did:example:org:core', + tenantId: 'did:example:tenant:alpha', + data: { + reason: 'invalid-password' + } +}); + +const auditPage = await auditService.query( + { + actorId: 'did:example:user:alice', + event: 'login-failure', + startDate: '2026-04-01T00:00:00.000Z', + endDate: '2026-04-30T23:59:59.999Z' + }, + undefined, + 50 +); + +console.log(createdAuditId); // 018f2f67bb9d4a0caad8386f56df85ce +console.log(auditPage.entries.length); // 1 +console.log(auditPage.cursor); // eyJpZCI6IjAxOGYyZjY3YmI5ZDRhMGNhYWQ4Mzg2ZjU2ZGY4NWNlIn0= +``` + +## AuthHeaderProcessor + +```typescript +import { AuthHeaderProcessor } from '@twin.org/api-auth-entity-storage-service'; + +const processor = new AuthHeaderProcessor(); + +await processor.start('default'); +console.log(processor.className()); // AuthHeaderProcessor + +const request = { + method: 'get', + url: '/info', + headers: { + authorization: 'Bearer token-value' + } +}; + +const response = {}; +const contextIds = {}; +const processorState: { [id: string]: unknown } = {}; + +await processor.pre(request, response, { skipAuth: false }, contextIds, processorState); + +await processor.post(request, response, { skipAuth: false }, contextIds, processorState); +``` diff --git a/packages/api-auth-service/docs/open-api/spec.json b/packages/api-auth-service/docs/open-api/spec.json new file mode 100644 index 0000000..e3fd182 --- /dev/null +++ b/packages/api-auth-service/docs/open-api/spec.json @@ -0,0 +1,911 @@ +{ + "openapi": "3.1.1", + "info": { + "title": "TWIN - Test Endpoints", + "description": "REST API for TWIN - Test Endpoints.", + "version": "1.0.0", + "license": { + "name": "Apache 2.0 License", + "url": "https://opensource.org/licenses/Apache-2.0" + } + }, + "servers": [ + { + "url": "https://localhost" + } + ], + "tags": [ + { + "name": "Authentication Audit", + "description": "Authentication audit endpoints for the REST server." + } + ], + "paths": { + "/authentication/audit": { + "post": { + "operationId": "authenticationAuditCreate", + "summary": "Create an authentication audit entry", + "tags": [ + "Authentication Audit" + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Create an authentication audit entry.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditCreateRequest" + }, + "examples": { + "authenticationAuditCreateRequestExample": { + "summary": "The request to create an authentication audit entry.", + "value": { + "actorId": "user@example.com", + "event": "login-success", + "data": { + "organizationIdentity": "did:example:org1" + } + } + } + } + } + } + }, + "responses": { + "201": { + "description": "The rest request ended in created response.", + "headers": { + "location": { + "schema": { + "type": "string" + }, + "description": "e.g. 018f0b53d5d5704fa3a06d6ed2478575" + } + } + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + }, + "get": { + "operationId": "authenticationAuditQuery", + "summary": "Query authentication audit entries", + "tags": [ + "Authentication Audit" + ], + "parameters": [ + { + "name": "actorId", + "description": "The actor identifier to filter by.", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "example": "user@example.com" + }, + { + "name": "organizationId", + "description": "The organisation identifier to filter by.", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "tenantId", + "description": "The tenant identifier to filter by.", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "nodeId", + "description": "The node identifier to filter by.", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "event", + "description": "The event to filter by.", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AuthAuditEvent" + }, + { + "type": "string" + } + ] + }, + "example": "login-success" + }, + { + "name": "startDate", + "description": "The inclusive start date for filtering, in ISO 8601 format.", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "example": "2026-01-01T00:00:00.000Z" + }, + { + "name": "endDate", + "description": "The inclusive end date for filtering, in ISO 8601 format.", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "example": "2026-01-31T23:59:59.999Z" + }, + { + "name": "cursor", + "description": "The pagination cursor.", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "description": "The maximum number of results to return.", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "example": "50" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "responses": { + "200": { + "description": "Response from querying authentication audit entries.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditQueryResponse" + }, + "examples": { + "authenticationAuditQueryResponseExample": { + "summary": "The response for querying authentication audit entries.", + "value": { + "entries": [ + { + "id": "018f0b53d5d5704fa3a06d6ed2478575", + "actorId": "user@example.com", + "dateCreated": "2026-01-12T09:05:23.123Z", + "event": "login-success", + "data": { + "organizationIdentity": "did:example:org1" + } + } + ], + "cursor": "next-cursor" + } + } + } + } + } + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/audit/{id}": { + "get": { + "operationId": "authenticationAuditGet", + "summary": "Get an authentication audit entry", + "tags": [ + "Authentication Audit" + ], + "parameters": [ + { + "name": "id", + "description": "The audit entry id.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "018f0b53d5d5704fa3a06d6ed2478575" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "responses": { + "200": { + "description": "Response from getting an authentication audit entry.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationAuditEntry" + }, + "examples": { + "authenticationAuditGetResponseExample": { + "summary": "The response for getting an authentication audit entry.", + "value": { + "id": "018f0b53d5d5704fa3a06d6ed2478575", + "actorId": "user@example.com", + "dateCreated": "2026-01-12T09:05:23.123Z", + "event": "login-success", + "data": { + "organizationIdentity": "did:example:org1" + } + } + } + } + } + } + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + }, + "put": { + "operationId": "authenticationAuditUpdate", + "summary": "Update an authentication audit entry", + "tags": [ + "Authentication Audit" + ], + "parameters": [ + { + "name": "id", + "description": "The audit entry id.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "018f0b53d5d5704fa3a06d6ed2478575" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Update an authentication audit entry.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditUpdateRequest" + }, + "examples": { + "authenticationAuditUpdateRequestExample": { + "summary": "The request to update an authentication audit entry.", + "value": { + "data": { + "organizationIdentity": "did:example:org1" + } + } + } + } + } + } + }, + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + }, + "delete": { + "operationId": "authenticationAuditRemove", + "summary": "Remove an authentication audit entry", + "tags": [ + "Authentication Audit" + ], + "parameters": [ + { + "name": "id", + "description": "The audit entry id.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "018f0b53d5d5704fa3a06d6ed2478575" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AuditCreateRequest": { + "description": "The body of the request.", + "type": "object", + "properties": { + "event": { + "anyOf": [ + { + "$ref": "#/components/schemas/AuthAuditEvent" + }, + { + "type": "string" + } + ], + "description": "The audit event that occurred." + }, + "actorId": { + "type": "string", + "description": "The actor identifier, could be e-mail, username, or other unique identifier." + }, + "nodeId": { + "type": "string", + "description": "The node identifier associated with the audit entry, if applicable." + }, + "organizationId": { + "type": "string", + "description": "The organization identifier associated with the audit entry, if applicable." + }, + "tenantId": { + "type": "string", + "description": "The tenant identifier associated with the audit entry, if applicable." + }, + "ipAddressHashes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The hashed IP addresses of the client." + }, + "userAgent": { + "type": "string", + "description": "The user agent string of the client." + }, + "correlationId": { + "type": "string", + "description": "The correlation ID for request tracing." + }, + "data": { + "description": "Additional data related to the audit entry, such as IP address, user agent, etc." + } + }, + "required": [ + "event" + ] + }, + "AuditQueryResponse": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AuthenticationAuditEntry" + }, + "description": "The returned audit entries." + }, + "cursor": { + "type": "string", + "description": "The cursor to retrieve the next page, if any." + } + }, + "required": [ + "entries" + ], + "description": "The response body." + }, + "AuditUpdateRequest": { + "description": "The body of the request.", + "type": "object", + "properties": { + "event": { + "anyOf": [ + { + "$ref": "#/components/schemas/AuthAuditEvent" + }, + { + "type": "string" + } + ], + "description": "The audit event that occurred." + }, + "actorId": { + "type": "string", + "description": "The actor identifier, could be e-mail, username, or other unique identifier." + }, + "nodeId": { + "type": "string", + "description": "The node identifier associated with the audit entry, if applicable." + }, + "organizationId": { + "type": "string", + "description": "The organization identifier associated with the audit entry, if applicable." + }, + "tenantId": { + "type": "string", + "description": "The tenant identifier associated with the audit entry, if applicable." + }, + "ipAddressHashes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The hashed IP addresses of the client." + }, + "userAgent": { + "type": "string", + "description": "The user agent string of the client." + }, + "correlationId": { + "type": "string", + "description": "The correlation ID for request tracing." + }, + "data": { + "description": "Additional data related to the audit entry, such as IP address, user agent, etc." + } + } + }, + "AuthAuditEvent": { + "description": "Supported authentication audit event values.", + "anyOf": [ + { + "const": "login-success", + "description": "Login success." + }, + { + "const": "login-failure", + "description": "Login failure." + }, + { + "const": "logout", + "description": "Logout." + }, + { + "const": "token-refreshed", + "description": "Token refreshed." + }, + { + "const": "account-created", + "description": "Account created." + }, + { + "const": "account-deleted", + "description": "Account deleted." + }, + { + "const": "account-updated", + "description": "Account updated." + }, + { + "const": "account-locked", + "description": "Account locked." + }, + { + "const": "account-unlocked", + "description": "Account unlocked." + }, + { + "const": "password-changed", + "description": "Password changed." + } + ] + }, + "AuthenticationAuditEntry": { + "description": "Contract definition for authentication audit entry.", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the audit entry." + }, + "event": { + "anyOf": [ + { + "$ref": "#/components/schemas/AuthAuditEvent" + }, + { + "type": "string" + } + ], + "description": "The audit event that occurred." + }, + "dateCreated": { + "type": "string", + "description": "The timestamp of the audit entry in ISO 8601 format." + }, + "actorId": { + "type": "string", + "description": "The actor identifier, could be e-mail, username, or other unique identifier." + }, + "nodeId": { + "type": "string", + "description": "The node identifier associated with the audit entry, if applicable." + }, + "organizationId": { + "type": "string", + "description": "The organization identifier associated with the audit entry, if applicable." + }, + "tenantId": { + "type": "string", + "description": "The tenant identifier associated with the audit entry, if applicable." + }, + "ipAddressHashes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The hashed IP addresses of the client." + }, + "userAgent": { + "type": "string", + "description": "The user agent string of the client." + }, + "correlationId": { + "type": "string", + "description": "The correlation ID for request tracing." + }, + "data": { + "description": "Additional data related to the audit entry, such as IP address, user agent, etc." + } + }, + "required": [ + "id", + "event", + "dateCreated" + ] + }, + "Error": { + "description": "Model to describe serialized error.", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name for the error." + }, + "message": { + "type": "string", + "description": "The message for the error as an i18n key." + }, + "source": { + "type": "string", + "description": "The source of the error." + }, + "properties": { + "type": "object", + "additionalProperties": {}, + "description": "Any additional information for the error." + }, + "stack": { + "type": "string", + "description": "The stack trace for the error." + }, + "cause": { + "$ref": "#/components/schemas/Error" + } + }, + "required": [ + "name", + "message" + ] + } + }, + "securitySchemes": { + "jwtBearerAuthScheme": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + } +} diff --git a/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationAuditEntry.md b/packages/api-auth-service/docs/reference/classes/AuthenticationAuditEntry.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationAuditEntry.md rename to packages/api-auth-service/docs/reference/classes/AuthenticationAuditEntry.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationRateEntry.md b/packages/api-auth-service/docs/reference/classes/AuthenticationRateEntry.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationRateEntry.md rename to packages/api-auth-service/docs/reference/classes/AuthenticationRateEntry.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/classes/EntityStorageAuthenticationAuditService.md b/packages/api-auth-service/docs/reference/classes/EntityStorageAuthenticationAuditService.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/classes/EntityStorageAuthenticationAuditService.md rename to packages/api-auth-service/docs/reference/classes/EntityStorageAuthenticationAuditService.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/classes/EntityStorageAuthenticationRateService.md b/packages/api-auth-service/docs/reference/classes/EntityStorageAuthenticationRateService.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/classes/EntityStorageAuthenticationRateService.md rename to packages/api-auth-service/docs/reference/classes/EntityStorageAuthenticationRateService.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditCreate.md b/packages/api-auth-service/docs/reference/functions/authenticationAuditCreate.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditCreate.md rename to packages/api-auth-service/docs/reference/functions/authenticationAuditCreate.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditGet.md b/packages/api-auth-service/docs/reference/functions/authenticationAuditGet.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditGet.md rename to packages/api-auth-service/docs/reference/functions/authenticationAuditGet.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditQuery.md b/packages/api-auth-service/docs/reference/functions/authenticationAuditQuery.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditQuery.md rename to packages/api-auth-service/docs/reference/functions/authenticationAuditQuery.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditRemove.md b/packages/api-auth-service/docs/reference/functions/authenticationAuditRemove.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditRemove.md rename to packages/api-auth-service/docs/reference/functions/authenticationAuditRemove.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditUpdate.md b/packages/api-auth-service/docs/reference/functions/authenticationAuditUpdate.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/functions/authenticationAuditUpdate.md rename to packages/api-auth-service/docs/reference/functions/authenticationAuditUpdate.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/functions/generateRestRoutesAuthenticationAudit.md b/packages/api-auth-service/docs/reference/functions/generateRestRoutesAuthenticationAudit.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/functions/generateRestRoutesAuthenticationAudit.md rename to packages/api-auth-service/docs/reference/functions/generateRestRoutesAuthenticationAudit.md diff --git a/packages/api-auth-service/docs/reference/functions/initSchema.md b/packages/api-auth-service/docs/reference/functions/initSchema.md new file mode 100644 index 0000000..d13a5f3 --- /dev/null +++ b/packages/api-auth-service/docs/reference/functions/initSchema.md @@ -0,0 +1,9 @@ +# Function: initSchema() + +> **initSchema**(): `void` + +Initialize the schema for the authentication service. + +## Returns + +`void` diff --git a/packages/api-auth-service/docs/reference/index.md b/packages/api-auth-service/docs/reference/index.md new file mode 100644 index 0000000..6cece89 --- /dev/null +++ b/packages/api-auth-service/docs/reference/index.md @@ -0,0 +1,30 @@ +# @twin.org/api-auth-service + +## Classes + +- [AuthenticationAuditEntry](classes/AuthenticationAuditEntry.md) +- [AuthenticationRateEntry](classes/AuthenticationRateEntry.md) +- [EntityStorageAuthenticationAuditService](classes/EntityStorageAuthenticationAuditService.md) +- [EntityStorageAuthenticationRateService](classes/EntityStorageAuthenticationRateService.md) + +## Interfaces + +- [IEntityStorageAuthenticationAuditServiceConfig](interfaces/IEntityStorageAuthenticationAuditServiceConfig.md) +- [IEntityStorageAuthenticationAuditServiceConstructorOptions](interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md) +- [IEntityStorageAuthenticationRateServiceConfig](interfaces/IEntityStorageAuthenticationRateServiceConfig.md) +- [IEntityStorageAuthenticationRateServiceConstructorOptions](interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md) + +## Variables + +- [restEntryPoints](variables/restEntryPoints.md) +- [tagsAuthenticationAudit](variables/tagsAuthenticationAudit.md) + +## Functions + +- [generateRestRoutesAuthenticationAudit](functions/generateRestRoutesAuthenticationAudit.md) +- [authenticationAuditCreate](functions/authenticationAuditCreate.md) +- [authenticationAuditGet](functions/authenticationAuditGet.md) +- [authenticationAuditUpdate](functions/authenticationAuditUpdate.md) +- [authenticationAuditRemove](functions/authenticationAuditRemove.md) +- [authenticationAuditQuery](functions/authenticationAuditQuery.md) +- [initSchema](functions/initSchema.md) diff --git a/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md b/packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md rename to packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md b/packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md rename to packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md b/packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md rename to packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md diff --git a/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md b/packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md rename to packages/api-auth-service/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md diff --git a/packages/api-auth-service/docs/reference/variables/restEntryPoints.md b/packages/api-auth-service/docs/reference/variables/restEntryPoints.md new file mode 100644 index 0000000..0dce4ee --- /dev/null +++ b/packages/api-auth-service/docs/reference/variables/restEntryPoints.md @@ -0,0 +1,5 @@ +# Variable: restEntryPoints + +> `const` **restEntryPoints**: `IRestRouteEntryPoint`[] + +REST entry points for the authentication, authentication admin, and authentication audit services. diff --git a/packages/api-auth-entity-storage-service/docs/reference/variables/tagsAuthenticationAudit.md b/packages/api-auth-service/docs/reference/variables/tagsAuthenticationAudit.md similarity index 100% rename from packages/api-auth-entity-storage-service/docs/reference/variables/tagsAuthenticationAudit.md rename to packages/api-auth-service/docs/reference/variables/tagsAuthenticationAudit.md diff --git a/packages/api-auth-service/locales/en.json b/packages/api-auth-service/locales/en.json new file mode 100644 index 0000000..88f5fb8 --- /dev/null +++ b/packages/api-auth-service/locales/en.json @@ -0,0 +1,17 @@ +{ + "error": { + "entityStorageAuthenticationAuditService": { + "auditEntryNotFound": "The audit entry with the specified identifier could not be found \"{notFoundId}\"", + "getAuditEntryFailed": "Getting the audit entry failed", + "updateAuditEntryFailed": "Updating the audit entry failed", + "removeAuditEntryFailed": "Removing the audit entry failed" + }, + "entityStorageAuthenticationRateService": { + "actionConfigMissing": "No rate-limit configuration exists for action \"{action}\".", + "rateLimitExceeded": "The rate limit for action \"{action}\" has been exceeded. Retry after {retryAfterSeconds} seconds." + }, + "validation": { + "saltEntropyTooLow": "Salt must have at least 8 unique characters for sufficient entropy." + } + } +} diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json new file mode 100644 index 0000000..51cce61 --- /dev/null +++ b/packages/api-auth-service/package.json @@ -0,0 +1,97 @@ +{ + "name": "@twin.org/api-auth-service", + "version": "0.9.2-next.9", + "description": "Authentication service implementation and REST routes backed by entity storage.", + "repository": { + "type": "git", + "url": "git+https://github.com/iotaledger/twin-api-auth.git", + "directory": "packages/api-auth-service" + }, + "author": "martyn.janes@iota.org", + "license": "Apache-2.0", + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "scripts": { + "clean": "rimraf dist coverage", + "build": "tspc", + "dev": "nodemon --watch src --ext ts --exec \"npm run build\"", + "test": "vitest --run --config ./vitest.config.ts --no-cache", + "test:build": "tspc -p ./tests/tsconfig.json --noEmit", + "validate-locales": "validate-locales", + "test:coverage": "vitest --run --coverage --config ./vitest.config.ts --no-cache", + "docs:clean": "rimraf docs/reference", + "docs:generate": "typedoc --options typedoc.config.mjs", + "docs:api": "ts-to-openapi ./ts-to-openapi.json ./docs/open-api/spec.json", + "docs": "npm run docs:clean && npm run docs:generate && npm run docs:api", + "dist": "npm run clean && npm run build && npm run validate-locales && npm run test:build && npm run test", + "dist:no-test": "npm run clean && npm run build && npm run test:build" + }, + "dependencies": { + "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-core": "next", + "@twin.org/api-models": "next", + "@twin.org/background-task-models": "next", + "@twin.org/context": "next", + "@twin.org/core": "next", + "@twin.org/crypto": "next", + "@twin.org/entity": "next", + "@twin.org/entity-storage-models": "next", + "@twin.org/logging-models": "next", + "@twin.org/nameof": "next", + "@twin.org/vault-models": "next", + "@twin.org/web": "next" + }, + "devDependencies": { + "@twin.org/entity-storage-connector-memory": "next", + "@twin.org/nameof-transformer": "next", + "@twin.org/nameof-vitest-plugin": "next", + "@twin.org/ts-to-openapi": "next", + "@twin.org/validate-locales": "next", + "@types/node": "26.1.2", + "@vitest/coverage-v8": "4.1.10", + "copyfiles": "2.4.1", + "nodemon": "3.1.14", + "rimraf": "6.1.3", + "ts-patch": "4.0.1", + "typedoc": "0.28.20", + "typedoc-plugin-markdown": "4.12.0", + "typescript": "6.0.3", + "vitest": "4.1.10" + }, + "main": "./dist/es/index.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/es/index.js", + "default": "./dist/es/index.js" + } + }, + "files": [ + "dist/es", + "dist/types", + "locales", + "docs" + ], + "keywords": [ + "twin", + "trade", + "iota", + "framework", + "blockchain", + "api", + "entity", + "storage", + "persistence", + "database", + "service", + "microservice", + "business-logic" + ], + "bugs": { + "url": "git+https://github.com/iotaledger/twin-api-auth/issues" + }, + "homepage": "https://twindev.org" +} diff --git a/packages/api-auth-entity-storage-service/src/entities/authenticationAuditEntry.ts b/packages/api-auth-service/src/entities/authenticationAuditEntry.ts similarity index 100% rename from packages/api-auth-entity-storage-service/src/entities/authenticationAuditEntry.ts rename to packages/api-auth-service/src/entities/authenticationAuditEntry.ts diff --git a/packages/api-auth-entity-storage-service/src/entities/authenticationRateEntry.ts b/packages/api-auth-service/src/entities/authenticationRateEntry.ts similarity index 100% rename from packages/api-auth-entity-storage-service/src/entities/authenticationRateEntry.ts rename to packages/api-auth-service/src/entities/authenticationRateEntry.ts diff --git a/packages/api-auth-service/src/index.ts b/packages/api-auth-service/src/index.ts new file mode 100644 index 0000000..9c785c4 --- /dev/null +++ b/packages/api-auth-service/src/index.ts @@ -0,0 +1,13 @@ +// Copyright 2024 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +export * from "./entities/authenticationAuditEntry.js"; +export * from "./entities/authenticationRateEntry.js"; +export * from "./models/IEntityStorageAuthenticationAuditServiceConfig.js"; +export * from "./models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js"; +export * from "./models/IEntityStorageAuthenticationRateServiceConfig.js"; +export * from "./models/IEntityStorageAuthenticationRateServiceConstructorOptions.js"; +export * from "./restEntryPoints.js"; +export * from "./routes/entityStorageAuthenticationAuditRoutes.js"; +export * from "./schema.js"; +export * from "./services/entityStorageAuthenticationAuditService.js"; +export * from "./services/entityStorageAuthenticationRateService.js"; diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAuditServiceConfig.ts b/packages/api-auth-service/src/models/IEntityStorageAuthenticationAuditServiceConfig.ts similarity index 100% rename from packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAuditServiceConfig.ts rename to packages/api-auth-service/src/models/IEntityStorageAuthenticationAuditServiceConfig.ts diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.ts b/packages/api-auth-service/src/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.ts similarity index 100% rename from packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.ts rename to packages/api-auth-service/src/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.ts diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationRateServiceConfig.ts b/packages/api-auth-service/src/models/IEntityStorageAuthenticationRateServiceConfig.ts similarity index 100% rename from packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationRateServiceConfig.ts rename to packages/api-auth-service/src/models/IEntityStorageAuthenticationRateServiceConfig.ts diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationRateServiceConstructorOptions.ts b/packages/api-auth-service/src/models/IEntityStorageAuthenticationRateServiceConstructorOptions.ts similarity index 100% rename from packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationRateServiceConstructorOptions.ts rename to packages/api-auth-service/src/models/IEntityStorageAuthenticationRateServiceConstructorOptions.ts diff --git a/packages/api-auth-service/src/restEntryPoints.ts b/packages/api-auth-service/src/restEntryPoints.ts new file mode 100644 index 0000000..19b40dc --- /dev/null +++ b/packages/api-auth-service/src/restEntryPoints.ts @@ -0,0 +1,19 @@ +// Copyright 2024 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import type { IRestRouteEntryPoint } from "@twin.org/api-models"; +import { + generateRestRoutesAuthenticationAudit, + tagsAuthenticationAudit +} from "./routes/entityStorageAuthenticationAuditRoutes.js"; + +/** + * REST entry points for the authentication, authentication admin, and authentication audit services. + */ +export const restEntryPoints: IRestRouteEntryPoint[] = [ + { + name: "authenticationAudit", + defaultBaseRoute: "authentication/audit", + tags: tagsAuthenticationAudit, + generateRoutes: generateRestRoutesAuthenticationAudit + } +]; diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAuditRoutes.ts b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts similarity index 95% rename from packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAuditRoutes.ts rename to packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts index 00a551b..da8087b 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAuditRoutes.ts +++ b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts @@ -9,7 +9,7 @@ import type { IAuditRemoveRequest, IAuditUpdateRequest, IAuthenticationAuditComponent -} from "@twin.org/api-auth-entity-storage-models"; +} from "@twin.org/api-auth-models"; import { HttpContextIdKeys, HttpHeaderHelper, @@ -31,6 +31,15 @@ import { HeaderTypes, HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; */ const ROUTES_SOURCE = "authenticationAuditRoutes"; +/** + * The default authorization roles for the routes, use to populate authorization rules. + */ +const PERMISSIONS_DEFAULT_ROUTE_READER = "audit:read"; +const PERMISSIONS_DEFAULT_ROUTE_WRITER = { + permission: "audit:write", + inherits: [PERMISSIONS_DEFAULT_ROUTE_READER] +}; + /** * The tag to associate with the routes. */ @@ -96,7 +105,8 @@ export function generateRestRoutesAuthenticationAudit( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; const getRoute: IRestRoute = { @@ -145,7 +155,8 @@ export function generateRestRoutesAuthenticationAudit( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] }; const updateRoute: IRestRoute = { @@ -182,7 +193,8 @@ export function generateRestRoutesAuthenticationAudit( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; const removeRoute: IRestRoute = { @@ -214,7 +226,8 @@ export function generateRestRoutesAuthenticationAudit( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] }; const queryRoute: IRestRoute = { @@ -272,7 +285,8 @@ export function generateRestRoutesAuthenticationAudit( { type: nameof() } - ] + ], + defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] }; return [createRoute, getRoute, updateRoute, removeRoute, queryRoute]; diff --git a/packages/api-auth-service/src/schema.ts b/packages/api-auth-service/src/schema.ts new file mode 100644 index 0000000..dc78dcf --- /dev/null +++ b/packages/api-auth-service/src/schema.ts @@ -0,0 +1,18 @@ +// Copyright 2024 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { EntitySchemaFactory, EntitySchemaHelper } from "@twin.org/entity"; +import { nameof } from "@twin.org/nameof"; +import { AuthenticationAuditEntry } from "./entities/authenticationAuditEntry.js"; +import { AuthenticationRateEntry } from "./entities/authenticationRateEntry.js"; + +/** + * Initialize the schema for the authentication service. + */ +export function initSchema(): void { + EntitySchemaFactory.register(nameof(), () => + EntitySchemaHelper.getSchema(AuthenticationAuditEntry) + ); + EntitySchemaFactory.register(nameof(), () => + EntitySchemaHelper.getSchema(AuthenticationRateEntry) + ); +} diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAuditService.ts b/packages/api-auth-service/src/services/entityStorageAuthenticationAuditService.ts similarity index 99% rename from packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAuditService.ts rename to packages/api-auth-service/src/services/entityStorageAuthenticationAuditService.ts index 89c08bb..dfcadbf 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAuditService.ts +++ b/packages/api-auth-service/src/services/entityStorageAuthenticationAuditService.ts @@ -4,7 +4,7 @@ import type { AuthAuditEvent, IAuthenticationAuditComponent, IAuthenticationAuditEntry -} from "@twin.org/api-auth-entity-storage-models"; +} from "@twin.org/api-auth-models"; import { HttpContextIdKeys } from "@twin.org/api-models"; import { ContextIdStore, ContextIdKeys } from "@twin.org/context"; import { diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationRateService.ts b/packages/api-auth-service/src/services/entityStorageAuthenticationRateService.ts similarity index 99% rename from packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationRateService.ts rename to packages/api-auth-service/src/services/entityStorageAuthenticationRateService.ts index 6c5cbeb..70bedcd 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationRateService.ts +++ b/packages/api-auth-service/src/services/entityStorageAuthenticationRateService.ts @@ -3,7 +3,7 @@ import type { IAuthenticationRateActionConfig, IAuthenticationRateComponent -} from "@twin.org/api-auth-entity-storage-models"; +} from "@twin.org/api-auth-models"; import { TooManyRequestsError, type IPlatformComponent } from "@twin.org/api-models"; import type { ITaskSchedulerComponent } from "@twin.org/background-task-models"; import { ComponentFactory, Converter, GeneralError, Guards, Is } from "@twin.org/core"; diff --git a/packages/api-auth-entity-storage-service/tests/routes/entityStorageAuthenticationAuditRoutes.spec.ts b/packages/api-auth-service/tests/routes/entityStorageAuthenticationAuditRoutes.spec.ts similarity index 98% rename from packages/api-auth-entity-storage-service/tests/routes/entityStorageAuthenticationAuditRoutes.spec.ts rename to packages/api-auth-service/tests/routes/entityStorageAuthenticationAuditRoutes.spec.ts index 2372df1..afab658 100644 --- a/packages/api-auth-entity-storage-service/tests/routes/entityStorageAuthenticationAuditRoutes.spec.ts +++ b/packages/api-auth-service/tests/routes/entityStorageAuthenticationAuditRoutes.spec.ts @@ -3,7 +3,7 @@ import type { IAuthenticationAuditComponent, IAuthenticationAuditEntry -} from "@twin.org/api-auth-entity-storage-models"; +} from "@twin.org/api-auth-models"; import { ComponentFactory } from "@twin.org/core"; import { HeaderTypes, HttpStatusCode } from "@twin.org/web"; import { diff --git a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAuditService.spec.ts b/packages/api-auth-service/tests/services/entityStorageAuthenticationAuditService.spec.ts similarity index 100% rename from packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAuditService.spec.ts rename to packages/api-auth-service/tests/services/entityStorageAuthenticationAuditService.spec.ts diff --git a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationRateService.spec.ts b/packages/api-auth-service/tests/services/entityStorageAuthenticationRateService.spec.ts similarity index 100% rename from packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationRateService.spec.ts rename to packages/api-auth-service/tests/services/entityStorageAuthenticationRateService.spec.ts diff --git a/packages/api-auth-service/tests/tsconfig.json b/packages/api-auth-service/tests/tsconfig.json new file mode 100644 index 0000000..44d8c8f --- /dev/null +++ b/packages/api-auth-service/tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "rootDir": "..", + "types": ["vitest/globals"] + }, + "include": ["./**/*.ts"] +} diff --git a/packages/api-auth-service/ts-to-openapi.json b/packages/api-auth-service/ts-to-openapi.json new file mode 100644 index 0000000..c3fa898 --- /dev/null +++ b/packages/api-auth-service/ts-to-openapi.json @@ -0,0 +1,14 @@ +{ + "title": "TWIN - Test Endpoints", + "version": "1.0.0", + "description": "REST API for TWIN - Test Endpoints.", + "licenseName": "Apache 2.0 License", + "licenseUrl": "https://opensource.org/licenses/Apache-2.0", + "servers": ["https://localhost"], + "authMethods": ["jwtBearer"], + "restRoutes": [ + { + "packageRoot": "./" + } + ] +} diff --git a/packages/api-auth-service/tsconfig.json b/packages/api-auth-service/tsconfig.json new file mode 100644 index 0000000..a6d6759 --- /dev/null +++ b/packages/api-auth-service/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext", + "target": "ES2022", + "rootDir": "./src", + "outDir": "dist/es", + "strict": true, + "forceConsistentCasingInFileNames": true, + "stripInternal": true, + "noEmitHelpers": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "erasableSyntaxOnly": true, + "sourceMap": true, + "inlineSources": true, + "declaration": true, + "declarationDir": "dist/types", + "types": ["node"], + "plugins": [{ "transform": "@twin.org/nameof-transformer" }], + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/api-auth-service/typedoc.config.mjs b/packages/api-auth-service/typedoc.config.mjs new file mode 100644 index 0000000..aad5b59 --- /dev/null +++ b/packages/api-auth-service/typedoc.config.mjs @@ -0,0 +1,24 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { OptionDefaults } from 'typedoc'; + +const customSchemaTags = ['@json-ld', '@json-schema']; + +export default { + entryPoints: ['src/index.ts'], + out: './docs/reference', + hideGenerator: true, + hideBreadcrumbs: true, + hidePageHeader: true, + excludeExternals: true, + githubPages: false, + disableSources: true, + sort: ['source-order'], + treatWarningsAsErrors: true, + readme: 'none', + entryFileName: 'index', + plugin: ['typedoc-plugin-markdown'], + blockTags: [...new Set([...OptionDefaults.blockTags, ...customSchemaTags])], + excludeTags: customSchemaTags, + useCustomAnchors: true +}; diff --git a/packages/api-auth-service/vitest.config.ts b/packages/api-auth-service/vitest.config.ts new file mode 100644 index 0000000..648b91d --- /dev/null +++ b/packages/api-auth-service/vitest.config.ts @@ -0,0 +1,23 @@ +// Copyright 2025 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { NameOfPlugin } from "@twin.org/nameof-vitest-plugin"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [NameOfPlugin], + test: { + include: ["./tests/**/*.spec.ts"], + globals: true, + testTimeout: 300000, + hookTimeout: 300000, + bail: 1, + reporters: ["verbose"], + disableConsoleIntercept: true, + coverage: { + reporter: ["text", "lcov"], + include: ["src/**/*.ts"], + exclude: ["**/index.ts", "**/models/**/*.ts"] + }, + fileParallelism: false + } +}); diff --git a/release/release-please-config.prerelease.json b/release/release-please-config.prerelease.json index 8a419f0..e09c019 100644 --- a/release/release-please-config.prerelease.json +++ b/release/release-please-config.prerelease.json @@ -10,6 +10,18 @@ "prerelease": true, "prerelease-type": "next", "packages": { + "packages/api-auth-models": { + "package-name": "api-auth-models", + "changelog-path": "docs/changelog.md" + }, + "packages/api-auth-service": { + "package-name": "api-auth-service", + "changelog-path": "docs/changelog.md" + }, + "packages/api-auth-rest-client": { + "package-name": "api-auth-rest-client", + "changelog-path": "docs/changelog.md" + }, "packages/api-auth-entity-storage-models": { "package-name": "api-auth-entity-storage-models", "changelog-path": "docs/changelog.md" @@ -29,6 +41,9 @@ "type": "linked-versions", "groupName": "repo", "components": [ + "api-auth-models", + "api-auth-service", + "api-auth-rest-client", "api-auth-entity-storage-models", "api-auth-entity-storage-service", "api-auth-entity-storage-rest-client" diff --git a/release/release-please-config.prod.json b/release/release-please-config.prod.json index 60bb509..a3271b9 100644 --- a/release/release-please-config.prod.json +++ b/release/release-please-config.prod.json @@ -8,6 +8,18 @@ "initial-version": "0.0.1", "versioning": "default", "packages": { + "packages/api-auth-models": { + "package-name": "api-auth-models", + "changelog-path": "docs/changelog.md" + }, + "packages/api-auth-service": { + "package-name": "api-auth-service", + "changelog-path": "docs/changelog.md" + }, + "packages/api-auth-rest-client": { + "package-name": "api-auth-rest-client", + "changelog-path": "docs/changelog.md" + }, "packages/api-auth-entity-storage-models": { "package-name": "api-auth-entity-storage-models", "changelog-path": "docs/changelog.md" @@ -27,6 +39,9 @@ "type": "linked-versions", "groupName": "repo", "components": [ + "api-auth-models", + "api-auth-service", + "api-auth-rest-client", "api-auth-entity-storage-models", "api-auth-entity-storage-service", "api-auth-entity-storage-rest-client" diff --git a/release/release-please-manifest.prerelease.json b/release/release-please-manifest.prerelease.json index f2186ce..146c72b 100644 --- a/release/release-please-manifest.prerelease.json +++ b/release/release-please-manifest.prerelease.json @@ -1,4 +1,7 @@ { + "packages/api-auth-models": "0.9.2-next.9", + "packages/api-auth-service": "0.9.2-next.9", + "packages/api-auth-rest-client": "0.9.2-next.9", "packages/api-auth-entity-storage-models": "0.9.2-next.9", "packages/api-auth-entity-storage-service": "0.9.2-next.9", "packages/api-auth-entity-storage-rest-client": "0.9.2-next.9" diff --git a/release/release-please-manifest.prod.json b/release/release-please-manifest.prod.json index 2a1d64b..4e403bc 100644 --- a/release/release-please-manifest.prod.json +++ b/release/release-please-manifest.prod.json @@ -1,4 +1,7 @@ { + "packages/api-auth-models": "0.9.1", + "packages/api-auth-service": "0.9.1", + "packages/api-auth-rest-client": "0.9.1", "packages/api-auth-entity-storage-models": "0.9.1", "packages/api-auth-entity-storage-service": "0.9.1", "packages/api-auth-entity-storage-rest-client": "0.9.1" From d331ee3028b2946ee6cd077135b78e1ec4a6466d Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Mon, 17 Aug 2026 16:43:33 +0800 Subject: [PATCH 03/20] feat: authentication user remove roles --- package-lock.json | 14 +- .../src/models/IAuthenticationUser.ts | 5 - ...orageAuthenticationAdminRestClient.spec.ts | 3 +- .../docs/open-api/spec.json | 1277 ----------------- .../src/entities/authenticationUser.ts | 8 +- .../src/entities/authenticationUserV0.ts | 51 + .../src/processors/authHeaderProcessor.ts | 1 - .../entityStorageAuthenticationAdminRoutes.ts | 14 +- .../entityStorageAuthenticationRoutes.ts | 2 +- .../src/schema.ts | 37 +- ...entityStorageAuthenticationAdminService.ts | 36 +- .../entityStorageAuthenticationService.ts | 6 +- .../src/utils/passwordHelper.ts | 1 - .../tests/helpers/tokenHelper.spec.ts | 3 +- .../processors/authHeaderProcessor.spec.ts | 10 +- ...yStorageAuthenticationAdminService.spec.ts | 166 +-- ...entityStorageAuthenticationService.spec.ts | 41 +- packages/api-auth-service/locales/en.json | 5 + packages/api-auth-service/package.json | 1 + packages/api-auth-service/src/index.ts | 3 + .../models/ITenantOverrideProcessorConfig.ts | 17 + ...nantOverrideProcessorConstructorOptions.ts | 25 + .../src/processors/tenantOverrideProcessor.ts | 156 ++ .../entityStorageAuthenticationAuditRoutes.ts | 2 +- .../tenantOverrideProcessor.spec.ts | 264 ++++ 25 files changed, 627 insertions(+), 1521 deletions(-) delete mode 100644 packages/api-auth-entity-storage-service/docs/open-api/spec.json create mode 100644 packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts create mode 100644 packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts create mode 100644 packages/api-auth-service/src/models/ITenantOverrideProcessorConstructorOptions.ts create mode 100644 packages/api-auth-service/src/processors/tenantOverrideProcessor.ts create mode 100644 packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts diff --git a/package-lock.json b/package-lock.json index 7e95e82..3d9942e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2457,9 +2457,9 @@ } }, "node_modules/@twin.org/entity-storage-connector-memory": { - "version": "0.9.2-next.11", - "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-connector-memory/-/entity-storage-connector-memory-0.9.2-next.11.tgz", - "integrity": "sha512-Cwb4ZWjHLHaMTTw3I72kx+XXVoHA7SGnHWodBsVFpbFuPLWwF/S9kkc13jyi70LNq3z4lewu4/Tbf5BElAMLYA==", + "version": "0.9.2-next.12", + "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-connector-memory/-/entity-storage-connector-memory-0.9.2-next.12.tgz", + "integrity": "sha512-Th83PcJXg+8BhgLrj30UiqRevt90pahzr3Yk3bxibmrk7DS9IOMusb5CJc2MyuTLi0MDM7sKtgcJy0fhHi/dzQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2467,7 +2467,7 @@ "@twin.org/context": "next", "@twin.org/core": "next", "@twin.org/entity": "next", - "@twin.org/entity-storage-models": "0.9.2-next.11", + "@twin.org/entity-storage-models": "0.9.2-next.12", "@twin.org/logging-models": "next", "@twin.org/nameof": "next" }, @@ -2476,9 +2476,9 @@ } }, "node_modules/@twin.org/entity-storage-models": { - "version": "0.9.2-next.11", - "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-models/-/entity-storage-models-0.9.2-next.11.tgz", - "integrity": "sha512-0x6yyzPmTo8DwVv7LAPNPawDg3YzKdaDgzhwPVB4xh9aQzUm3+8f6zWAzyZwcBeouACzmfFEmJZ/ellNP1Tp2A==", + "version": "0.9.2-next.12", + "resolved": "https://registry.npmjs.org/@twin.org/entity-storage-models/-/entity-storage-models-0.9.2-next.12.tgz", + "integrity": "sha512-uNipwg9mJTzxKvYgIU2OAgQd4C1/l5nytAE7unfUXsBnDX/8ydZVFqQbOvv0LHQjDWhhN/tYxgt2TIgB3mcl0A==", "license": "Apache-2.0", "dependencies": { "@twin.org/context": "next", diff --git a/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts b/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts index d54e9c2..a8b8e2c 100644 --- a/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts +++ b/packages/api-auth-entity-storage-models/src/models/IAuthenticationUser.ts @@ -19,9 +19,4 @@ export interface IAuthenticationUser { * The users organization. */ organizationIdentity: string; - - /** - * The roles assigned to the user. - */ - roles: string[]; } diff --git a/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts b/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts index 6f2d981..d95c48d 100644 --- a/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts +++ b/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationAdminRestClient.spec.ts @@ -17,8 +17,7 @@ const PREFIX = "authentication/admin"; const TEST_USER = { email: "user@example.com", userIdentity: "did:example:123456789abcdefghi", - organizationIdentity: "did:example:123456789abcdefghi", - roles: ["role1", "role2"] + organizationIdentity: "did:example:123456789abcdefghi" }; const fetchMock = vi.fn(); diff --git a/packages/api-auth-entity-storage-service/docs/open-api/spec.json b/packages/api-auth-entity-storage-service/docs/open-api/spec.json deleted file mode 100644 index 8940500..0000000 --- a/packages/api-auth-entity-storage-service/docs/open-api/spec.json +++ /dev/null @@ -1,1277 +0,0 @@ -{ - "openapi": "3.1.1", - "info": { - "title": "TWIN - Test Endpoints", - "description": "REST API for TWIN - Test Endpoints.", - "version": "1.0.0", - "license": { - "name": "Apache 2.0 License", - "url": "https://opensource.org/licenses/Apache-2.0" - } - }, - "servers": [ - { - "url": "https://localhost" - } - ], - "tags": [ - { - "name": "Authentication", - "description": "Authentication endpoints for the REST server." - }, - { - "name": "Authentication Admin", - "description": "Authentication Admin endpoints for the REST server." - } - ], - "paths": { - "/authentication/login": { - "post": { - "operationId": "authenticationLogin", - "summary": "Login to the server", - "tags": [ - "Authentication" - ], - "requestBody": { - "description": "Perform a login on the server.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LoginRequest" - }, - "examples": { - "loginRequestExample": { - "summary": "The request to login to the server.", - "value": { - "email": "user@example.com", - "password": "MyPassword123!" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Response from a login on the server.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LoginResponse" - }, - "examples": { - "loginResponseExample": { - "summary": "The response for the login request.", - "value": { - "expiry": 1722514341067 - } - } - } - } - } - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - }, - "/authentication/logout": { - "post": { - "operationId": "authenticationLogout", - "summary": "Logout from the server", - "tags": [ - "Authentication" - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "requestBody": { - "description": "Perform a logout on the auth token.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LogoutRequest" - }, - "examples": { - "logoutRequestExample": { - "summary": "The request to logout from the server.", - "value": { - "token": "eyJhbGciOiJIU...sw5c" - } - } - } - } - } - }, - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - }, - "/authentication/refresh": { - "post": { - "operationId": "authenticationRefreshToken", - "summary": "Refresh an authentication token", - "tags": [ - "Authentication" - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "requestBody": { - "description": "Perform a refresh of the auth token.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RefreshTokenRequest" - }, - "examples": { - "refreshTokenRequestExample": { - "summary": "The request to refresh an auth token.", - "value": { - "token": "eyJhbGciOiJIU...sw5c" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Response from a refresh on the auth token.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RefreshTokenResponse" - }, - "examples": { - "refreshTokenResponseExample": { - "summary": "The response for the refresh token request.", - "value": { - "expiry": 1722514341067 - } - } - } - } - } - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - }, - "/authentication/password": { - "put": { - "operationId": "authenticationUpdatePassword", - "summary": "Update the current user's password", - "tags": [ - "Authentication" - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "requestBody": { - "description": "Update the current user's password.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdatePasswordRequest" - }, - "examples": { - "updatePasswordRequestExample": { - "summary": "The request to update the current user's password.", - "value": { - "currentPassword": "MyNewPassword123!", - "newPassword": "MyNewPassword123!" - } - } - } - } - } - }, - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - }, - "/authentication/admin/users": { - "post": { - "operationId": "authenticationAdminCreateUser", - "summary": "Create a new user", - "tags": [ - "Authentication Admin" - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "requestBody": { - "description": "Create a new user as an admin.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AdminUserCreateRequest" - }, - "examples": { - "createUserRequestExample": { - "summary": "The request to create a new user.", - "value": { - "email": "user@example.com", - "password": "MyPassword123!", - "userIdentity": "did:example:123456789abcdefghi", - "organizationIdentity": "did:example:123456789abcdefghi", - "roles": [ - "role1", - "role2" - ] - } - } - } - } - } - }, - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - }, - "/authentication/admin/users/{email}": { - "put": { - "operationId": "authenticationAdminUpdateUser", - "summary": "Update an existing user", - "tags": [ - "Authentication Admin" - ], - "parameters": [ - { - "name": "email", - "description": "The user email.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "user@example.com" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "requestBody": { - "description": "Update a user as an admin.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AdminUserUpdateRequest" - }, - "examples": { - "updateUserRequestExample": { - "summary": "The request to update an existing user.", - "value": { - "userIdentity": "did:example:123456789abcdefghi", - "organizationIdentity": "did:example:123456789abcdefghi", - "roles": [ - "role1", - "role2" - ] - } - } - } - } - } - }, - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - }, - "get": { - "operationId": "authenticationAdminGetUser", - "summary": "Get existing user", - "tags": [ - "Authentication Admin" - ], - "parameters": [ - { - "name": "email", - "description": "The user email.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "user@example.com" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "responses": { - "200": { - "description": "Get a user as an admin.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthenticationUser" - }, - "examples": { - "getUserResponseExample": { - "summary": "The response to get an existing user.", - "value": { - "email": "user@example.com", - "userIdentity": "did:example:123456789abcdefghi", - "organizationIdentity": "did:example:123456789abcdefghi", - "roles": [ - "role1", - "role2" - ] - } - } - } - } - } - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - }, - "delete": { - "operationId": "authenticationAdminRemoveUser", - "summary": "Remove existing user", - "tags": [ - "Authentication Admin" - ], - "parameters": [ - { - "name": "email", - "description": "The user email.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "user@example.com" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - }, - "/authentication/admin/users/{email}/password": { - "put": { - "operationId": "authenticationAdminUpdateUserPassword", - "summary": "Update an existing user password", - "tags": [ - "Authentication Admin" - ], - "parameters": [ - { - "name": "email", - "description": "The user email.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "user@example.com" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "requestBody": { - "description": "Update a users password as an admin.", - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AdminUserUpdatePasswordRequest" - }, - "examples": { - "updateUserPasswordRequestExample": { - "summary": "The request to update an existing user password.", - "value": { - "newPassword": "MyNewPassword123!" - } - } - } - } - } - }, - "responses": { - "204": { - "description": "The rest request ended in success with no data." - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - }, - "/authentication/admin/users/identity/{identity}": { - "get": { - "operationId": "authenticationAdminGetByIdentityUser", - "summary": "Get existing user by identity", - "tags": [ - "Authentication Admin" - ], - "parameters": [ - { - "name": "identity", - "description": "The user identity.", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "style": "simple", - "example": "did:example:123456789abcdefghi" - } - ], - "security": [ - { - "jwtBearerAuthScheme": [] - } - ], - "responses": { - "200": { - "description": "Get a user as an admin.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthenticationUser" - }, - "examples": { - "getUserResponseExample": { - "summary": "The response to get an existing user.", - "value": { - "email": "user@example.com", - "userIdentity": "did:example:123456789abcdefghi", - "organizationIdentity": "did:example:123456789abcdefghi", - "roles": [ - "role1", - "role2" - ] - } - } - } - } - } - }, - "400": { - "description": "The server cannot process the request, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "GeneralError", - "message": "errorMessage", - "properties": { - "foo": "bar" - } - } - } - } - } - } - }, - "401": { - "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "UnauthorizedError", - "message": "errorMessage" - } - } - } - } - } - }, - "500": { - "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - }, - "examples": { - "exampleResponse": { - "value": { - "name": "InternalServerError", - "message": "errorMessage" - } - } - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "AdminUserCreateRequest": { - "type": "object", - "properties": { - "password": { - "type": "string" - } - }, - "required": [ - "password" - ], - "allOf": [ - { - "$ref": "#/components/schemas/AuthenticationUser" - } - ], - "description": "The body of the request." - }, - "AdminUserUpdatePasswordRequest": { - "type": "object", - "properties": { - "newPassword": { - "type": "string", - "description": "The new password for the user." - }, - "currentPassword": { - "type": "string", - "description": "The current password for the user." - } - }, - "required": [ - "newPassword" - ], - "description": "The body of the request." - }, - "AdminUserUpdateRequest": { - "description": "The body of the request.", - "type": "object", - "properties": { - "userIdentity": { - "type": "string", - "description": "The user identity." - }, - "organizationIdentity": { - "type": "string", - "description": "The users organization." - }, - "roles": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The roles assigned to the user." - } - } - }, - "AuthenticationUser": { - "description": "Contract definition for authentication user.", - "type": "object", - "properties": { - "email": { - "type": "string", - "description": "The user e-mail address." - }, - "userIdentity": { - "type": "string", - "description": "The user identity." - }, - "organizationIdentity": { - "type": "string", - "description": "The users organization." - }, - "roles": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The roles assigned to the user." - } - }, - "required": [ - "email", - "userIdentity", - "organizationIdentity", - "roles" - ] - }, - "Error": { - "description": "Model to describe serialized error.", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The name for the error." - }, - "message": { - "type": "string", - "description": "The message for the error as an i18n key." - }, - "source": { - "type": "string", - "description": "The source of the error." - }, - "properties": { - "type": "object", - "additionalProperties": {}, - "description": "Any additional information for the error." - }, - "stack": { - "type": "string", - "description": "The stack trace for the error." - }, - "cause": { - "$ref": "#/components/schemas/Error" - } - }, - "required": [ - "name", - "message" - ] - }, - "LoginRequest": { - "type": "object", - "properties": { - "email": { - "type": "string", - "description": "The email address for the user." - }, - "password": { - "type": "string", - "description": "The password for the user." - } - }, - "required": [ - "email", - "password" - ], - "description": "The login details." - }, - "LoginResponse": { - "type": "object", - "properties": { - "expiry": { - "type": "number", - "description": "The expiry time of the token." - } - }, - "required": [ - "expiry" - ], - "description": "The login response details." - }, - "LogoutRequest": { - "type": "object", - "properties": { - "token": { - "type": "string", - "description": "The token to logout, if it uses a mechanism with public access." - } - }, - "description": "The logout token details." - }, - "RefreshTokenRequest": { - "type": "object", - "properties": { - "token": { - "type": "string", - "description": "The token to refresh, if it uses a mechanism with public access." - } - }, - "description": "The refresh token details." - }, - "RefreshTokenResponse": { - "type": "object", - "properties": { - "expiry": { - "type": "number", - "description": "The expiry time of the token." - } - }, - "required": [ - "expiry" - ], - "description": "The refresh token details." - }, - "UpdatePasswordRequest": { - "type": "object", - "properties": { - "currentPassword": { - "type": "string", - "description": "The current password for the user." - }, - "newPassword": { - "type": "string", - "description": "The new password for the user." - } - }, - "required": [ - "currentPassword", - "newPassword" - ], - "description": "The body of the request." - } - }, - "securitySchemes": { - "jwtBearerAuthScheme": { - "type": "http", - "scheme": "bearer", - "bearerFormat": "JWT" - } - } - } -} diff --git a/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts b/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts index bdf8007..2a9192b 100644 --- a/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts +++ b/packages/api-auth-entity-storage-service/src/entities/authenticationUser.ts @@ -5,7 +5,7 @@ import { entity, property } from "@twin.org/entity"; /** * Class defining the storage for user login credentials. */ -@entity() +@entity({ version: 1 }) export class AuthenticationUser { /** * The user e-mail address. @@ -37,12 +37,6 @@ export class AuthenticationUser { @property({ type: "string" }) public organization!: string; - /** - * The roles assigned to the user, bar separated. - */ - @property({ type: "string" }) - public roles!: string; - /** * The password version counter, incremented on every password change to invalidate existing tokens. */ diff --git a/packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts b/packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts new file mode 100644 index 0000000..767ec93 --- /dev/null +++ b/packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts @@ -0,0 +1,51 @@ +// Copyright 2024 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { entity, property } from "@twin.org/entity"; + +/** + * Class defining the storage for user login credentials. + */ +@entity({ version: 0 }) +export class AuthenticationUserV0 { + /** + * The user e-mail address. + */ + @property({ type: "string", isPrimary: true }) + public email!: string; + + /** + * The encrypted password for the user. + */ + @property({ type: "string" }) + public password!: string; + + /** + * The salt for the password. + */ + @property({ type: "string" }) + public salt!: string; + + /** + * The user identity. + */ + @property({ type: "string", isSecondary: true }) + public identity!: string; + + /** + * The users organization. + */ + @property({ type: "string" }) + public organization!: string; + + /** + * The scope assigned to the user, comma separated. + */ + @property({ type: "string" }) + public scope!: string; + + /** + * The password version counter, incremented on every password change to invalidate existing tokens. + */ + @property({ type: "integer", optional: true }) + public passwordVersion?: number; +} diff --git a/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts b/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts index d1768f0..f470a5d 100644 --- a/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts +++ b/packages/api-auth-entity-storage-service/src/processors/authHeaderProcessor.ts @@ -213,7 +213,6 @@ export class AuthHeaderProcessor implements IBaseRouteProcessor { contextIds[ContextIdKeys.Organization] = tenantOrganizationId ?? this._nodeOrganizationId; contextIds[ContextIdKeys.User] = user?.identity; contextIds[ContextIdKeys.UserOrganization] = user?.organization; - contextIds[HttpContextIdKeys.Roles] = user?.roles; // If the tenant has a custom public origin, we set it in the context for downstream processors to use. if (Is.stringValue(tenantPublicOrigin)) { diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts index 22de5c3..cf0c702 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts @@ -32,7 +32,7 @@ import { HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationAdminRoutes"; /** - * The default authorization roles for the routes, use to populate authorization rules. + * The default authorization permissions for the routes, use to populate authorization rules. */ const PERMISSIONS_DEFAULT_ROUTE_READER = "user-admin:read"; const PERMISSIONS_DEFAULT_ROUTE_WRITER = { @@ -79,8 +79,7 @@ export function generateRestRoutesAuthenticationAdmin( email: "user@example.com", password: "MyPassword123!", userIdentity: "did:example:123456789abcdefghi", - organizationIdentity: "did:example:123456789abcdefghi", - roles: ["role1", "role2"] + organizationIdentity: "did:example:123456789abcdefghi" } } } @@ -117,8 +116,7 @@ export function generateRestRoutesAuthenticationAdmin( }, body: { userIdentity: "did:example:123456789abcdefghi", - organizationIdentity: "did:example:123456789abcdefghi", - roles: ["role1", "role2"] + organizationIdentity: "did:example:123456789abcdefghi" } } } @@ -204,8 +202,7 @@ export function generateRestRoutesAuthenticationAdmin( body: { email: "user@example.com", userIdentity: "did:example:123456789abcdefghi", - organizationIdentity: "did:example:123456789abcdefghi", - roles: ["role1", "role2"] + organizationIdentity: "did:example:123456789abcdefghi" } } } @@ -252,8 +249,7 @@ export function generateRestRoutesAuthenticationAdmin( body: { email: "user@example.com", userIdentity: "did:example:123456789abcdefghi", - organizationIdentity: "did:example:123456789abcdefghi", - roles: ["role1", "role2"] + organizationIdentity: "did:example:123456789abcdefghi" } } } diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index ee00351..31c5ed1 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -27,7 +27,7 @@ import { HttpStatusCode } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationRoutes"; /** - * The default authorization roles for the routes, use to populate authorization rules. + * The default authorization permissions for the routes, use to populate authorization rules. */ const PERMISSIONS_DEFAULT_ROUTE_READER = "user:read"; const PERMISSIONS_DEFAULT_ROUTE_WRITER = { diff --git a/packages/api-auth-entity-storage-service/src/schema.ts b/packages/api-auth-entity-storage-service/src/schema.ts index e87141c..eb787ff 100644 --- a/packages/api-auth-entity-storage-service/src/schema.ts +++ b/packages/api-auth-entity-storage-service/src/schema.ts @@ -1,8 +1,15 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. -import { EntitySchemaFactory, EntitySchemaHelper } from "@twin.org/entity"; +import { SharedStore } from "@twin.org/core"; +import { + EntitySchemaFactory, + EntitySchemaHelper, + type IEntitySchemaProperty +} from "@twin.org/entity"; +import { type ISchemaMigration, SchemaMigrationFactory } from "@twin.org/entity-storage-models"; import { nameof } from "@twin.org/nameof"; import { AuthenticationUser } from "./entities/authenticationUser.js"; +import { AuthenticationUserV0 } from "./entities/authenticationUserV0.js"; /** * Initialize the schema for the authentication service. @@ -11,4 +18,32 @@ export function initSchema(): void { EntitySchemaFactory.register(nameof(), () => EntitySchemaHelper.getSchema(AuthenticationUser) ); + EntitySchemaFactory.register(nameof(), () => + EntitySchemaHelper.getSchema(AuthenticationUserV0) + ); + + const migrationV0V1: ISchemaMigration = { + removeEntityProperty: ( + entity: AuthenticationUserV0, + removedProperties: IEntitySchemaProperty[] + ): void => { + // If the scope property is not being removed, we don't need to do anything + if (removedProperties.find(prop => prop.property === "scope") === undefined) { + return; + } + + // We need to store the old roles in here if a migration is performed + // they will be picked up by the start method of the AuthenticationService + // and used to populate the new roles table + const migratedRoles = SharedStore.get<{ [id: string]: string[] }>("migrationUserRoles") ?? {}; + migratedRoles[entity.identity] = entity.scope + .split(",") + .map(role => role.trim().toLocaleLowerCase()); + SharedStore.set("migrationUserRoles", migratedRoles); + } + }; + SchemaMigrationFactory.register( + `${nameof()}_0_1`, + () => migrationV0V1 as ISchemaMigration + ); } diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts index 15b1c36..9e4e032 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts +++ b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts @@ -6,7 +6,6 @@ import type { } from "@twin.org/api-auth-entity-storage-models"; import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-models"; import { AuthAuditEvent } from "@twin.org/api-auth-models"; -import { RolesHelper } from "@twin.org/api-models"; import { ContextIdKeys, ContextIdStore } from "@twin.org/context"; import { ComponentFactory, @@ -109,11 +108,6 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA nameof(user.organizationIdentity), user.organizationIdentity ); - Guards.array( - EntityStorageAuthenticationAdminService.CLASS_NAME, - nameof(user.roles), - user.roles - ); try { PasswordValidator.validatePassword(user.password, { @@ -136,7 +130,6 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA password: hashedPassword, identity: user.userIdentity, organization: user.organizationIdentity, - roles: RolesHelper.toString(user.roles), passwordVersion: 0 }; @@ -150,8 +143,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA data: { userIdentity: user.userIdentity, organizationIdentity: user.organizationIdentity, - tenantId: requestorTenantId, - roles: user.roles + tenantId: requestorTenantId } }); } catch (error) { @@ -195,13 +187,6 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA user.organizationIdentity ); } - if (!Is.empty(user.roles)) { - Guards.array( - EntityStorageAuthenticationAdminService.CLASS_NAME, - nameof(user.roles), - user.roles - ); - } try { const existingUser = await this._userEntityStorage.get(user.email); @@ -214,9 +199,6 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA } const updatedFields: string[] = []; - const updatedRoles = Is.array(user.roles) - ? RolesHelper.toString(user.roles) - : existingUser.roles; if (user.userIdentity !== undefined && user.userIdentity !== existingUser.identity) { updatedFields.push("userIdentity"); @@ -227,13 +209,9 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA ) { updatedFields.push("organizationIdentity"); } - if (Is.array(user.roles) && updatedRoles !== existingUser.roles) { - updatedFields.push("roles"); - } existingUser.identity = user.userIdentity ?? existingUser.identity; existingUser.organization = user.organizationIdentity ?? existingUser.organization; - existingUser.roles = Is.array(user.roles) ? updatedRoles : existingUser.roles; await this._userEntityStorage.set(existingUser); @@ -246,8 +224,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA updatedFields, userIdentity: existingUser.identity, organizationIdentity: existingUser.organization, - tenantId: requestorTenantId, - roles: RolesHelper.toArray(existingUser.roles) + tenantId: requestorTenantId } }); } catch (error) { @@ -281,8 +258,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA return { email: user.email, userIdentity: user.identity, - organizationIdentity: user.organization, - roles: RolesHelper.toArray(user.roles) + organizationIdentity: user.organization }; } catch (error) { throw new GeneralError( @@ -319,8 +295,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA return { email: user.email, userIdentity: user.identity, - organizationIdentity: user.organization, - roles: RolesHelper.toArray(user.roles) + organizationIdentity: user.organization }; } catch (error) { throw new GeneralError( @@ -360,8 +335,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA data: { userIdentity: user.identity, organizationIdentity: user.organization, - tenantId: requestorTenantId, - roles: RolesHelper.toArray(user.roles) + tenantId: requestorTenantId } }); } catch (error) { diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts index 9bfd3a7..ccadea1 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts +++ b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts @@ -7,7 +7,7 @@ import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-models"; import { AuthAuditEvent } from "@twin.org/api-auth-models"; -import { RolesHelper, type ITenantAdminComponent } from "@twin.org/api-models"; +import type { ITenantAdminComponent } from "@twin.org/api-models"; import { ContextIdHelper, ContextIdKeys, ContextIdStore } from "@twin.org/context"; import { Coerce, @@ -327,8 +327,7 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon data: { userIdentity: loginUser.identity, organizationIdentity: loginUser.organization, - tenantId: loginTenantId, - roles: RolesHelper.toArray(loginUser.roles) + tenantId: loginTenantId } }); @@ -434,7 +433,6 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon data: { organizationIdentity: payloadOrg, tenantId, - roles: RolesHelper.toArray(refreshUser?.roles), version: refreshUser?.passwordVersion ?? 0 } }); diff --git a/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts b/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts index 54e108c..db0440b 100644 --- a/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts +++ b/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts @@ -59,7 +59,6 @@ export class PasswordHelper { password: hashedPassword, identity: user.identity, organization: user.organization, - roles: user.roles, passwordVersion: (user.passwordVersion ?? 0) + 1 }; diff --git a/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts b/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts index fa78c12..e964797 100644 --- a/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/helpers/tokenHelper.spec.ts @@ -388,8 +388,7 @@ describe("TokenHelper", () => { sub: "user123", org: "org456", exp: Math.trunc(Date.now() / 1000) + 3600, - customClaim: "customValue", - roles: ["admin", "editor"] + customClaim: "customValue" }; const token = "custom-claims.jwt.token"; diff --git a/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts b/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts index f4b2a18..d0ccd62 100644 --- a/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/processors/authHeaderProcessor.spec.ts @@ -57,7 +57,6 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -111,7 +110,6 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -210,7 +208,6 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - roles: "user-admin", passwordVersion: 1 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -257,8 +254,7 @@ describe("AuthHeaderProcessor", () => { identity: "did:user:123", organization: "did:org:456", password: "hashed", - salt: "salt", - roles: "user-admin" + salt: "salt" }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ token: "jwt", @@ -299,7 +295,6 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -354,7 +349,6 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -399,7 +393,6 @@ describe("AuthHeaderProcessor", () => { organization: "did:org:456", password: "hashed", salt: "salt", - roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ @@ -452,7 +445,6 @@ describe("AuthHeaderProcessor", () => { organization: USER_ORG, password: "hashed", salt: "salt", - roles: "user-admin", passwordVersion: 0 }); vi.spyOn(TokenHelper, "extractTokenFromHeaders").mockReturnValue({ diff --git a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts index 2460d14..e22c5d2 100644 --- a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationAdminService.spec.ts @@ -58,50 +58,13 @@ describe("EntityStorageAuthenticationAdminService", () => { expect(service.className()).toBe(EntityStorageAuthenticationAdminService.CLASS_NAME); }); - it("should create a new user with normalised roles", async () => { - vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); - vi.spyOn(RandomHelper, "generate").mockReturnValue(new Uint8Array([1, 2, 3, 4])); - vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("hashed-password"); - - await service.create({ - email: "user@example.com", - password: "correct-horse-battery", - userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: [" Read ", "WRITE"] - }); - - expect(PasswordValidator.validatePassword).toHaveBeenCalledWith("correct-horse-battery", { - minLength: 10 - }); - expect(await userEntityStorage.get("user@example.com")).toEqual({ - email: "user@example.com", - salt: "AQIDBA==", - password: "hashed-password", - identity: "did:user:123", - organization: "did:org:456", - roles: "read,write", - passwordVersion: 0 - }); - expect(mockAuthenticationAuditService.create).toHaveBeenCalledWith({ - actorId: "user@example.com", - event: "account-created", - data: { - userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: [" Read ", "WRITE"] - } - }); - }); - it("should wrap create failures when the user already exists", async () => { await userEntityStorage.set({ email: "user@example.com", password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); @@ -110,8 +73,7 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", password: "correct-horse-battery", userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read"] + organizationIdentity: "did:org:456" }) ).rejects.toThrow(GeneralError); expect(await userEntityStorage.get("user@example.com")).toMatchObject({ @@ -129,49 +91,12 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", password: "short", userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read"] + organizationIdentity: "did:org:456" }) ).rejects.toThrow(GeneralError); expect(await userEntityStorage.get("user@example.com")).toBeUndefined(); }); - it("should update an existing user and normalise roles", async () => { - await userEntityStorage.set({ - email: "user@example.com", - password: "stored-password", - salt: "AQIDBA==", - identity: "did:user:123", - organization: "did:org:456", - roles: "read" - }); - - await service.update({ - email: "user@example.com", - organizationIdentity: "did:org:999", - roles: [" Admin ", "WRITE"] - }); - - expect(await userEntityStorage.get("user@example.com")).toEqual({ - email: "user@example.com", - password: "stored-password", - salt: "AQIDBA==", - identity: "did:user:123", - organization: "did:org:999", - roles: "admin,write" - }); - expect(mockAuthenticationAuditService.create).toHaveBeenCalledWith({ - actorId: "user@example.com", - event: "account-updated", - data: { - updatedFields: ["organizationIdentity", "roles"], - userIdentity: "did:user:123", - organizationIdentity: "did:org:999", - roles: ["admin", "write"] - } - }); - }); - it("should wrap update failures when the user is missing", async () => { await expect( service.update({ @@ -188,8 +113,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); const result = await service.get("user@example.com"); @@ -197,8 +121,7 @@ describe("EntityStorageAuthenticationAdminService", () => { expect(result).toEqual({ email: "user@example.com", userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read", "write"] + organizationIdentity: "did:org:456" }); }); @@ -208,8 +131,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); const result = await service.getByIdentity("did:user:123"); @@ -217,8 +139,7 @@ describe("EntityStorageAuthenticationAdminService", () => { expect(result).toEqual({ email: "user@example.com", userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read", "write"] + organizationIdentity: "did:org:456" }); }); @@ -236,8 +157,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); await service.remove("user@example.com"); @@ -248,8 +168,7 @@ describe("EntityStorageAuthenticationAdminService", () => { event: "account-deleted", data: { userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read"] + organizationIdentity: "did:org:456" } }); }); @@ -267,8 +186,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); vi.spyOn(PasswordValidator, "comparePasswordHashes").mockReturnValue(true); @@ -285,7 +203,6 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "new-password-hash", identity: "did:user:123", organization: "did:org:456", - roles: "read,write", passwordVersion: 1 }); expect(mockAuthenticationAuditService.create).toHaveBeenCalledWith({ @@ -304,8 +221,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); const comparePasswordHashesSpy = vi.spyOn(PasswordValidator, "comparePasswordHashes"); @@ -321,7 +237,6 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "new-password-hash", identity: "did:user:123", organization: "did:org:456", - roles: "read,write", passwordVersion: 1 }); }); @@ -332,8 +247,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); vi.spyOn(PasswordValidator, "validatePassword").mockImplementation(() => {}); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("current-password-hash"); @@ -396,8 +310,7 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", password: "correct-horse-battery", userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read"] + organizationIdentity: "did:org:456" }); expect(await userEntityStorage.get("user@example.com")).toEqual({ @@ -406,7 +319,6 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "hashed-password", identity: "did:user:123", organization: "did:org:456", - roles: "read", passwordVersion: 0 }); }); @@ -419,8 +331,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); const result = await service.get("user@example.com"); @@ -428,8 +339,7 @@ describe("EntityStorageAuthenticationAdminService", () => { expect(result).toEqual({ email: "user@example.com", userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read", "write"] + organizationIdentity: "did:org:456" }); }); }); @@ -441,8 +351,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); const result = await service.getByIdentity("did:user:123"); @@ -450,8 +359,7 @@ describe("EntityStorageAuthenticationAdminService", () => { expect(result).toEqual({ email: "user@example.com", userIdentity: "did:user:123", - organizationIdentity: "did:org:456", - roles: ["read", "write"] + organizationIdentity: "did:org:456" }); }); }); @@ -463,19 +371,16 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); await service.update({ email: "user@example.com", - organizationIdentity: "did:org:999", - roles: ["admin"] + organizationIdentity: "did:org:999" }); expect(await userEntityStorage.get("user@example.com")).toMatchObject({ - organization: "did:org:999", - roles: "admin" + organization: "did:org:999" }); }); }); @@ -487,8 +392,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); await service.remove("user@example.com"); @@ -508,8 +412,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read,write" + organization: "did:org:456" }); await service.updatePassword("user@example.com", "better-password-value"); @@ -532,8 +435,7 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", password: "correct-horse-battery", userIdentity: "did:user:tenant-a", - organizationIdentity: "did:org:456", - roles: ["read"] + organizationIdentity: "did:org:456" }); }); @@ -542,19 +444,18 @@ describe("EntityStorageAuthenticationAdminService", () => { email: "user@example.com", password: "correct-horse-battery", userIdentity: "did:user:tenant-b", - organizationIdentity: "did:org:456", - roles: ["write"] + organizationIdentity: "did:org:456" }); }); await ContextIdStore.run({ [ContextIdKeys.Tenant]: TENANT_A }, async () => { const userA = await service.get("user@example.com"); - expect(userA).toMatchObject({ userIdentity: "did:user:tenant-a", roles: ["read"] }); + expect(userA).toMatchObject({ userIdentity: "did:user:tenant-a" }); }); await ContextIdStore.run({ [ContextIdKeys.Tenant]: TENANT_B }, async () => { const userB = await service.get("user@example.com"); - expect(userB).toMatchObject({ userIdentity: "did:user:tenant-b", roles: ["write"] }); + expect(userB).toMatchObject({ userIdentity: "did:user:tenant-b" }); }); }); @@ -565,8 +466,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); }); @@ -582,8 +482,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); }); @@ -599,8 +498,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); }); @@ -624,8 +522,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); }); @@ -647,8 +544,7 @@ describe("EntityStorageAuthenticationAdminService", () => { password: "stored-password", salt: "AQIDBA==", identity: "did:user:123", - organization: "did:org:456", - roles: "read" + organization: "did:org:456" }); }); diff --git a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts index 2348344..e95ec25 100644 --- a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts @@ -98,8 +98,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read,write" + salt: "c2FsdA==" }); vi.spyOn(PasswordGenerator, "hashPassword").mockResolvedValue("generated-password-hash"); vi.spyOn(PasswordValidator, "comparePasswordHashes").mockReturnValue(true); @@ -129,7 +128,6 @@ describe("EntityStorageAuthenticationService", () => { event: "login-success", data: { userIdentity: "did:user:123", - roles: ["read", "write"], organizationIdentity: "did:org:456", tenantId: "tenant-1" } @@ -300,8 +298,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read,write" + salt: "c2FsdA==" }); vi.spyOn(TokenHelper, "verify").mockImplementation( async (vaultConnector, nodeId, signingKeyName, token, verifyUser) => { @@ -347,7 +344,6 @@ describe("EntityStorageAuthenticationService", () => { data: { organizationIdentity: "did:org:456", tenantId: undefined, - roles: ["read", "write"], version: 0 } }); @@ -411,8 +407,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:other", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read,write" + salt: "c2FsdA==" }); const createTokenSpy = vi.spyOn(TokenHelper, "createToken"); vi.spyOn(TokenHelper, "verify").mockImplementation( @@ -448,8 +443,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:other", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read,write" + salt: "c2FsdA==" }); const createTokenSpy = vi.spyOn(TokenHelper, "createToken"); vi.spyOn(TokenHelper, "verify").mockImplementation( @@ -486,8 +480,7 @@ describe("EntityStorageAuthenticationService", () => { organization: "did:org:456", password: "stored-password-hash", salt: "c2FsdA==", - passwordVersion: 2, - roles: "read,write" + passwordVersion: 2 }); const createTokenSpy = vi.spyOn(TokenHelper, "createToken"); vi.spyOn(TokenHelper, "verify").mockImplementation( @@ -520,8 +513,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read,write" + salt: "c2FsdA==" }); vi.spyOn(PasswordHelper, "updatePassword").mockResolvedValue(undefined); @@ -627,8 +619,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read,write" + salt: "c2FsdA==" }); const result = await service.login("user@example.com", "correct-password"); @@ -644,8 +635,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read" + salt: "c2FsdA==" }); }); @@ -672,8 +662,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:tenant-a", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read" + salt: "c2FsdA==" }); }); @@ -683,8 +672,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:tenant-b", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "write" + salt: "c2FsdA==" }); }); @@ -719,8 +707,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read" + salt: "c2FsdA==" }); await service.updatePassword("current-password", "new-password"); @@ -744,8 +731,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read" + salt: "c2FsdA==" }); }); @@ -766,8 +752,7 @@ describe("EntityStorageAuthenticationService", () => { identity: "did:user:123", organization: "did:org:456", password: "stored-password-hash", - salt: "c2FsdA==", - roles: "read" + salt: "c2FsdA==" }); }); diff --git a/packages/api-auth-service/locales/en.json b/packages/api-auth-service/locales/en.json index 88f5fb8..6255647 100644 --- a/packages/api-auth-service/locales/en.json +++ b/packages/api-auth-service/locales/en.json @@ -12,6 +12,11 @@ }, "validation": { "saltEntropyTooLow": "Salt must have at least 8 unique characters for sufficient entropy." + }, + "tenantOverrideProcessor": { + "insufficientRoleForTenantOverride": "Caller does not hold the required role for tenant override.", + "missingUserIdForTenantOverride": "Missing user id for tenant override.", + "tenantNotFound": "Tenant \"{notFoundId}\" not found." } } } diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json index 51cce61..bd90be8 100644 --- a/packages/api-auth-service/package.json +++ b/packages/api-auth-service/package.json @@ -32,6 +32,7 @@ "@twin.org/api-auth-models": "0.9.2-next.9", "@twin.org/api-core": "next", "@twin.org/api-models": "next", + "@twin.org/authorization-models": "next", "@twin.org/background-task-models": "next", "@twin.org/context": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-service/src/index.ts b/packages/api-auth-service/src/index.ts index 9c785c4..c60362c 100644 --- a/packages/api-auth-service/src/index.ts +++ b/packages/api-auth-service/src/index.ts @@ -6,6 +6,9 @@ export * from "./models/IEntityStorageAuthenticationAuditServiceConfig.js"; export * from "./models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js"; export * from "./models/IEntityStorageAuthenticationRateServiceConfig.js"; export * from "./models/IEntityStorageAuthenticationRateServiceConstructorOptions.js"; +export * from "./models/ITenantOverrideProcessorConfig.js"; +export * from "./models/ITenantOverrideProcessorConstructorOptions.js"; +export * from "./processors/tenantOverrideProcessor.js"; export * from "./restEntryPoints.js"; export * from "./routes/entityStorageAuthenticationAuditRoutes.js"; export * from "./schema.js"; diff --git a/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts new file mode 100644 index 0000000..06aa165 --- /dev/null +++ b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts @@ -0,0 +1,17 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. + +/** + * Configuration for the TenantOverrideProcessor. + */ +export interface ITenantOverrideProcessorConfig { + /** + * Include the stack with errors. + */ + includeErrorStack?: boolean; + + /** + * The role value that grants escalated privilege for cross-tenant access. Defaults to "global-admin". + */ + escalatedPrivilegeRole?: string; +} diff --git a/packages/api-auth-service/src/models/ITenantOverrideProcessorConstructorOptions.ts b/packages/api-auth-service/src/models/ITenantOverrideProcessorConstructorOptions.ts new file mode 100644 index 0000000..e6fafb7 --- /dev/null +++ b/packages/api-auth-service/src/models/ITenantOverrideProcessorConstructorOptions.ts @@ -0,0 +1,25 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import type { ITenantOverrideProcessorConfig } from "./ITenantOverrideProcessorConfig.js"; + +/** + * Options for the TenantOverrideProcessor constructor. + */ +export interface ITenantOverrideProcessorConstructorOptions { + /** + * The tenant admin component type. + * @default tenant-admin + */ + tenantAdminComponentType?: string; + + /** + * The authorization component type. + * @default authorization + */ + authorizationComponentType?: string; + + /** + * Configuration for the processor. + */ + config?: ITenantOverrideProcessorConfig; +} diff --git a/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts new file mode 100644 index 0000000..b5d57c8 --- /dev/null +++ b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts @@ -0,0 +1,156 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { + ForbiddenError, + HttpContextIdKeys, + HttpErrorHelper, + type IBaseRoute, + type IBaseRouteProcessor, + type IHttpResponse, + type IHttpServerRequest, + type ITenantAdminComponent +} from "@twin.org/api-models"; +import type { IAuthorizationComponent } from "@twin.org/authorization-models"; +import { ContextIdKeys, type IContextIds } from "@twin.org/context"; +import { BaseError, ComponentFactory, Is, NotFoundError } from "@twin.org/core"; +import { nameof } from "@twin.org/nameof"; +import type { ITenantOverrideProcessorConstructorOptions } from "../models/ITenantOverrideProcessorConstructorOptions.js"; + +/** + * Processes the overrideTenant query parameter for routes that allow tenant override. + * Requires the caller to hold the escalated privilege role. Runs after AuthHeaderProcessor so + * authentication is always resolved in the caller's own partition first. + */ +export class TenantOverrideProcessor implements IBaseRouteProcessor { + /** + * The query parameter name used to supply the override tenant ID. + */ + public static readonly OVERRIDE_TENANT_PARAM: string = "override-tenant"; + + /** + * The role string required to perform a tenant override. + */ + public static readonly DEFAULT_ESCALATED_PRIVILEGE_ROLE: string = "global-admin"; + + /** + * Runtime name for the class. + */ + public static readonly CLASS_NAME: string = nameof(); + + /** + * The entity storage for api keys. + * @internal + */ + private readonly _tenantAdminComponent: ITenantAdminComponent; + + /** + * The authorization component for checking authorization roles. + * @internal + */ + private readonly _authorizationComponent: IAuthorizationComponent; + + /** + * Include the stack with errors. + * @internal + */ + private readonly _includeErrorStack: boolean; + + /** + * The role value that grants cross-tenant access. + * @internal + */ + private readonly _escalatedPrivilegeRole: string; + + /** + * Create a new instance of TenantOverrideProcessor. + * @param options Options for the processor. + */ + constructor(options?: ITenantOverrideProcessorConstructorOptions) { + this._tenantAdminComponent = ComponentFactory.get( + options?.tenantAdminComponentType ?? "tenant-admin" + ); + this._authorizationComponent = ComponentFactory.get( + options?.authorizationComponentType ?? "authorization" + ); + this._includeErrorStack = options?.config?.includeErrorStack ?? false; + this._escalatedPrivilegeRole = + options?.config?.escalatedPrivilegeRole ?? + TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE; + } + + /** + * Returns the class name of the component. + * @returns The class name of the component. + */ + public className(): string { + return TenantOverrideProcessor.CLASS_NAME; + } + + /** + * Pre process the REST request for the specified route. + * @param request The incoming request. + * @param response The outgoing response. + * @param route The route to process. + * @param contextIds The context IDs of the request. + * @param processorState The state handed through the processors. + * @returns A promise that resolves when the tenant override has been applied or skipped. + */ + public async pre( + request: IHttpServerRequest, + response: IHttpResponse, + route: IBaseRoute | undefined, + contextIds: IContextIds, + processorState: { [id: string]: unknown } + ): Promise { + if (Is.empty(route) || (route.disableTenantOverride ?? false)) { + return; + } + + const overrideTenantParam = request.query?.[TenantOverrideProcessor.OVERRIDE_TENANT_PARAM]; + if (!Is.stringValue(overrideTenantParam)) { + return; + } + + // If a prior processor already set an error response, skip processing. + if (Is.notEmpty(response.statusCode)) { + return; + } + + try { + const userId = contextIds[ContextIdKeys.User]; + if (!Is.stringValue(userId)) { + throw new ForbiddenError( + TenantOverrideProcessor.CLASS_NAME, + "missingUserIdForTenantOverride" + ); + } + + const authorizationRoles = await this._authorizationComponent.getRolesForSubject(userId); + + // Caller must hold the escalated privilege role. + if (!authorizationRoles.includes(this._escalatedPrivilegeRole)) { + throw new ForbiddenError( + TenantOverrideProcessor.CLASS_NAME, + "insufficientRoleForTenantOverride" + ); + } + + // Verify the tenant exists before substituting. + const tenant = await this._tenantAdminComponent.get(overrideTenantParam); + if (tenant?.id !== overrideTenantParam) { + throw new NotFoundError( + TenantOverrideProcessor.CLASS_NAME, + "tenantNotFound", + overrideTenantParam + ); + } + + contextIds[HttpContextIdKeys.OriginalTenant] = contextIds[ContextIdKeys.Tenant]; + contextIds[ContextIdKeys.Tenant] = overrideTenantParam; + } catch (err) { + const error = BaseError.fromError(err); + const { httpStatusCode } = HttpErrorHelper.processError(error); + HttpErrorHelper.buildResponse(response, error, httpStatusCode, this._includeErrorStack); + } + } +} diff --git a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts index da8087b..05b1124 100644 --- a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts +++ b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts @@ -32,7 +32,7 @@ import { HeaderTypes, HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationAuditRoutes"; /** - * The default authorization roles for the routes, use to populate authorization rules. + * The default authorization permissions for the routes, use to populate authorization rules. */ const PERMISSIONS_DEFAULT_ROUTE_READER = "audit:read"; const PERMISSIONS_DEFAULT_ROUTE_WRITER = { diff --git a/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts new file mode 100644 index 0000000..e29b47b --- /dev/null +++ b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts @@ -0,0 +1,264 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import { + ForbiddenError, + HttpContextIdKeys, + type IHttpResponse, + type IHttpServerRequest, + type ITenantAdminComponent +} from "@twin.org/api-models"; +import type { IAuthorizationComponent } from "@twin.org/authorization-models"; +import { ContextIdKeys, type IContextIds } from "@twin.org/context"; +import { ComponentFactory } from "@twin.org/core"; +import { HttpStatusCode } from "@twin.org/web"; +import { TenantOverrideProcessor } from "../../src/processors/tenantOverrideProcessor.js"; + +const CALLER_TENANT = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"; +const CALLER_USER = "user-001"; +const OVERRIDE_TENANT = "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7"; +const INVALID_TENANT = "not-a-valid-tenant-id"; + +describe("TenantOverrideProcessor", () => { + let mockTenantAdminComponent: ITenantAdminComponent; + let mockAuthorizationComponent: IAuthorizationComponent; + // Untyped reference so mockResolvedValue(undefined) needs no cast. + let getTenantMock: ReturnType; + let processor: TenantOverrideProcessor; + + beforeEach(() => { + vi.restoreAllMocks(); + + getTenantMock = vi.fn().mockResolvedValue({ id: OVERRIDE_TENANT }); + + mockTenantAdminComponent = { + get: getTenantMock + } as unknown as ITenantAdminComponent; + + mockAuthorizationComponent = { + getRolesForSubject: vi + .fn() + .mockResolvedValue([TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE]) + } as unknown as IAuthorizationComponent; + + vi.spyOn(ComponentFactory, "get").mockImplementation(type => { + if (type === "tenant-admin") { + return mockTenantAdminComponent as never; + } + return mockAuthorizationComponent as never; + }); + + processor = new TenantOverrideProcessor(); + }); + + it("should return the class name", () => { + expect(processor.className()).toBe(TenantOverrideProcessor.CLASS_NAME); + }); + + it("should skip processing when route is undefined", async () => { + const contextIds: IContextIds = { [ContextIdKeys.Tenant]: CALLER_TENANT }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + undefined, + contextIds, + {} + ); + + expect(response.statusCode).toBeUndefined(); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + }); + + it("should skip processing when route explicitly disables tenant override", async () => { + const contextIds: IContextIds = { [ContextIdKeys.Tenant]: CALLER_TENANT }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "someRoute", path: "/", disableTenantOverride: true }, + contextIds, + {} + ); + + expect(response.statusCode).toBeUndefined(); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + }); + + it("should skip processing when override-tenant query param is absent", async () => { + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await processor.pre( + { query: {} } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBeUndefined(); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + }); + + it("should skip processing when response already has a status code set", async () => { + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = { statusCode: HttpStatusCode.unauthorized }; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBe(HttpStatusCode.unauthorized); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + expect(mockTenantAdminComponent.get).not.toHaveBeenCalled(); + }); + + it("should return 403 when caller user ID is not set in context", async () => { + const contextIds: IContextIds = { [ContextIdKeys.Tenant]: CALLER_TENANT }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBe(HttpStatusCode.forbidden); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + expect(mockAuthorizationComponent.getRolesForSubject).not.toHaveBeenCalled(); + }); + + it("should return 403 when override-tenant is supplied but caller lacks escalated privilege", async () => { + vi.mocked(mockAuthorizationComponent.getRolesForSubject).mockResolvedValue(["user-admin"]); + + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBe(HttpStatusCode.forbidden); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + expect(mockTenantAdminComponent.get).not.toHaveBeenCalled(); + }); + + it("should return 404 when override-tenant does not match a known tenant", async () => { + getTenantMock.mockResolvedValue(undefined); + + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": INVALID_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBe(HttpStatusCode.notFound); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + }); + + it("should return 404 when override-tenant does not exist in the system", async () => { + getTenantMock.mockResolvedValue(undefined); + + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBe(HttpStatusCode.notFound); + expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); + }); + + it("should substitute the tenant partition and store the original when override is valid", async () => { + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBeUndefined(); + expect(contextIds[ContextIdKeys.Tenant]).toBe(OVERRIDE_TENANT); + expect(contextIds[HttpContextIdKeys.OriginalTenant]).toBe(CALLER_TENANT); + expect(mockTenantAdminComponent.get).toHaveBeenCalledWith(OVERRIDE_TENANT); + }); + + it("should respond with 403 body containing the error name", async () => { + vi.mocked(mockAuthorizationComponent.getRolesForSubject).mockResolvedValue(["user-admin"]); + + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBe(HttpStatusCode.forbidden); + const body = response.body as { name: string }; + expect(body.name).toBe(ForbiddenError.CLASS_NAME); + }); + + it("should accept a custom escalated privilege role when configured", async () => { + vi.mocked(mockAuthorizationComponent.getRolesForSubject).mockResolvedValue(["super-admin"]); + + const customProcessor = new TenantOverrideProcessor({ + config: { escalatedPrivilegeRole: "super-admin" } + }); + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await customProcessor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBeUndefined(); + expect(contextIds[ContextIdKeys.Tenant]).toBe(OVERRIDE_TENANT); + }); +}); From 82568f7932a136f28e78c946edf36e831ce32e0b Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Wed, 19 Aug 2026 15:14:10 +0800 Subject: [PATCH 04/20] feat: align with api repo versions --- packages/api-auth-entity-storage-models/package.json | 2 +- .../api-auth-entity-storage-rest-client/package.json | 6 +++--- .../api-auth-entity-storage-service/package.json | 6 +++--- packages/api-auth-models/package.json | 2 +- packages/api-auth-rest-client/package.json | 4 ++-- packages/api-auth-service/package.json | 4 ++-- release/release-please-manifest.prerelease.json | 12 ++++++------ 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/api-auth-entity-storage-models/package.json b/packages/api-auth-entity-storage-models/package.json index abf4298..3ef2afb 100644 --- a/packages/api-auth-entity-storage-models/package.json +++ b/packages/api-auth-entity-storage-models/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-models", - "version": "0.9.2-next.9", + "version": "0.9.2-next.10", "description": "Contracts for authentication flows and admin user management with entity storage.", "repository": { "type": "git", diff --git a/packages/api-auth-entity-storage-rest-client/package.json b/packages/api-auth-entity-storage-rest-client/package.json index de05ec9..ad7354b 100644 --- a/packages/api-auth-entity-storage-rest-client/package.json +++ b/packages/api-auth-entity-storage-rest-client/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-rest-client", - "version": "0.9.2-next.9", + "version": "0.9.2-next.10", "description": "REST clients for authentication and admin operations against entity storage endpoints.", "repository": { "type": "git", @@ -28,8 +28,8 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.10", + "@twin.org/api-auth-models": "0.9.2-next.10", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-entity-storage-service/package.json b/packages/api-auth-entity-storage-service/package.json index a94ee15..a14c920 100644 --- a/packages/api-auth-entity-storage-service/package.json +++ b/packages/api-auth-entity-storage-service/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-service", - "version": "0.9.2-next.9", + "version": "0.9.2-next.10", "description": "Authentication service implementation and REST routes backed by entity storage.", "repository": { "type": "git", @@ -29,8 +29,8 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.10", + "@twin.org/api-auth-models": "0.9.2-next.10", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", diff --git a/packages/api-auth-models/package.json b/packages/api-auth-models/package.json index a3efb8f..b36b515 100644 --- a/packages/api-auth-models/package.json +++ b/packages/api-auth-models/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-models", - "version": "0.9.2-next.9", + "version": "0.9.2-next.10", "description": "Contracts for authentication audit and rate limiting.", "repository": { "type": "git", diff --git a/packages/api-auth-rest-client/package.json b/packages/api-auth-rest-client/package.json index 88cc27c..d53713a 100644 --- a/packages/api-auth-rest-client/package.json +++ b/packages/api-auth-rest-client/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-rest-client", - "version": "0.9.2-next.9", + "version": "0.9.2-next.10", "description": "REST clients for authentication and admin operations against entity storage endpoints.", "repository": { "type": "git", @@ -28,7 +28,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.2-next.10", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json index bd90be8..68b769f 100644 --- a/packages/api-auth-service/package.json +++ b/packages/api-auth-service/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-service", - "version": "0.9.2-next.9", + "version": "0.9.2-next.10", "description": "Authentication service implementation and REST routes backed by entity storage.", "repository": { "type": "git", @@ -29,7 +29,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.2-next.10", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/authorization-models": "next", diff --git a/release/release-please-manifest.prerelease.json b/release/release-please-manifest.prerelease.json index 146c72b..fa8e5ea 100644 --- a/release/release-please-manifest.prerelease.json +++ b/release/release-please-manifest.prerelease.json @@ -1,8 +1,8 @@ { - "packages/api-auth-models": "0.9.2-next.9", - "packages/api-auth-service": "0.9.2-next.9", - "packages/api-auth-rest-client": "0.9.2-next.9", - "packages/api-auth-entity-storage-models": "0.9.2-next.9", - "packages/api-auth-entity-storage-service": "0.9.2-next.9", - "packages/api-auth-entity-storage-rest-client": "0.9.2-next.9" + "packages/api-auth-models": "0.9.2-next.10", + "packages/api-auth-service": "0.9.2-next.10", + "packages/api-auth-rest-client": "0.9.2-next.10", + "packages/api-auth-entity-storage-models": "0.9.2-next.10", + "packages/api-auth-entity-storage-service": "0.9.2-next.10", + "packages/api-auth-entity-storage-rest-client": "0.9.2-next.10" } From 4e4088c0f79bef39a41e699d245ef1a2d4fa542f Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Thu, 20 Aug 2026 11:37:27 +0800 Subject: [PATCH 05/20] feat: align with api repo versions --- packages/api-auth-entity-storage-models/package.json | 2 +- .../api-auth-entity-storage-rest-client/package.json | 6 +++--- .../api-auth-entity-storage-service/package.json | 6 +++--- packages/api-auth-models/package.json | 2 +- packages/api-auth-rest-client/package.json | 4 ++-- packages/api-auth-service/package.json | 4 ++-- release/release-please-manifest.prerelease.json | 12 ++++++------ 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/api-auth-entity-storage-models/package.json b/packages/api-auth-entity-storage-models/package.json index 3ef2afb..55f12a1 100644 --- a/packages/api-auth-entity-storage-models/package.json +++ b/packages/api-auth-entity-storage-models/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-models", - "version": "0.9.2-next.10", + "version": "0.9.2-next.11", "description": "Contracts for authentication flows and admin user management with entity storage.", "repository": { "type": "git", diff --git a/packages/api-auth-entity-storage-rest-client/package.json b/packages/api-auth-entity-storage-rest-client/package.json index ad7354b..4c0412b 100644 --- a/packages/api-auth-entity-storage-rest-client/package.json +++ b/packages/api-auth-entity-storage-rest-client/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-rest-client", - "version": "0.9.2-next.10", + "version": "0.9.2-next.11", "description": "REST clients for authentication and admin operations against entity storage endpoints.", "repository": { "type": "git", @@ -28,8 +28,8 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.10", - "@twin.org/api-auth-models": "0.9.2-next.10", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.11", + "@twin.org/api-auth-models": "0.9.2-next.11", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-entity-storage-service/package.json b/packages/api-auth-entity-storage-service/package.json index a14c920..70208d7 100644 --- a/packages/api-auth-entity-storage-service/package.json +++ b/packages/api-auth-entity-storage-service/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-service", - "version": "0.9.2-next.10", + "version": "0.9.2-next.11", "description": "Authentication service implementation and REST routes backed by entity storage.", "repository": { "type": "git", @@ -29,8 +29,8 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.10", - "@twin.org/api-auth-models": "0.9.2-next.10", + "@twin.org/api-auth-entity-storage-models": "0.9.2-next.11", + "@twin.org/api-auth-models": "0.9.2-next.11", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", diff --git a/packages/api-auth-models/package.json b/packages/api-auth-models/package.json index b36b515..d962d26 100644 --- a/packages/api-auth-models/package.json +++ b/packages/api-auth-models/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-models", - "version": "0.9.2-next.10", + "version": "0.9.2-next.11", "description": "Contracts for authentication audit and rate limiting.", "repository": { "type": "git", diff --git a/packages/api-auth-rest-client/package.json b/packages/api-auth-rest-client/package.json index d53713a..0e8ee3d 100644 --- a/packages/api-auth-rest-client/package.json +++ b/packages/api-auth-rest-client/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-rest-client", - "version": "0.9.2-next.10", + "version": "0.9.2-next.11", "description": "REST clients for authentication and admin operations against entity storage endpoints.", "repository": { "type": "git", @@ -28,7 +28,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.10", + "@twin.org/api-auth-models": "0.9.2-next.11", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json index 68b769f..818c184 100644 --- a/packages/api-auth-service/package.json +++ b/packages/api-auth-service/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-service", - "version": "0.9.2-next.10", + "version": "0.9.2-next.11", "description": "Authentication service implementation and REST routes backed by entity storage.", "repository": { "type": "git", @@ -29,7 +29,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.10", + "@twin.org/api-auth-models": "0.9.2-next.11", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/authorization-models": "next", diff --git a/release/release-please-manifest.prerelease.json b/release/release-please-manifest.prerelease.json index fa8e5ea..6beb6c4 100644 --- a/release/release-please-manifest.prerelease.json +++ b/release/release-please-manifest.prerelease.json @@ -1,8 +1,8 @@ { - "packages/api-auth-models": "0.9.2-next.10", - "packages/api-auth-service": "0.9.2-next.10", - "packages/api-auth-rest-client": "0.9.2-next.10", - "packages/api-auth-entity-storage-models": "0.9.2-next.10", - "packages/api-auth-entity-storage-service": "0.9.2-next.10", - "packages/api-auth-entity-storage-rest-client": "0.9.2-next.10" + "packages/api-auth-models": "0.9.2-next.11", + "packages/api-auth-service": "0.9.2-next.11", + "packages/api-auth-rest-client": "0.9.2-next.11", + "packages/api-auth-entity-storage-models": "0.9.2-next.11", + "packages/api-auth-entity-storage-service": "0.9.2-next.11", + "packages/api-auth-entity-storage-rest-client": "0.9.2-next.11" } From 86ef21769ba9c5a8c05da5ceb4870ddb94ca6f9e Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Fri, 21 Aug 2026 11:44:15 +0800 Subject: [PATCH 06/20] feat: update route defaults --- .../entityStorageAuthenticationAdminRoutes.ts | 35 +++++++++++++------ .../entityStorageAuthenticationRoutes.ts | 26 +++++++++----- .../entityStorageAuthenticationAuditRoutes.ts | 34 ++++++++++++------ 3 files changed, 67 insertions(+), 28 deletions(-) diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts index cf0c702..fe407eb 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts @@ -32,12 +32,21 @@ import { HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationAdminRoutes"; /** - * The default authorization permissions for the routes, use to populate authorization rules. + * The default permissions for the routes, used to seed authorization rules. */ -const PERMISSIONS_DEFAULT_ROUTE_READER = "user-admin:read"; -const PERMISSIONS_DEFAULT_ROUTE_WRITER = { +const DEFAULT_ROUTE_PERMISSIONS_READER = "user-admin:read"; +const DEFAULT_ROUTE_PERMISSIONS_WRITER = { permission: "user-admin:write", - inherits: [PERMISSIONS_DEFAULT_ROUTE_READER] + inherits: [DEFAULT_ROUTE_PERMISSIONS_READER] +}; + +/** + * The default roles for the routes, used to seed authorization rules. + */ +const DEFAULT_ROUTE_ROLES_READER = "user-viewer"; +const DEFAULT_ROUTE_ROLES_WRITER = { + role: "user-admin", + inherits: [DEFAULT_ROUTE_ROLES_READER] }; /** @@ -93,7 +102,8 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], + defaultRoles: [DEFAULT_ROUTE_ROLES_READER] }; const updateUserRoute: IRestRoute = { @@ -130,7 +140,8 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; const updateUserPasswordRoute: IRestRoute = { @@ -166,7 +177,8 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; const getUserRoute: IRestRoute = { @@ -212,7 +224,8 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], + defaultRoles: [DEFAULT_ROUTE_ROLES_READER] }; const getByIdentityUserRoute: IRestRoute = @@ -259,7 +272,8 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], + defaultRoles: [DEFAULT_ROUTE_ROLES_READER] }; const removeUserRoute: IRestRoute = { @@ -292,7 +306,8 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; return [ diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index 31c5ed1..29c3aae 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -27,14 +27,20 @@ import { HttpStatusCode } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationRoutes"; /** - * The default authorization permissions for the routes, use to populate authorization rules. + * The default permissions for the routes, used to seed authorization rules. */ -const PERMISSIONS_DEFAULT_ROUTE_READER = "user:read"; -const PERMISSIONS_DEFAULT_ROUTE_WRITER = { +const DEFAULT_ROUTE_PERMISSIONS_EXECUTE = "user:execute"; +const DEFAULT_ROUTE_PERMISSIONS_WRITER = { permission: "user:write", - inherits: [PERMISSIONS_DEFAULT_ROUTE_READER] + inherits: ["user:read"] }; +/** + * The default roles for the routes, used to seed authorization rules. + */ +const DEFAULT_ROUTE_ROLES_EXECUTE = "user"; +const DEFAULT_ROUTE_ROLES_WRITER = "user"; + /** * The tag to associate with the routes. */ @@ -98,7 +104,8 @@ export function generateRestRoutesAuthentication( } ], skipAuth: true, - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_EXECUTE], + defaultRoles: [DEFAULT_ROUTE_ROLES_EXECUTE] }; const logoutRoute: IRestRoute = { @@ -128,7 +135,8 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_EXECUTE], + defaultRoles: [DEFAULT_ROUTE_ROLES_EXECUTE] }; const refreshTokenRoute: IRestRoute = { @@ -172,7 +180,8 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_EXECUTE], + defaultRoles: [DEFAULT_ROUTE_ROLES_EXECUTE] }; const updatePasswordRoute: IRestRoute = { @@ -206,7 +215,8 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; return [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute]; diff --git a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts index 05b1124..25cfe68 100644 --- a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts +++ b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts @@ -32,12 +32,21 @@ import { HeaderTypes, HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationAuditRoutes"; /** - * The default authorization permissions for the routes, use to populate authorization rules. + * The default permissions for the routes, used to seed authorization rules. */ -const PERMISSIONS_DEFAULT_ROUTE_READER = "audit:read"; -const PERMISSIONS_DEFAULT_ROUTE_WRITER = { - permission: "audit:write", - inherits: [PERMISSIONS_DEFAULT_ROUTE_READER] +const DEFAULT_ROUTE_PERMISSIONS_READER = "user-audit:read"; +const DEFAULT_ROUTE_PERMISSIONS_WRITER = { + permission: "user-audit:write", + inherits: [DEFAULT_ROUTE_PERMISSIONS_READER] +}; + +/** + * The default roles for the routes, used to seed authorization rules. + */ +const DEFAULT_ROUTE_ROLES_READER = "user-audit-viewer"; +const DEFAULT_ROUTE_ROLES_WRITER = { + role: "user-audit-admin", + inherits: [DEFAULT_ROUTE_ROLES_READER] }; /** @@ -106,7 +115,8 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; const getRoute: IRestRoute = { @@ -156,7 +166,8 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; const updateRoute: IRestRoute = { @@ -194,7 +205,8 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; const removeRoute: IRestRoute = { @@ -227,7 +239,8 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_WRITER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], + defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] }; const queryRoute: IRestRoute = { @@ -286,7 +299,8 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [PERMISSIONS_DEFAULT_ROUTE_READER] + defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], + defaultRoles: [DEFAULT_ROUTE_ROLES_READER] }; return [createRoute, getRoute, updateRoute, removeRoute, queryRoute]; From a6a0a23ed4db16eba560c27855f605fc4a32e41d Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Fri, 21 Aug 2026 14:44:30 +0800 Subject: [PATCH 07/20] chore: update lint rules --- eslint.config.js | 29 +++++++++++++++++++ .../tests/helpers/restClientTestHelpers.ts | 4 +-- .../tests/helpers/restClientTestHelpers.ts | 4 +-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 4e2b91b..599b1e9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -304,6 +304,34 @@ const tsRestrictedSyntax = [ { selector: 'MethodDefinition[static=true] ThisExpression', message: 'Do not use "this" in static methods' + }, + { + selector: 'VariableDeclarator[definite=true]', + message: + 'Definite assignment assertions (let x!: T) are disallowed. Restructure the code so TypeScript can infer definite assignment, or initialise to a safe default.' + }, + { + selector: + ":matches(BinaryExpression[operator='==='], BinaryExpression[operator='!=='], BinaryExpression[operator='=='], BinaryExpression[operator='!=']) > UnaryExpression[operator='typeof']", + message: 'Avoid runtime typeof comparisons. Use the @twin.org/core Is.* methods instead.' + }, + { + selector: + ':matches(FunctionDeclaration, ArrowFunctionExpression, FunctionExpression, TSDeclareFunction, TSMethodSignature, TSCallSignatureDeclaration, TSConstructSignatureDeclaration) > Identifier.params > TSTypeAnnotation > TSObjectKeyword', + message: + 'Do not use `object` as a parameter type. Use a specific interface, type alias, or generic constraint instead.' + }, + { + selector: + 'ObjectExpression > SpreadElement > ConditionalExpression[consequent.type="ObjectExpression"][consequent.properties.length=0]', + message: + 'Avoid spreading a conditional where one branch is an empty object to omit properties. Use a property with a conditional value instead: { prop: condition ? value : undefined }.' + }, + { + selector: + 'ObjectExpression > SpreadElement > ConditionalExpression[alternate.type="ObjectExpression"][alternate.properties.length=0]', + message: + 'Avoid spreading a conditional where one branch is an empty object to omit properties. Use a property with a conditional value instead: { prop: condition ? value : undefined }.' } ]; @@ -473,6 +501,7 @@ const importRules = { { devDependencies: [ '**/tests/**/*.ts', + '**/tests/**/*.js', '**/scripts/**/*.mjs', '**/*.config.js', '**/*.config.cjs', diff --git a/packages/api-auth-entity-storage-rest-client/tests/helpers/restClientTestHelpers.ts b/packages/api-auth-entity-storage-rest-client/tests/helpers/restClientTestHelpers.ts index 5bae838..7918dd2 100644 --- a/packages/api-auth-entity-storage-rest-client/tests/helpers/restClientTestHelpers.ts +++ b/packages/api-auth-entity-storage-rest-client/tests/helpers/restClientTestHelpers.ts @@ -78,8 +78,8 @@ export function createdResponse(location: string): object { * Call in beforeEach alongside any client construction. * @param mock The vi.fn() mock to install as globalThis.fetch. */ -export function setupFetchMock(mock: object): void { - globalThis.fetch = mock as typeof fetch; +export function setupFetchMock(mock: typeof fetch): void { + globalThis.fetch = mock; } /** diff --git a/packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts b/packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts index 5bae838..7918dd2 100644 --- a/packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts +++ b/packages/api-auth-rest-client/tests/helpers/restClientTestHelpers.ts @@ -78,8 +78,8 @@ export function createdResponse(location: string): object { * Call in beforeEach alongside any client construction. * @param mock The vi.fn() mock to install as globalThis.fetch. */ -export function setupFetchMock(mock: object): void { - globalThis.fetch = mock as typeof fetch; +export function setupFetchMock(mock: typeof fetch): void { + globalThis.fetch = mock; } /** From 72bf20eab3a97db5a45b779d438514bb6003a650 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Mon, 24 Aug 2026 14:22:05 +0800 Subject: [PATCH 08/20] chore: align versions --- packages/api-auth-entity-storage-models/package.json | 2 +- .../api-auth-entity-storage-rest-client/package.json | 6 +++--- .../api-auth-entity-storage-service/package.json | 6 +++--- packages/api-auth-models/package.json | 2 +- packages/api-auth-rest-client/package.json | 4 ++-- packages/api-auth-service/package.json | 4 ++-- release/release-please-manifest.prerelease.json | 12 ++++++------ release/release-please-manifest.prod.json | 12 ++++++------ 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/api-auth-entity-storage-models/package.json b/packages/api-auth-entity-storage-models/package.json index 55f12a1..859e2b7 100644 --- a/packages/api-auth-entity-storage-models/package.json +++ b/packages/api-auth-entity-storage-models/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-models", - "version": "0.9.2-next.11", + "version": "0.9.3-next.0", "description": "Contracts for authentication flows and admin user management with entity storage.", "repository": { "type": "git", diff --git a/packages/api-auth-entity-storage-rest-client/package.json b/packages/api-auth-entity-storage-rest-client/package.json index 4c0412b..e952890 100644 --- a/packages/api-auth-entity-storage-rest-client/package.json +++ b/packages/api-auth-entity-storage-rest-client/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-rest-client", - "version": "0.9.2-next.11", + "version": "0.9.3-next.0", "description": "REST clients for authentication and admin operations against entity storage endpoints.", "repository": { "type": "git", @@ -28,8 +28,8 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.11", - "@twin.org/api-auth-models": "0.9.2-next.11", + "@twin.org/api-auth-entity-storage-models": "0.9.3-next.0", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-entity-storage-service/package.json b/packages/api-auth-entity-storage-service/package.json index 70208d7..fe0daf6 100644 --- a/packages/api-auth-entity-storage-service/package.json +++ b/packages/api-auth-entity-storage-service/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-entity-storage-service", - "version": "0.9.2-next.11", + "version": "0.9.3-next.0", "description": "Authentication service implementation and REST routes backed by entity storage.", "repository": { "type": "git", @@ -29,8 +29,8 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.11", - "@twin.org/api-auth-models": "0.9.2-next.11", + "@twin.org/api-auth-entity-storage-models": "0.9.3-next.0", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", diff --git a/packages/api-auth-models/package.json b/packages/api-auth-models/package.json index d962d26..0af10a7 100644 --- a/packages/api-auth-models/package.json +++ b/packages/api-auth-models/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-models", - "version": "0.9.2-next.11", + "version": "0.9.3-next.0", "description": "Contracts for authentication audit and rate limiting.", "repository": { "type": "git", diff --git a/packages/api-auth-rest-client/package.json b/packages/api-auth-rest-client/package.json index 0e8ee3d..6e1aa58 100644 --- a/packages/api-auth-rest-client/package.json +++ b/packages/api-auth-rest-client/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-rest-client", - "version": "0.9.2-next.11", + "version": "0.9.3-next.0", "description": "REST clients for authentication and admin operations against entity storage endpoints.", "repository": { "type": "git", @@ -28,7 +28,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.11", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json index 818c184..f583387 100644 --- a/packages/api-auth-service/package.json +++ b/packages/api-auth-service/package.json @@ -1,6 +1,6 @@ { "name": "@twin.org/api-auth-service", - "version": "0.9.2-next.11", + "version": "0.9.3-next.0", "description": "Authentication service implementation and REST routes backed by entity storage.", "repository": { "type": "git", @@ -29,7 +29,7 @@ "dist:no-test": "npm run clean && npm run build && npm run test:build" }, "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.11", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/authorization-models": "next", diff --git a/release/release-please-manifest.prerelease.json b/release/release-please-manifest.prerelease.json index 6beb6c4..01772a5 100644 --- a/release/release-please-manifest.prerelease.json +++ b/release/release-please-manifest.prerelease.json @@ -1,8 +1,8 @@ { - "packages/api-auth-models": "0.9.2-next.11", - "packages/api-auth-service": "0.9.2-next.11", - "packages/api-auth-rest-client": "0.9.2-next.11", - "packages/api-auth-entity-storage-models": "0.9.2-next.11", - "packages/api-auth-entity-storage-service": "0.9.2-next.11", - "packages/api-auth-entity-storage-rest-client": "0.9.2-next.11" + "packages/api-auth-models": "0.9.3-next.0", + "packages/api-auth-service": "0.9.3-next.0", + "packages/api-auth-rest-client": "0.9.3-next.0", + "packages/api-auth-entity-storage-models": "0.9.3-next.0", + "packages/api-auth-entity-storage-service": "0.9.3-next.0", + "packages/api-auth-entity-storage-rest-client": "0.9.3-next.0" } diff --git a/release/release-please-manifest.prod.json b/release/release-please-manifest.prod.json index 4e403bc..0cadd4e 100644 --- a/release/release-please-manifest.prod.json +++ b/release/release-please-manifest.prod.json @@ -1,8 +1,8 @@ { - "packages/api-auth-models": "0.9.1", - "packages/api-auth-service": "0.9.1", - "packages/api-auth-rest-client": "0.9.1", - "packages/api-auth-entity-storage-models": "0.9.1", - "packages/api-auth-entity-storage-service": "0.9.1", - "packages/api-auth-entity-storage-rest-client": "0.9.1" + "packages/api-auth-models": "0.9.2", + "packages/api-auth-service": "0.9.2", + "packages/api-auth-rest-client": "0.9.2", + "packages/api-auth-entity-storage-models": "0.9.2", + "packages/api-auth-entity-storage-service": "0.9.2", + "packages/api-auth-entity-storage-rest-client": "0.9.2" } From 2994961ce69a036397966b306bc9e56f37254e2b Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Tue, 25 Aug 2026 09:00:22 +0800 Subject: [PATCH 09/20] chore: update dependencies --- package-lock.json | 466 +++++++++++++++++++++++----------------------- 1 file changed, 237 insertions(+), 229 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d9942e..c54a2e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -406,9 +406,9 @@ } }, "node_modules/@conventional-changelog/template": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.3.0.tgz", - "integrity": "sha512-GsCw/qu92GI0EX6s7fxUi/SG1lmFjG9XZivxxEDZXqztuQKCn5o5wKdz4v005zci0Md7EZzgAwmQLoIEDZoaow==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", + "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", "dev": true, "license": "MIT", "engines": { @@ -755,9 +755,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-golang": { - "version": "6.0.26", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.26.tgz", - "integrity": "sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==", + "version": "6.0.27", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.27.tgz", + "integrity": "sha512-rRDb2JL8EefaezHGTEclKXCs4eMOS0q/datk94Lm7KQOhlGlzS9FDVZiR1/guh0o+iJCmejQuf5NR2FQeQSWsg==", "dev": true, "license": "MIT" }, @@ -776,9 +776,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-html": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.15.tgz", - "integrity": "sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==", + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.16.tgz", + "integrity": "sha512-9B6/Cpb5YVcYgsEAlKudun1yd4ONllYS/ZibKLYea2apPR+l2vQdARnPrP9kTqa7NUNfRKl7m9Fb6k9rjimnow==", "dev": true, "license": "MIT" }, @@ -846,14 +846,14 @@ "license": "MIT" }, "node_modules/@cspell/dict-markdown": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.17.tgz", - "integrity": "sha512-H8bAxih6U8NOnSPL7R8My+tqjaB4tmnJTjERuz4zYqmf+cH+5xshX3UVgKlwWFcyjsYfv/zEDuRdMctQv1q6HQ==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.18.tgz", + "integrity": "sha512-KLRSwVrwKDz4n7bl2XQjzvaBZbGgx5QKkP5Ok1b24xaO3TpXsLhAbAn1mItvFlI2tF6Rkt83iYjQcYppywuf5Q==", "dev": true, "license": "MIT", "peerDependencies": { "@cspell/dict-css": "^4.1.2", - "@cspell/dict-html": "^4.0.15", + "@cspell/dict-html": "^4.0.16", "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-typescript": "^3.2.3" } @@ -873,9 +873,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-npm": { - "version": "5.2.45", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.45.tgz", - "integrity": "sha512-BXUmMMspl+AhPIk/ZOjxlNu5k1yCRAzSMzdNPYM+2vaBL8ffev7ZFfOyBCDk8kgcpsHotz8/3fLNX+xwWaDR2w==", + "version": "5.2.47", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.47.tgz", + "integrity": "sha512-r5ePbkmRtpMwiv+MHd6be1xIRLuXoEwaSLIZLu3mY+qtPVo0LUj/kVl4YeIxHONiwyef75zqS3gnT+s5UF7diQ==", "dev": true, "license": "MIT" }, @@ -901,9 +901,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-python": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.3.0.tgz", - "integrity": "sha512-afVcbsCOYtpdvu8I4ANiIXVcxqxzrno+oEuObNx9hwpvEmf/AbXQBk9CMjFR9QZllcfnOfpjCSXgGuTna5hKQA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.4.0.tgz", + "integrity": "sha512-49Eju/a97V6ExAeMJDM2Tk4jsYr0pm+kQzqQrKLAZdrRd6VrLIgseI481EQSmgoVkPPdsuwK1xj9ZlpDQIF4qg==", "dev": true, "license": "MIT", "dependencies": { @@ -918,9 +918,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-ruby": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.1.tgz", - "integrity": "sha512-LHrp84oEV6q1ZxPPyj4z+FdKyq1XAKYPtmGptrd+uwHbrF/Ns5+fy6gtSi7pS+uc0zk3JdO9w/tPK+8N1/7WUA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.2.tgz", + "integrity": "sha512-f6Slf11N91ittf71AWlfVVG9GZPezVRBfcMPCTNTWhUsY/VEspkBRZYDhPXjV4df3jqHy5YJs8nlsFcFVF/bMA==", "dev": true, "license": "MIT" }, @@ -946,9 +946,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-software-terms": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.3.0.tgz", - "integrity": "sha512-HEMvFWeItTA5A/MFxrH5fpOz9DU7ormXaGp1PJPsd0jt+qqaYNpevGIgTfIhfp/hsabPyAGa51N/DeDT0ZMg2Q==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.4.1.tgz", + "integrity": "sha512-tY88gnOWj2ax6h+i7BjU7dIH1f3fvY4WypS0a+TpjBgXC318AdOMmy0zK1SzxGNOjhWJfCeCZNVoLFM/DuOG4g==", "dev": true, "license": "MIT" }, @@ -1404,9 +1404,9 @@ } }, "node_modules/@google-automations/git-file-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@google-automations/git-file-utils/-/git-file-utils-3.1.0.tgz", - "integrity": "sha512-WITzh+nsH7ugjTIXnx8H9pqCh58ulYippEpa6+ioN8q8F1iUaoQfINMzWci1aYLsZU78yeO90S8zB4QTqCBYbw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@google-automations/git-file-utils/-/git-file-utils-3.2.0.tgz", + "integrity": "sha512-EtQD1DX17kGpckDNCdI0jvMdF98EWR/TM9uogQtnhK0ODv8z5tPSPMICv2lk54c/9ucUgbfdLDrRdDyx56Sznw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1812,9 +1812,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.144.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz", - "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==", + "version": "0.146.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz", + "integrity": "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==", "dev": true, "license": "MIT", "funding": { @@ -1834,10 +1834,27 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz", + "integrity": "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", - "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz", + "integrity": "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==", "cpu": [ "arm64" ], @@ -1852,9 +1869,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz", + "integrity": "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==", "cpu": [ "arm64" ], @@ -1869,9 +1886,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz", - "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz", + "integrity": "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==", "cpu": [ "x64" ], @@ -1886,9 +1903,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz", - "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz", + "integrity": "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==", "cpu": [ "x64" ], @@ -1903,9 +1920,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz", - "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz", + "integrity": "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==", "cpu": [ "arm" ], @@ -1920,9 +1937,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz", - "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz", + "integrity": "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==", "cpu": [ "arm64" ], @@ -1940,9 +1957,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz", - "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz", + "integrity": "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==", "cpu": [ "arm64" ], @@ -1960,9 +1977,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz", - "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz", + "integrity": "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==", "cpu": [ "ppc64" ], @@ -1980,9 +1997,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz", - "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz", + "integrity": "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==", "cpu": [ "s390x" ], @@ -2000,9 +2017,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz", - "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz", + "integrity": "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==", "cpu": [ "x64" ], @@ -2020,9 +2037,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz", - "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz", + "integrity": "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==", "cpu": [ "x64" ], @@ -2040,9 +2057,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz", - "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz", + "integrity": "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==", "cpu": [ "arm64" ], @@ -2057,9 +2074,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz", - "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz", + "integrity": "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==", "cpu": [ "arm64" ], @@ -2074,9 +2091,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz", - "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz", + "integrity": "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==", "cpu": [ "x64" ], @@ -2332,12 +2349,12 @@ "link": true }, "node_modules/@twin.org/api-core": { - "version": "0.9.2-next.9", - "resolved": "https://registry.npmjs.org/@twin.org/api-core/-/api-core-0.9.2-next.9.tgz", - "integrity": "sha512-JZXWHeaNyV+0a+feqYE7RHNSLvwa8wzb45s/Bmes2N8nF+vW447IW9uPesmLTBR47HKO/szk0q/rV0IkqKZzQw==", + "version": "0.9.2-next.13", + "resolved": "https://registry.npmjs.org/@twin.org/api-core/-/api-core-0.9.2-next.13.tgz", + "integrity": "sha512-p380mnxhUJu+U6Yq9BdIHRtu59FDbbEjsDKtdJTLlSGejpxREZi0j3QcLh/fbc7dpzyXaEKv36jTeGbjIIK9qA==", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-models": "0.9.2-next.9", + "@twin.org/api-models": "0.9.2-next.13", "@twin.org/core": "next", "@twin.org/nameof": "next", "@twin.org/web": "next", @@ -2348,9 +2365,9 @@ } }, "node_modules/@twin.org/api-models": { - "version": "0.9.2-next.9", - "resolved": "https://registry.npmjs.org/@twin.org/api-models/-/api-models-0.9.2-next.9.tgz", - "integrity": "sha512-hdNZFe1IDC/Xmk54QcgUTiMtcydPWL/2bvphdW7NvT4i5U5/Sxkw/ffahfu6UOBSx1tCHYahgfj83YsBCIBOUg==", + "version": "0.9.2-next.13", + "resolved": "https://registry.npmjs.org/@twin.org/api-models/-/api-models-0.9.2-next.13.tgz", + "integrity": "sha512-83NDSqXRFZmyQ5m9w1TXSRmwq4UNxMY9W+RUqgQ4zkUlMzB58vmxVGrTxUl8tq4jmWAcBgexcYW9VDxsDoRP9Q==", "license": "Apache-2.0", "dependencies": { "@twin.org/context": "next", @@ -2364,9 +2381,9 @@ } }, "node_modules/@twin.org/background-task-models": { - "version": "0.9.2-next.4", - "resolved": "https://registry.npmjs.org/@twin.org/background-task-models/-/background-task-models-0.9.2-next.4.tgz", - "integrity": "sha512-dr7B0zY/JNxuGNd79ep3dFhdYvadtsiOtGEHMiaFXHsE/JyAodqvVCgA8qFLaezccr7DbtXvqzwBarD1GGU4Bg==", + "version": "0.9.2-next.5", + "resolved": "https://registry.npmjs.org/@twin.org/background-task-models/-/background-task-models-0.9.2-next.5.tgz", + "integrity": "sha512-hd7noq2creKlTm8zJET3sVTUpWvRQWE609cpHRvFXTMzPQlBfdvJFx8/8BNqOgqVItr1mlHnZ0REnzxcddSQjg==", "license": "Apache-2.0", "dependencies": { "@twin.org/core": "next", @@ -2378,14 +2395,14 @@ } }, "node_modules/@twin.org/cli-core": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/cli-core/-/cli-core-0.9.2-next.10.tgz", - "integrity": "sha512-Npe4iu3qNcW+zkYgijHrET555jb6PxU0hZX5IzijJrUK55KMDIHVWhNo2XLd4aHXubyIMmwac1mWZY7Sx8RuJA==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/cli-core/-/cli-core-0.9.2-next.11.tgz", + "integrity": "sha512-ZIbM+xcXzw8XVMlYXcFX22LVev9/DEDlLHJ+IZ2Ph27kBlhH5IMAoTW62wWtkxQdp6QbIgrpHX6aj27PEs2m/Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@twin.org/core": "0.9.2-next.10", - "@twin.org/nameof": "0.9.2-next.10", + "@twin.org/core": "0.9.2-next.11", + "@twin.org/nameof": "0.9.2-next.11", "chalk": "5.6.2", "commander": "15.0.0", "dotenv": "17.4.2" @@ -2395,25 +2412,25 @@ } }, "node_modules/@twin.org/context": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/context/-/context-0.9.2-next.10.tgz", - "integrity": "sha512-qFWw7PPjL93k/Yv3j5uSYiyhEAGV5MyzI24E12Us1p8vD4HiAppdCLxKU54l8gAHtr3MJkTgPIHOqCVgJYuWPQ==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/context/-/context-0.9.2-next.11.tgz", + "integrity": "sha512-CCuiIdBtUxuyNnG1N7SesbL7XG0IBzFZVJDx/sKAB6SSE457hkXZZcgpwkm1kavymnWRFeZDGtQL0TKCccNowA==", "license": "Apache-2.0", "dependencies": { - "@twin.org/core": "0.9.2-next.10", - "@twin.org/nameof": "0.9.2-next.10" + "@twin.org/core": "0.9.2-next.11", + "@twin.org/nameof": "0.9.2-next.11" }, "engines": { "node": ">=24.0.0" } }, "node_modules/@twin.org/core": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/core/-/core-0.9.2-next.10.tgz", - "integrity": "sha512-pX/MGtNF9TPQGAD2g8Wll1nFP0D/cUuhdXNzHH8NTrHEW0AnoViYXRHfNUuHa0tEQJONMOdT/nHPE6ldf6PlTw==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/core/-/core-0.9.2-next.11.tgz", + "integrity": "sha512-hXQfjXaqLezVakEKp+T2vXxcg42wRj8+GeDK7BSHri0F4WhgCorcTBPLpchBwDExAffcwxTgZGnF4YpTEda85g==", "license": "Apache-2.0", "dependencies": { - "@twin.org/nameof": "0.9.2-next.10", + "@twin.org/nameof": "0.9.2-next.11", "intl-messageformat": "11.2.13", "rfc6902": "5.3.0" }, @@ -2422,9 +2439,9 @@ } }, "node_modules/@twin.org/crypto": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/crypto/-/crypto-0.9.2-next.10.tgz", - "integrity": "sha512-1eCAfjfdXT8N6GVcnKINk2dnTL7u7GwyAPPDl0pm/xJvTOJNxRbNDUZoJkXULayqtVEwRsYbi7P+LC+/BEmB1g==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/crypto/-/crypto-0.9.2-next.11.tgz", + "integrity": "sha512-i1cKykVnteWpdQQQ3Yfg7pz3UyV0nj6ByGwGJjxqU4n9x6dPXc49OaSGlnyKhasSM2pJEgGqeUvpEN09q81pGQ==", "license": "Apache-2.0", "dependencies": { "@noble/ciphers": "2.2.0", @@ -2433,8 +2450,8 @@ "@scure/base": "2.2.0", "@scure/bip32": "2.2.0", "@scure/bip39": "2.2.0", - "@twin.org/core": "0.9.2-next.10", - "@twin.org/nameof": "0.9.2-next.10", + "@twin.org/core": "0.9.2-next.11", + "@twin.org/nameof": "0.9.2-next.11", "micro-key-producer": "0.9.0" }, "engines": { @@ -2442,13 +2459,13 @@ } }, "node_modules/@twin.org/entity": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/entity/-/entity-0.9.2-next.10.tgz", - "integrity": "sha512-3x0b9s+bmNSxfEAJ1NRX4W693W9OdvJ8+bxaYVGuM0f3EXZU211unZfQAxGmrZDiOK6RChpNkEehvx1aJ9u67w==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/entity/-/entity-0.9.2-next.11.tgz", + "integrity": "sha512-7Mbxcgu2FI18d2iFCJZTa5PCLDYXv/QULf4w7scbAUjXx9bZ/lmrxKrD8qCMyAdnYll8T1UDHD4C29hwrKM9PA==", "license": "Apache-2.0", "dependencies": { - "@twin.org/core": "0.9.2-next.10", - "@twin.org/nameof": "0.9.2-next.10", + "@twin.org/core": "0.9.2-next.11", + "@twin.org/nameof": "0.9.2-next.11", "reflect-metadata": "0.2.2", "tslib": "2.8.1" }, @@ -2493,9 +2510,9 @@ } }, "node_modules/@twin.org/logging-models": { - "version": "0.9.2-next.5", - "resolved": "https://registry.npmjs.org/@twin.org/logging-models/-/logging-models-0.9.2-next.5.tgz", - "integrity": "sha512-YLfUNBCkQV5oDz+yhsY6ZG8lXQX4HwZM2l2d7Ne3+0ZtRIGlGj+8eQZUlxJePK73BBvawcJC/3yVmNkDC9fPsA==", + "version": "0.9.2-next.8", + "resolved": "https://registry.npmjs.org/@twin.org/logging-models/-/logging-models-0.9.2-next.8.tgz", + "integrity": "sha512-RWUk0lJeTzGR2kRCORa+rcZbgKx2/wQlpQyn/ZonK0n2G1oB+JWCw92zrAlHdFb10k3fzHkPzMYFVxcXQ/U5SA==", "license": "Apache-2.0", "dependencies": { "@twin.org/core": "next", @@ -2507,18 +2524,18 @@ } }, "node_modules/@twin.org/nameof": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/nameof/-/nameof-0.9.2-next.10.tgz", - "integrity": "sha512-r8Uqr+3YcbODdpaG7SoLJC9YQ/WOk2Z1Lbq5yJPBnkMlODj3T/Pen6VaXPd3m6ryqETHg2aQZeUi2Dotrjj0RQ==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/nameof/-/nameof-0.9.2-next.11.tgz", + "integrity": "sha512-s67KEYQ2Kl8lG8NF/EqCCVAe0B8k2/m/Hzh/3EURZzeUEYu5dMOQ5tF0uIUk3nSeI1fUtk3XOGZ2BE9e7lyapg==", "license": "Apache-2.0", "engines": { "node": ">=24.0.0" } }, "node_modules/@twin.org/nameof-transformer": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/nameof-transformer/-/nameof-transformer-0.9.2-next.10.tgz", - "integrity": "sha512-019oKkGTHhy8Mh26/SrJ5Uhbur7HkCLZ6qCsb8TLdyu1LtpD7fD2l9YsRs0Y+IIwEImU5niLcUpRtCyXCbgoSQ==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/nameof-transformer/-/nameof-transformer-0.9.2-next.11.tgz", + "integrity": "sha512-WFbpJe6g9rOpaG5fbBHWmYCAi45pVXc0tnhTeSZAhKU88D32IpJWo24WmF128dXH9PMzWEqX883ia5eL4ZiBTg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2529,13 +2546,13 @@ } }, "node_modules/@twin.org/nameof-vitest-plugin": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/nameof-vitest-plugin/-/nameof-vitest-plugin-0.9.2-next.10.tgz", - "integrity": "sha512-Gm7ge1B7WUGV9IuTzQswBqGT+tYtnVQBOslmsON52O1wnEwGKGfRwoRtBfGn53n9I9MyDT2Xxe1MwN0EzlBaTQ==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/nameof-vitest-plugin/-/nameof-vitest-plugin-0.9.2-next.11.tgz", + "integrity": "sha512-441klgdTJUjawYE1oZWn8kNz7t9KG+7ox89ARCLvXQ8I0nEyifDIYGAFbWem90rnv9QJQAs/HGxhwhc1E8Mdbg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@twin.org/nameof-transformer": "0.9.2-next.10", + "@twin.org/nameof-transformer": "0.9.2-next.11", "vitest": "4.1.10" }, "engines": { @@ -2543,15 +2560,15 @@ } }, "node_modules/@twin.org/tools-core": { - "version": "0.9.2-next.1", - "resolved": "https://registry.npmjs.org/@twin.org/tools-core/-/tools-core-0.9.2-next.1.tgz", - "integrity": "sha512-vOtq/PgaZoFrbTdVggv/U4PUlWb9s+RTAQ8SA5iidVH4ojL8703FYL6cNlxWFsYQG1KM57bl0fcoBbZJLQQ97g==", + "version": "0.9.2-next.2", + "resolved": "https://registry.npmjs.org/@twin.org/tools-core/-/tools-core-0.9.2-next.2.tgz", + "integrity": "sha512-cC3cgZd6AUMChNMQKwOMVi8f4dqA3R589+qRIlchQA3umBSUJtt5KB1/520lzvSgjetllz2OanElMQrjwGpCEA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@twin.org/core": "next", "@twin.org/nameof": "next", - "@twin.org/tools-models": "0.9.2-next.1", + "@twin.org/tools-models": "0.9.2-next.2", "glob": "13.0.6", "typescript": "6.0.3" }, @@ -2560,9 +2577,9 @@ } }, "node_modules/@twin.org/tools-models": { - "version": "0.9.2-next.1", - "resolved": "https://registry.npmjs.org/@twin.org/tools-models/-/tools-models-0.9.2-next.1.tgz", - "integrity": "sha512-RztcPBM5KXS3l9sIEF2rmFIOzbAK9+5lI9D7/IqmJO0wLWFL1r4l9FLMamCatTI8bCx23gBN0ix55/UvApADLg==", + "version": "0.9.2-next.2", + "resolved": "https://registry.npmjs.org/@twin.org/tools-models/-/tools-models-0.9.2-next.2.tgz", + "integrity": "sha512-lW6ZpepTwajtvf2S0/l4pSPo+bMKfgiTO0ge3uAT2TQGz7sIjAhKDIQGgIqujFriztNbpHvXh1jh4V9qohB10A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2574,17 +2591,17 @@ } }, "node_modules/@twin.org/ts-to-openapi": { - "version": "0.9.2-next.1", - "resolved": "https://registry.npmjs.org/@twin.org/ts-to-openapi/-/ts-to-openapi-0.9.2-next.1.tgz", - "integrity": "sha512-ZrxqlL9+5txiVWLO/KMjW0bQJAsBUI1/DaxeJeJTbTzzOegmsaSl+MpyL7jbE7Q0YNmLwFUIxJdsNF1w/gNM5w==", + "version": "0.9.2-next.2", + "resolved": "https://registry.npmjs.org/@twin.org/ts-to-openapi/-/ts-to-openapi-0.9.2-next.2.tgz", + "integrity": "sha512-xJTaXkWyp8AomZjTBTWVcPVQkbCyI3QFyTqmZi7Pi4hvfAMq1d6S+xcwdqXUxkoufEfLKvF2eRvVn7oBlt8sMg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@twin.org/cli-core": "next", "@twin.org/core": "next", "@twin.org/nameof": "next", - "@twin.org/tools-core": "0.9.2-next.1", - "@twin.org/tools-models": "0.9.2-next.1", + "@twin.org/tools-core": "0.9.2-next.2", + "@twin.org/tools-models": "0.9.2-next.2", "@twin.org/web": "next", "commander": "15.0.0", "glob": "13.0.6" @@ -2597,16 +2614,16 @@ } }, "node_modules/@twin.org/validate-locales": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/validate-locales/-/validate-locales-0.9.2-next.10.tgz", - "integrity": "sha512-Qyy7ioR7iYPcnAbb429+Q/TQFK+QZ8xWuNMZ5igiWKxX2Ew3n4A0+0aOA3k2RvRcgw2umoRVWwKKLOz1bcHOAQ==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/validate-locales/-/validate-locales-0.9.2-next.11.tgz", + "integrity": "sha512-G/cQ65EDo+GaAnX6ElZB0hROYe9adsrcGvmMkV4ofzGtPkCb7X2me4r1lJj7OB1Ei5uGRoHMWFg4QaSMHaY2IQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@twin.org/cli-core": "0.9.2-next.10", - "@twin.org/core": "0.9.2-next.10", - "@twin.org/nameof": "0.9.2-next.10", - "@twin.org/nameof-transformer": "0.9.2-next.10", + "@twin.org/cli-core": "0.9.2-next.11", + "@twin.org/core": "0.9.2-next.11", + "@twin.org/nameof": "0.9.2-next.11", + "@twin.org/nameof-transformer": "0.9.2-next.11", "commander": "15.0.0", "glob": "13.0.6", "typescript": "6.0.3" @@ -2633,14 +2650,14 @@ } }, "node_modules/@twin.org/web": { - "version": "0.9.2-next.10", - "resolved": "https://registry.npmjs.org/@twin.org/web/-/web-0.9.2-next.10.tgz", - "integrity": "sha512-KnLM5dPQKz+b4OVxMRCTErfCgevDQGdnHXI7hwL3Sk217TtNDThzkFqoNPVATNfss7ZZXfgHWU92p4WMIKzaGQ==", + "version": "0.9.2-next.11", + "resolved": "https://registry.npmjs.org/@twin.org/web/-/web-0.9.2-next.11.tgz", + "integrity": "sha512-Z0+Z+DA9b1zCfl9pgp7KnrCGEpSe1FTAUJ7ODrCYNg3qgHQ+ky7n3ItAQPm23BcXIAMigOGyCSMDO1sdI8i8yA==", "license": "Apache-2.0", "dependencies": { - "@twin.org/core": "0.9.2-next.10", - "@twin.org/crypto": "0.9.2-next.10", - "@twin.org/nameof": "0.9.2-next.10", + "@twin.org/core": "0.9.2-next.11", + "@twin.org/crypto": "0.9.2-next.11", + "@twin.org/nameof": "0.9.2-next.11", "jose": "6.2.8" }, "engines": { @@ -2739,9 +2756,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", - "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.3.0.tgz", + "integrity": "sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw==", "dev": true, "license": "MIT", "peer": true, @@ -2959,9 +2976,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.67.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", - "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "version": "8.68.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.68.0.tgz", + "integrity": "sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==", "dev": true, "license": "MIT", "engines": { @@ -3624,9 +3641,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.14.tgz", - "integrity": "sha512-T4EDRUBVZYRldYApjEJiU0e1stYWaRAX7CuSnKzrpwdZKo53zGV8/pqfzV6FfwNl9YThD2OumQYvqtvjvgG7aQ==", + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==", "dev": true, "license": "MIT", "engines": { @@ -3830,9 +3847,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.11.14", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.14.tgz", - "integrity": "sha512-JyJ954WzuIR8/FFzX0o5krdSTrBAkcCSRfWSleRsIHSWV+cZe2FI1PKggVkFke1hBldRs+LRxUczzE9iPmgZww==", + "version": "2.11.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", + "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3979,9 +3996,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001809", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", - "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "dev": true, "funding": [ { @@ -4238,26 +4255,26 @@ "license": "MIT" }, "node_modules/conventional-changelog-angular": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.3.0.tgz", - "integrity": "sha512-0MWQLVUT1oVCsUGs9aAWteBVxPlLwJTn5VbQH7B0B3fDizZgrJ9QGnKl/2mp1+5P7153GCBCjO/v1aKJ6eysCg==", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.4.0.tgz", + "integrity": "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==", "dev": true, "license": "ISC", "dependencies": { - "@conventional-changelog/template": "^1.3.0" + "@conventional-changelog/template": "^1.4.0" }, "engines": { "node": ">=22" } }, "node_modules/conventional-changelog-conventionalcommits": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.3.0.tgz", - "integrity": "sha512-qag0zFD867Qq1DK0jAWicyWlEMS1FFC/BLVLISaoeI7Y6Em6aWchk7BCkhOTsecRpMsV6qX41XmlNnghiiTmSw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", + "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", "dev": true, "license": "ISC", "dependencies": { - "@conventional-changelog/template": "^1.3.0" + "@conventional-changelog/template": "^1.4.0" }, "engines": { "node": ">=22" @@ -4299,16 +4316,6 @@ "node": ">=14" } }, - "node_modules/conventional-commits-filter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-6.0.1.tgz", - "integrity": "sha512-cs+LadpH7Kpw0M3k8wurk+sOVVDAENA0iK4OBOrkL94j5lEVYRJ4j3zd2bhY9qgzyrPqthdcYT3axzRN7AliMg==", - "extraneous": true, - "license": "MIT", - "engines": { - "node": ">=22" - } - }, "node_modules/conventional-commits-parser": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", @@ -5080,9 +5087,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.408", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.408.tgz", - "integrity": "sha512-SLoprcYpJ/OH2v2ps0+N5biv9H4/KBT3+YmmDew64TwK5y9j2wv7pMOFY7IorVkyMtEyLSCRlXKLsNlakeAlPw==", + "version": "1.5.413", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.413.tgz", + "integrity": "sha512-F1XPKvt7HVfly5WND90ec16nFsdr4g5x/cVUP3EqjeyXynupabGDqpMa84wwvuYGDnldXLBz6DLXyZXWO9TPvw==", "dev": true, "license": "ISC" }, @@ -5824,9 +5831,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", - "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz", + "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", "dev": true, "funding": [ { @@ -6040,9 +6047,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.2.tgz", - "integrity": "sha512-XpwZALwwl/BaKTAyC6+c5T8y6kCg2jk+XGqOVrKIQmW49pNypYLMRjCUXqa28tQgJlhS2RlzP7sc+Rx7W6qsfw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", "dev": true, "license": "MIT", "dependencies": { @@ -8746,9 +8753,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -9477,13 +9484,13 @@ } }, "node_modules/rolldown": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz", - "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz", + "integrity": "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.144.0", + "@oxc-project/types": "=0.146.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -9493,20 +9500,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.2.4", - "@rolldown/binding-darwin-arm64": "1.2.4", - "@rolldown/binding-darwin-x64": "1.2.4", - "@rolldown/binding-freebsd-x64": "1.2.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", - "@rolldown/binding-linux-arm64-gnu": "1.2.4", - "@rolldown/binding-linux-arm64-musl": "1.2.4", - "@rolldown/binding-linux-ppc64-gnu": "1.2.4", - "@rolldown/binding-linux-s390x-gnu": "1.2.4", - "@rolldown/binding-linux-x64-gnu": "1.2.4", - "@rolldown/binding-linux-x64-musl": "1.2.4", - "@rolldown/binding-openharmony-arm64": "1.2.4", - "@rolldown/binding-win32-arm64-msvc": "1.2.4", - "@rolldown/binding-win32-x64-msvc": "1.2.4" + "@rolldown/binding-android-arm-eabi": "1.2.5", + "@rolldown/binding-android-arm64": "1.2.5", + "@rolldown/binding-darwin-arm64": "1.2.5", + "@rolldown/binding-darwin-x64": "1.2.5", + "@rolldown/binding-freebsd-x64": "1.2.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", + "@rolldown/binding-linux-arm64-gnu": "1.2.5", + "@rolldown/binding-linux-arm64-musl": "1.2.5", + "@rolldown/binding-linux-ppc64-gnu": "1.2.5", + "@rolldown/binding-linux-s390x-gnu": "1.2.5", + "@rolldown/binding-linux-x64-gnu": "1.2.5", + "@rolldown/binding-linux-x64-musl": "1.2.5", + "@rolldown/binding-openharmony-arm64": "1.2.5", + "@rolldown/binding-win32-arm64-msvc": "1.2.5", + "@rolldown/binding-win32-x64-msvc": "1.2.5" } }, "node_modules/run-con": { @@ -10446,16 +10454,16 @@ } }, "node_modules/vite": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", - "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", - "postcss": "^8.5.25", - "rolldown": "~1.2.1", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "bin": { @@ -10472,7 +10480,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -10621,9 +10629,9 @@ "license": "MIT" }, "node_modules/vscode-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", "dev": true, "license": "MIT" }, @@ -10875,7 +10883,7 @@ }, "packages/api-auth-entity-storage-models": { "name": "@twin.org/api-auth-entity-storage-models", - "version": "0.9.2-next.9", + "version": "0.9.3-next.0", "license": "Apache-2.0", "dependencies": { "@twin.org/core": "next", @@ -10913,11 +10921,11 @@ }, "packages/api-auth-entity-storage-rest-client": { "name": "@twin.org/api-auth-entity-storage-rest-client", - "version": "0.9.2-next.9", + "version": "0.9.3-next.0", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-entity-storage-models": "0.9.3-next.0", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", @@ -10955,11 +10963,11 @@ }, "packages/api-auth-entity-storage-service": { "name": "@twin.org/api-auth-entity-storage-service", - "version": "0.9.2-next.9", + "version": "0.9.3-next.0", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-auth-entity-storage-models": "0.9.2-next.9", - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-entity-storage-models": "0.9.3-next.0", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", @@ -11006,7 +11014,7 @@ }, "packages/api-auth-models": { "name": "@twin.org/api-auth-models", - "version": "0.9.2-next.9", + "version": "0.9.3-next.0", "license": "Apache-2.0", "dependencies": { "@twin.org/core": "next", @@ -11043,10 +11051,10 @@ }, "packages/api-auth-rest-client": { "name": "@twin.org/api-auth-rest-client", - "version": "0.9.2-next.9", + "version": "0.9.3-next.0", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", @@ -11084,10 +11092,10 @@ }, "packages/api-auth-service": { "name": "@twin.org/api-auth-service", - "version": "0.9.2-next.9", + "version": "0.9.3-next.0", "license": "Apache-2.0", "dependencies": { - "@twin.org/api-auth-models": "0.9.2-next.9", + "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/background-task-models": "next", From 74ef3e8c6eae56cd9612d6f7c7be7bb6363262bd Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Tue, 25 Aug 2026 16:07:48 +0800 Subject: [PATCH 10/20] feat: update config --- ...enticationAdminServiceConstructorOptions.ts | 2 +- .../entityStorageAuthenticationAdminService.ts | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.ts b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.ts index f9860e2..122dd9e 100644 --- a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.ts +++ b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.ts @@ -13,7 +13,7 @@ export interface IEntityStorageAuthenticationAdminServiceConstructorOptions { userEntityStorageType?: string; /** - * The audit service. + * The audit component. * @default authentication-audit */ authenticationAuditServiceType?: string; diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts index 9e4e032..a46d60b 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts +++ b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts @@ -4,8 +4,8 @@ import type { IAuthenticationAdminComponent, IAuthenticationUser } from "@twin.org/api-auth-entity-storage-models"; -import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-models"; -import { AuthAuditEvent } from "@twin.org/api-auth-models"; +import { type IAuthenticationAuditComponent, AuthAuditEvent } from "@twin.org/api-auth-models"; +import { HttpContextIdKeys } from "@twin.org/api-models"; import { ContextIdKeys, ContextIdStore } from "@twin.org/context"; import { ComponentFactory, @@ -42,7 +42,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA private readonly _userEntityStorage: IEntityStorageConnector; /** - * The audit service. + * The audit component. * @internal */ private readonly _authenticationAuditService?: IAuthenticationAuditComponent; @@ -137,13 +137,15 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA const contextIds = await ContextIdStore.getContextIds(); const requestorTenantId = contextIds?.[ContextIdKeys.Tenant]; + const originalTenantId = contextIds?.[HttpContextIdKeys.OriginalTenant]; await this._authenticationAuditService?.create({ actorId: user.email, event: AuthAuditEvent.AccountCreated, data: { userIdentity: user.userIdentity, organizationIdentity: user.organizationIdentity, - tenantId: requestorTenantId + tenantId: requestorTenantId, + originalTenantId } }); } catch (error) { @@ -217,6 +219,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA const contextIds = await ContextIdStore.getContextIds(); const requestorTenantId = contextIds?.[ContextIdKeys.Tenant]; + const originalTenantId = contextIds?.[HttpContextIdKeys.OriginalTenant]; await this._authenticationAuditService?.create({ actorId: existingUser.email, event: AuthAuditEvent.AccountUpdated, @@ -224,7 +227,8 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA updatedFields, userIdentity: existingUser.identity, organizationIdentity: existingUser.organization, - tenantId: requestorTenantId + tenantId: requestorTenantId, + originalTenantId } }); } catch (error) { @@ -329,13 +333,15 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA const contextIds = await ContextIdStore.getContextIds(); const requestorTenantId = contextIds?.[ContextIdKeys.Tenant]; + const originalTenantId = contextIds?.[HttpContextIdKeys.OriginalTenant]; await this._authenticationAuditService?.create({ actorId: email, event: AuthAuditEvent.AccountDeleted, data: { userIdentity: user.identity, organizationIdentity: user.organization, - tenantId: requestorTenantId + tenantId: requestorTenantId, + originalTenantId } }); } catch (error) { From 4613a66801c88231a5dd397511c7667e4cb5c3de Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Wed, 26 Aug 2026 11:59:46 +0800 Subject: [PATCH 11/20] feat: single default permission --- .../entityStorageAuthenticationAdminRoutes.ts | 27 ++++------------ .../entityStorageAuthenticationRoutes.ts | 18 +++-------- .../src/schema.ts | 31 ++++++++++++++----- .../models/ITenantOverrideProcessorConfig.ts | 6 ++++ .../src/processors/tenantOverrideProcessor.ts | 15 +++++++-- .../entityStorageAuthenticationAuditRoutes.ts | 24 +++----------- .../tenantOverrideProcessor.spec.ts | 12 +++---- 7 files changed, 63 insertions(+), 70 deletions(-) diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts index fe407eb..9715c8b 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts @@ -40,15 +40,6 @@ const DEFAULT_ROUTE_PERMISSIONS_WRITER = { inherits: [DEFAULT_ROUTE_PERMISSIONS_READER] }; -/** - * The default roles for the routes, used to seed authorization rules. - */ -const DEFAULT_ROUTE_ROLES_READER = "user-viewer"; -const DEFAULT_ROUTE_ROLES_WRITER = { - role: "user-admin", - inherits: [DEFAULT_ROUTE_ROLES_READER] -}; - /** * The tag to associate with the routes. */ @@ -102,8 +93,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], - defaultRoles: [DEFAULT_ROUTE_ROLES_READER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER }; const updateUserRoute: IRestRoute = { @@ -140,8 +130,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; const updateUserPasswordRoute: IRestRoute = { @@ -177,8 +166,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; const getUserRoute: IRestRoute = { @@ -224,8 +212,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], - defaultRoles: [DEFAULT_ROUTE_ROLES_READER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER }; const getByIdentityUserRoute: IRestRoute = @@ -272,8 +259,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], - defaultRoles: [DEFAULT_ROUTE_ROLES_READER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER }; const removeUserRoute: IRestRoute = { @@ -306,8 +292,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; return [ diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index 29c3aae..6657a24 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -35,12 +35,6 @@ const DEFAULT_ROUTE_PERMISSIONS_WRITER = { inherits: ["user:read"] }; -/** - * The default roles for the routes, used to seed authorization rules. - */ -const DEFAULT_ROUTE_ROLES_EXECUTE = "user"; -const DEFAULT_ROUTE_ROLES_WRITER = "user"; - /** * The tag to associate with the routes. */ @@ -104,8 +98,7 @@ export function generateRestRoutesAuthentication( } ], skipAuth: true, - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_EXECUTE], - defaultRoles: [DEFAULT_ROUTE_ROLES_EXECUTE] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_EXECUTE }; const logoutRoute: IRestRoute = { @@ -135,8 +128,7 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_EXECUTE], - defaultRoles: [DEFAULT_ROUTE_ROLES_EXECUTE] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_EXECUTE }; const refreshTokenRoute: IRestRoute = { @@ -180,8 +172,7 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_EXECUTE], - defaultRoles: [DEFAULT_ROUTE_ROLES_EXECUTE] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_EXECUTE }; const updatePasswordRoute: IRestRoute = { @@ -215,8 +206,7 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; return [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute]; diff --git a/packages/api-auth-entity-storage-service/src/schema.ts b/packages/api-auth-entity-storage-service/src/schema.ts index eb787ff..fef6652 100644 --- a/packages/api-auth-entity-storage-service/src/schema.ts +++ b/packages/api-auth-entity-storage-service/src/schema.ts @@ -1,5 +1,6 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. +import { ContextIdStore, type IContextIds } from "@twin.org/context"; import { SharedStore } from "@twin.org/core"; import { EntitySchemaFactory, @@ -23,22 +24,38 @@ export function initSchema(): void { ); const migrationV0V1: ISchemaMigration = { - removeEntityProperty: ( + removeEntityProperty: async ( entity: AuthenticationUserV0, removedProperties: IEntitySchemaProperty[] - ): void => { + ): Promise => { // If the scope property is not being removed, we don't need to do anything if (removedProperties.find(prop => prop.property === "scope") === undefined) { return; } + const scopes = entity.scope + .split(",") + .map(role => role.trim().toLocaleLowerCase()) + .filter(role => role.length > 0); + if (scopes.length === 0) { + scopes.push("user"); + } + // We need to store the old roles in here if a migration is performed // they will be picked up by the start method of the AuthenticationService - // and used to populate the new roles table - const migratedRoles = SharedStore.get<{ [id: string]: string[] }>("migrationUserRoles") ?? {}; - migratedRoles[entity.identity] = entity.scope - .split(",") - .map(role => role.trim().toLocaleLowerCase()); + // and used to populate the new roles table in the correct partition. + // The same identity can exist across multiple partitions so we accumulate + // entries as an array keyed by contextIds rather than by identity alone. + const contextIds: IContextIds | undefined = await ContextIdStore.getContextIds(); + const migratedRoles = + SharedStore.get< + { identity: string; roles: string[]; contextIds: IContextIds | undefined }[] + >("migrationUserRoles") ?? []; + migratedRoles.push({ + identity: entity.identity, + roles: scopes, + contextIds + }); SharedStore.set("migrationUserRoles", migratedRoles); } }; diff --git a/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts index 06aa165..db45937 100644 --- a/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts +++ b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts @@ -14,4 +14,10 @@ export interface ITenantOverrideProcessorConfig { * The role value that grants escalated privilege for cross-tenant access. Defaults to "global-admin". */ escalatedPrivilegeRole?: string; + + /** + * The authorization model ID to use for REST requests. + * @default rest + */ + authorizationModelId?: string; } diff --git a/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts index b5d57c8..3b25846 100644 --- a/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts +++ b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts @@ -61,6 +61,12 @@ export class TenantOverrideProcessor implements IBaseRouteProcessor { */ private readonly _escalatedPrivilegeRole: string; + /** + * The authorization model ID to use for REST requests. + * @internal + */ + private readonly _authorizationModelId: string; + /** * Create a new instance of TenantOverrideProcessor. * @param options Options for the processor. @@ -76,6 +82,7 @@ export class TenantOverrideProcessor implements IBaseRouteProcessor { this._escalatedPrivilegeRole = options?.config?.escalatedPrivilegeRole ?? TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE; + this._authorizationModelId = options?.config?.authorizationModelId ?? "rest"; } /** @@ -125,10 +132,14 @@ export class TenantOverrideProcessor implements IBaseRouteProcessor { ); } - const authorizationRoles = await this._authorizationComponent.getRolesForSubject(userId); + const hasRole = await this._authorizationComponent.hasRoleForSubject( + this._authorizationModelId, + userId, + this._escalatedPrivilegeRole + ); // Caller must hold the escalated privilege role. - if (!authorizationRoles.includes(this._escalatedPrivilegeRole)) { + if (!hasRole) { throw new ForbiddenError( TenantOverrideProcessor.CLASS_NAME, "insufficientRoleForTenantOverride" diff --git a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts index 25cfe68..d62fa2f 100644 --- a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts +++ b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts @@ -40,15 +40,6 @@ const DEFAULT_ROUTE_PERMISSIONS_WRITER = { inherits: [DEFAULT_ROUTE_PERMISSIONS_READER] }; -/** - * The default roles for the routes, used to seed authorization rules. - */ -const DEFAULT_ROUTE_ROLES_READER = "user-audit-viewer"; -const DEFAULT_ROUTE_ROLES_WRITER = { - role: "user-audit-admin", - inherits: [DEFAULT_ROUTE_ROLES_READER] -}; - /** * The tag to associate with the routes. */ @@ -115,8 +106,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; const getRoute: IRestRoute = { @@ -166,8 +156,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; const updateRoute: IRestRoute = { @@ -205,8 +194,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; const removeRoute: IRestRoute = { @@ -239,8 +227,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_WRITER], - defaultRoles: [DEFAULT_ROUTE_ROLES_WRITER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER }; const queryRoute: IRestRoute = { @@ -299,8 +286,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermissions: [DEFAULT_ROUTE_PERMISSIONS_READER], - defaultRoles: [DEFAULT_ROUTE_ROLES_READER] + defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER }; return [createRoute, getRoute, updateRoute, removeRoute, queryRoute]; diff --git a/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts index e29b47b..d8fe1fd 100644 --- a/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts +++ b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts @@ -35,9 +35,7 @@ describe("TenantOverrideProcessor", () => { } as unknown as ITenantAdminComponent; mockAuthorizationComponent = { - getRolesForSubject: vi - .fn() - .mockResolvedValue([TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE]) + hasRoleForSubject: vi.fn().mockResolvedValue(true) } as unknown as IAuthorizationComponent; vi.spyOn(ComponentFactory, "get").mockImplementation(type => { @@ -134,11 +132,11 @@ describe("TenantOverrideProcessor", () => { expect(response.statusCode).toBe(HttpStatusCode.forbidden); expect(contextIds[ContextIdKeys.Tenant]).toBe(CALLER_TENANT); - expect(mockAuthorizationComponent.getRolesForSubject).not.toHaveBeenCalled(); + expect(mockAuthorizationComponent.hasRoleForSubject).not.toHaveBeenCalled(); }); it("should return 403 when override-tenant is supplied but caller lacks escalated privilege", async () => { - vi.mocked(mockAuthorizationComponent.getRolesForSubject).mockResolvedValue(["user-admin"]); + vi.mocked(mockAuthorizationComponent.hasRoleForSubject).mockResolvedValue(false); const contextIds: IContextIds = { [ContextIdKeys.Tenant]: CALLER_TENANT, @@ -219,7 +217,7 @@ describe("TenantOverrideProcessor", () => { }); it("should respond with 403 body containing the error name", async () => { - vi.mocked(mockAuthorizationComponent.getRolesForSubject).mockResolvedValue(["user-admin"]); + vi.mocked(mockAuthorizationComponent.hasRoleForSubject).mockResolvedValue(false); const contextIds: IContextIds = { [ContextIdKeys.Tenant]: CALLER_TENANT, @@ -240,7 +238,7 @@ describe("TenantOverrideProcessor", () => { }); it("should accept a custom escalated privilege role when configured", async () => { - vi.mocked(mockAuthorizationComponent.getRolesForSubject).mockResolvedValue(["super-admin"]); + vi.mocked(mockAuthorizationComponent.hasRoleForSubject).mockResolvedValue(true); const customProcessor = new TenantOverrideProcessor({ config: { escalatedPrivilegeRole: "super-admin" } From 3dd78357cea7ddd0359883c93a9813efab1528da Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Wed, 26 Aug 2026 14:21:21 +0800 Subject: [PATCH 12/20] feat: replace permission with authorization --- .../entityStorageAuthenticationAdminRoutes.ts | 28 +++++++++++-------- .../entityStorageAuthenticationRoutes.ts | 21 ++++++++------ .../entityStorageAuthenticationAuditRoutes.ts | 25 ++++++++++------- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts index 9715c8b..e03b011 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts @@ -18,6 +18,7 @@ import { type IHttpRequestContext, type INoContentResponse, type IRestRoute, + type IRouteAuthorization, type ITag, type IUnauthorizedResponse } from "@twin.org/api-models"; @@ -32,14 +33,17 @@ import { HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationAdminRoutes"; /** - * The default permissions for the routes, used to seed authorization rules. + * The default authorization for the routes, used to seed authorization rules. */ -const DEFAULT_ROUTE_PERMISSIONS_READER = "user-admin:read"; -const DEFAULT_ROUTE_PERMISSIONS_WRITER = { - permission: "user-admin:write", - inherits: [DEFAULT_ROUTE_PERMISSIONS_READER] +const DEFAULT_AUTHORIZATION_READER: IRouteAuthorization = { + permission: "user:read", + role: "user-admin" +}; +const DEFAULT_AUTHORIZATION_WRITER: IRouteAuthorization = { + permission: "user:write", + role: "user-admin", + inherits: [DEFAULT_AUTHORIZATION_READER.permission] }; - /** * The tag to associate with the routes. */ @@ -93,7 +97,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; const updateUserRoute: IRestRoute = { @@ -130,7 +134,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; const updateUserPasswordRoute: IRestRoute = { @@ -166,7 +170,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; const getUserRoute: IRestRoute = { @@ -212,7 +216,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; const getByIdentityUserRoute: IRestRoute = @@ -259,7 +263,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; const removeUserRoute: IRestRoute = { @@ -292,7 +296,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; return [ diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index 6657a24..a525720 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -14,6 +14,7 @@ import type { INoContentResponse, IRestRoute, IRestRouteResponseOptions, + IRouteAuthorization, ITag, IUnauthorizedResponse } from "@twin.org/api-models"; @@ -27,12 +28,16 @@ import { HttpStatusCode } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationRoutes"; /** - * The default permissions for the routes, used to seed authorization rules. + * The default authorization for the routes, used to seed authorization rules. */ -const DEFAULT_ROUTE_PERMISSIONS_EXECUTE = "user:execute"; -const DEFAULT_ROUTE_PERMISSIONS_WRITER = { +const DEFAULT_AUTHORIZATION_READER: IRouteAuthorization = { + permission: "user:read", + role: "user" +}; +const DEFAULT_AUTHORIZATION_WRITER: IRouteAuthorization = { permission: "user:write", - inherits: ["user:read"] + role: "user", + inherits: [DEFAULT_AUTHORIZATION_READER.permission] }; /** @@ -98,7 +103,7 @@ export function generateRestRoutesAuthentication( } ], skipAuth: true, - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_EXECUTE + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; const logoutRoute: IRestRoute = { @@ -128,7 +133,7 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_EXECUTE + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; const refreshTokenRoute: IRestRoute = { @@ -172,7 +177,7 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_EXECUTE + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; const updatePasswordRoute: IRestRoute = { @@ -206,7 +211,7 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; return [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute]; diff --git a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts index d62fa2f..d95727a 100644 --- a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts +++ b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts @@ -18,6 +18,7 @@ import { type IHttpRequestContext, type INoContentResponse, type IRestRoute, + type IRouteAuthorization, type ITag, type IUnauthorizedResponse } from "@twin.org/api-models"; @@ -32,12 +33,16 @@ import { HeaderTypes, HttpStatusCode, type IHttpHeaders } from "@twin.org/web"; const ROUTES_SOURCE = "authenticationAuditRoutes"; /** - * The default permissions for the routes, used to seed authorization rules. + * The default authorization for the routes, used to seed authorization rules. */ -const DEFAULT_ROUTE_PERMISSIONS_READER = "user-audit:read"; -const DEFAULT_ROUTE_PERMISSIONS_WRITER = { - permission: "user-audit:write", - inherits: [DEFAULT_ROUTE_PERMISSIONS_READER] +const DEFAULT_AUTHORIZATION_READER: IRouteAuthorization = { + permission: "user:read", + role: "user-admin" +}; +const DEFAULT_AUTHORIZATION_WRITER: IRouteAuthorization = { + permission: "user:write", + role: "user-admin", + inherits: [DEFAULT_AUTHORIZATION_READER.permission] }; /** @@ -106,7 +111,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; const getRoute: IRestRoute = { @@ -156,7 +161,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; const updateRoute: IRestRoute = { @@ -194,7 +199,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; const removeRoute: IRestRoute = { @@ -227,7 +232,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; const queryRoute: IRestRoute = { @@ -286,7 +291,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultPermission: DEFAULT_ROUTE_PERMISSIONS_READER + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; return [createRoute, getRoute, updateRoute, removeRoute, queryRoute]; From fdba65104b8e74316120a9550b03e8f5cd293064 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Wed, 26 Aug 2026 16:31:50 +0800 Subject: [PATCH 13/20] feat: verify scope on migration --- .../src/schema.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/api-auth-entity-storage-service/src/schema.ts b/packages/api-auth-entity-storage-service/src/schema.ts index fef6652..4e03320 100644 --- a/packages/api-auth-entity-storage-service/src/schema.ts +++ b/packages/api-auth-entity-storage-service/src/schema.ts @@ -1,7 +1,7 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. import { ContextIdStore, type IContextIds } from "@twin.org/context"; -import { SharedStore } from "@twin.org/core"; +import { Is, SharedStore } from "@twin.org/core"; import { EntitySchemaFactory, EntitySchemaHelper, @@ -33,12 +33,15 @@ export function initSchema(): void { return; } - const scopes = entity.scope - .split(",") - .map(role => role.trim().toLocaleLowerCase()) - .filter(role => role.length > 0); - if (scopes.length === 0) { - scopes.push("user"); + let roles: string[] = []; + if (Is.stringValue(entity.scope)) { + roles = entity.scope + .split(",") + .map(role => role.trim().toLocaleLowerCase()) + .filter(role => role.length > 0); + } + if (roles.length === 0) { + roles.push("user"); } // We need to store the old roles in here if a migration is performed @@ -53,7 +56,7 @@ export function initSchema(): void { >("migrationUserRoles") ?? []; migratedRoles.push({ identity: entity.identity, - roles: scopes, + roles, contextIds }); SharedStore.set("migrationUserRoles", migratedRoles); From 00d8b48d3e3ad9d3409da274a6cba6cb7b6abaf3 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Wed, 26 Aug 2026 16:43:18 +0800 Subject: [PATCH 14/20] feat: verify scope on migration --- .../interfaces/IAuthenticationUser.md | 8 - .../interfaces/IAuthenticationUserSecure.md | 12 - .../docs/open-api/spec.json | 1246 +++++++++++++++++ .../reference/classes/AuthenticationUser.md | 8 - ...nticationAdminServiceConstructorOptions.md | 2 +- .../classes/TenantOverrideProcessor.md | 119 ++ .../api-auth-service/docs/reference/index.md | 3 + .../ITenantOverrideProcessorConfig.md | 33 + ...nantOverrideProcessorConstructorOptions.md | 39 + 9 files changed, 1441 insertions(+), 29 deletions(-) create mode 100644 packages/api-auth-entity-storage-service/docs/open-api/spec.json create mode 100644 packages/api-auth-service/docs/reference/classes/TenantOverrideProcessor.md create mode 100644 packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConfig.md create mode 100644 packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConstructorOptions.md diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md index 6978a5e..19e3b4b 100644 --- a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md +++ b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUser.md @@ -29,11 +29,3 @@ The user identity. > **organizationIdentity**: `string` The users organization. - -*** - -### roles {#roles} - -> **roles**: `string`[] - -The roles assigned to the user. diff --git a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md index b543c44..2947c9e 100644 --- a/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md +++ b/packages/api-auth-entity-storage-models/docs/reference/interfaces/IAuthenticationUserSecure.md @@ -44,18 +44,6 @@ The users organization. *** -### roles {#roles} - -> **roles**: `string`[] - -The roles assigned to the user. - -#### Inherited from - -[`IAuthenticationUser`](IAuthenticationUser.md).[`roles`](IAuthenticationUser.md#roles) - -*** - ### password {#password} > **password**: `string` diff --git a/packages/api-auth-entity-storage-service/docs/open-api/spec.json b/packages/api-auth-entity-storage-service/docs/open-api/spec.json new file mode 100644 index 0000000..52c3152 --- /dev/null +++ b/packages/api-auth-entity-storage-service/docs/open-api/spec.json @@ -0,0 +1,1246 @@ +{ + "openapi": "3.1.1", + "info": { + "title": "TWIN - Test Endpoints", + "description": "REST API for TWIN - Test Endpoints.", + "version": "1.0.0", + "license": { + "name": "Apache 2.0 License", + "url": "https://opensource.org/licenses/Apache-2.0" + } + }, + "servers": [ + { + "url": "https://localhost" + } + ], + "tags": [ + { + "name": "Authentication", + "description": "Authentication endpoints for the REST server." + }, + { + "name": "Authentication Admin", + "description": "Authentication Admin endpoints for the REST server." + } + ], + "paths": { + "/authentication/login": { + "post": { + "operationId": "authenticationLogin", + "summary": "Login to the server", + "tags": [ + "Authentication" + ], + "requestBody": { + "description": "Perform a login on the server.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginRequest" + }, + "examples": { + "loginRequestExample": { + "summary": "The request to login to the server.", + "value": { + "email": "user@example.com", + "password": "MyPassword123!" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Response from a login on the server.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginResponse" + }, + "examples": { + "loginResponseExample": { + "summary": "The response for the login request.", + "value": { + "expiry": 1722514341067 + } + } + } + } + } + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/logout": { + "post": { + "operationId": "authenticationLogout", + "summary": "Logout from the server", + "tags": [ + "Authentication" + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Perform a logout on the auth token.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LogoutRequest" + }, + "examples": { + "logoutRequestExample": { + "summary": "The request to logout from the server.", + "value": { + "token": "eyJhbGciOiJIU...sw5c" + } + } + } + } + } + }, + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/refresh": { + "post": { + "operationId": "authenticationRefreshToken", + "summary": "Refresh an authentication token", + "tags": [ + "Authentication" + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Perform a refresh of the auth token.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenRequest" + }, + "examples": { + "refreshTokenRequestExample": { + "summary": "The request to refresh an auth token.", + "value": { + "token": "eyJhbGciOiJIU...sw5c" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Response from a refresh on the auth token.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefreshTokenResponse" + }, + "examples": { + "refreshTokenResponseExample": { + "summary": "The response for the refresh token request.", + "value": { + "expiry": 1722514341067 + } + } + } + } + } + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/password": { + "put": { + "operationId": "authenticationUpdatePassword", + "summary": "Update the current user's password", + "tags": [ + "Authentication" + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Update the current user's password.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePasswordRequest" + }, + "examples": { + "updatePasswordRequestExample": { + "summary": "The request to update the current user's password.", + "value": { + "currentPassword": "MyNewPassword123!", + "newPassword": "MyNewPassword123!" + } + } + } + } + } + }, + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/admin/users": { + "post": { + "operationId": "authenticationAdminCreateUser", + "summary": "Create a new user", + "tags": [ + "Authentication Admin" + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Create a new user as an admin.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminUserCreateRequest" + }, + "examples": { + "createUserRequestExample": { + "summary": "The request to create a new user.", + "value": { + "email": "user@example.com", + "password": "MyPassword123!", + "userIdentity": "did:example:123456789abcdefghi", + "organizationIdentity": "did:example:123456789abcdefghi" + } + } + } + } + } + }, + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/admin/users/{email}": { + "put": { + "operationId": "authenticationAdminUpdateUser", + "summary": "Update an existing user", + "tags": [ + "Authentication Admin" + ], + "parameters": [ + { + "name": "email", + "description": "The user email.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "user@example.com" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Update a user as an admin.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminUserUpdateRequest" + }, + "examples": { + "updateUserRequestExample": { + "summary": "The request to update an existing user.", + "value": { + "userIdentity": "did:example:123456789abcdefghi", + "organizationIdentity": "did:example:123456789abcdefghi" + } + } + } + } + } + }, + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + }, + "get": { + "operationId": "authenticationAdminGetUser", + "summary": "Get existing user", + "tags": [ + "Authentication Admin" + ], + "parameters": [ + { + "name": "email", + "description": "The user email.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "user@example.com" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "responses": { + "200": { + "description": "Get a user as an admin.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationUser" + }, + "examples": { + "getUserResponseExample": { + "summary": "The response to get an existing user.", + "value": { + "email": "user@example.com", + "userIdentity": "did:example:123456789abcdefghi", + "organizationIdentity": "did:example:123456789abcdefghi" + } + } + } + } + } + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + }, + "delete": { + "operationId": "authenticationAdminRemoveUser", + "summary": "Remove existing user", + "tags": [ + "Authentication Admin" + ], + "parameters": [ + { + "name": "email", + "description": "The user email.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "user@example.com" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/admin/users/{email}/password": { + "put": { + "operationId": "authenticationAdminUpdateUserPassword", + "summary": "Update an existing user password", + "tags": [ + "Authentication Admin" + ], + "parameters": [ + { + "name": "email", + "description": "The user email.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "user@example.com" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "requestBody": { + "description": "Update a users password as an admin.", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminUserUpdatePasswordRequest" + }, + "examples": { + "updateUserPasswordRequestExample": { + "summary": "The request to update an existing user password.", + "value": { + "newPassword": "MyNewPassword123!" + } + } + } + } + } + }, + "responses": { + "204": { + "description": "The rest request ended in success with no data." + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + }, + "/authentication/admin/users/identity/{identity}": { + "get": { + "operationId": "authenticationAdminGetByIdentityUser", + "summary": "Get existing user by identity", + "tags": [ + "Authentication Admin" + ], + "parameters": [ + { + "name": "identity", + "description": "The user identity.", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "style": "simple", + "example": "did:example:123456789abcdefghi" + } + ], + "security": [ + { + "jwtBearerAuthScheme": [] + } + ], + "responses": { + "200": { + "description": "Get a user as an admin.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthenticationUser" + }, + "examples": { + "getUserResponseExample": { + "summary": "The response to get an existing user.", + "value": { + "email": "user@example.com", + "userIdentity": "did:example:123456789abcdefghi", + "organizationIdentity": "did:example:123456789abcdefghi" + } + } + } + } + } + }, + "400": { + "description": "The server cannot process the request, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "GeneralError", + "message": "errorMessage", + "properties": { + "foo": "bar" + } + } + } + } + } + } + }, + "401": { + "description": "You are not authorized to use the API or no credentials were supplied, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "UnauthorizedError", + "message": "errorMessage" + } + } + } + } + } + }, + "500": { + "description": "The server has encountered a situation it does not know how to handle, see the content for more details.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "exampleResponse": { + "value": { + "name": "InternalServerError", + "message": "errorMessage" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AdminUserCreateRequest": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "required": [ + "password" + ], + "allOf": [ + { + "$ref": "#/components/schemas/AuthenticationUser" + } + ], + "description": "The body of the request." + }, + "AdminUserUpdatePasswordRequest": { + "type": "object", + "properties": { + "newPassword": { + "type": "string", + "description": "The new password for the user." + }, + "currentPassword": { + "type": "string", + "description": "The current password for the user." + } + }, + "required": [ + "newPassword" + ], + "description": "The body of the request." + }, + "AdminUserUpdateRequest": { + "description": "The body of the request.", + "type": "object", + "properties": { + "userIdentity": { + "type": "string", + "description": "The user identity." + }, + "organizationIdentity": { + "type": "string", + "description": "The users organization." + } + } + }, + "AuthenticationUser": { + "description": "Contract definition for authentication user.", + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "The user e-mail address." + }, + "userIdentity": { + "type": "string", + "description": "The user identity." + }, + "organizationIdentity": { + "type": "string", + "description": "The users organization." + } + }, + "required": [ + "email", + "userIdentity", + "organizationIdentity" + ] + }, + "Error": { + "description": "Model to describe serialized error.", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name for the error." + }, + "message": { + "type": "string", + "description": "The message for the error as an i18n key." + }, + "source": { + "type": "string", + "description": "The source of the error." + }, + "properties": { + "type": "object", + "additionalProperties": {}, + "description": "Any additional information for the error." + }, + "stack": { + "type": "string", + "description": "The stack trace for the error." + }, + "cause": { + "$ref": "#/components/schemas/Error" + } + }, + "required": [ + "name", + "message" + ] + }, + "LoginRequest": { + "type": "object", + "properties": { + "email": { + "type": "string", + "description": "The email address for the user." + }, + "password": { + "type": "string", + "description": "The password for the user." + } + }, + "required": [ + "email", + "password" + ], + "description": "The login details." + }, + "LoginResponse": { + "type": "object", + "properties": { + "expiry": { + "type": "number", + "description": "The expiry time of the token." + } + }, + "required": [ + "expiry" + ], + "description": "The login response details." + }, + "LogoutRequest": { + "type": "object", + "properties": { + "token": { + "type": "string", + "description": "The token to logout, if it uses a mechanism with public access." + } + }, + "description": "The logout token details." + }, + "RefreshTokenRequest": { + "type": "object", + "properties": { + "token": { + "type": "string", + "description": "The token to refresh, if it uses a mechanism with public access." + } + }, + "description": "The refresh token details." + }, + "RefreshTokenResponse": { + "type": "object", + "properties": { + "expiry": { + "type": "number", + "description": "The expiry time of the token." + } + }, + "required": [ + "expiry" + ], + "description": "The refresh token details." + }, + "UpdatePasswordRequest": { + "type": "object", + "properties": { + "currentPassword": { + "type": "string", + "description": "The current password for the user." + }, + "newPassword": { + "type": "string", + "description": "The new password for the user." + } + }, + "required": [ + "currentPassword", + "newPassword" + ], + "description": "The body of the request." + } + }, + "securitySchemes": { + "jwtBearerAuthScheme": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } + } +} diff --git a/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md b/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md index 9d7c1f1..77026f8 100644 --- a/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md +++ b/packages/api-auth-entity-storage-service/docs/reference/classes/AuthenticationUser.md @@ -54,14 +54,6 @@ The users organization. *** -### roles {#roles} - -> **roles**: `string` - -The roles assigned to the user, bar separated. - -*** - ### passwordVersion? {#passwordversion} > `optional` **passwordVersion?**: `number` diff --git a/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md b/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md index a0f47de..d30fb0a 100644 --- a/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md +++ b/packages/api-auth-entity-storage-service/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md @@ -22,7 +22,7 @@ authentication-user > `optional` **authenticationAuditServiceType?**: `string` -The audit service. +The audit component. #### Default diff --git a/packages/api-auth-service/docs/reference/classes/TenantOverrideProcessor.md b/packages/api-auth-service/docs/reference/classes/TenantOverrideProcessor.md new file mode 100644 index 0000000..365f42f --- /dev/null +++ b/packages/api-auth-service/docs/reference/classes/TenantOverrideProcessor.md @@ -0,0 +1,119 @@ +# Class: TenantOverrideProcessor + +Processes the overrideTenant query parameter for routes that allow tenant override. +Requires the caller to hold the escalated privilege role. Runs after AuthHeaderProcessor so +authentication is always resolved in the caller's own partition first. + +## Implements + +- `IBaseRouteProcessor` + +## Constructors + +### Constructor + +> **new TenantOverrideProcessor**(`options?`): `TenantOverrideProcessor` + +Create a new instance of TenantOverrideProcessor. + +#### Parameters + +##### options? + +[`ITenantOverrideProcessorConstructorOptions`](../interfaces/ITenantOverrideProcessorConstructorOptions.md) + +Options for the processor. + +#### Returns + +`TenantOverrideProcessor` + +## Properties + +### OVERRIDE\_TENANT\_PARAM {#override_tenant_param} + +> `readonly` `static` **OVERRIDE\_TENANT\_PARAM**: `string` = `"override-tenant"` + +The query parameter name used to supply the override tenant ID. + +*** + +### DEFAULT\_ESCALATED\_PRIVILEGE\_ROLE {#default_escalated_privilege_role} + +> `readonly` `static` **DEFAULT\_ESCALATED\_PRIVILEGE\_ROLE**: `string` = `"global-admin"` + +The role string required to perform a tenant override. + +*** + +### CLASS\_NAME {#class_name} + +> `readonly` `static` **CLASS\_NAME**: `string` + +Runtime name for the class. + +## Methods + +### className() {#classname} + +> **className**(): `string` + +Returns the class name of the component. + +#### Returns + +`string` + +The class name of the component. + +#### Implementation of + +`IBaseRouteProcessor.className` + +*** + +### pre() {#pre} + +> **pre**(`request`, `response`, `route`, `contextIds`, `processorState`): `Promise`\<`void`\> + +Pre process the REST request for the specified route. + +#### Parameters + +##### request + +`IHttpServerRequest` + +The incoming request. + +##### response + +`IHttpResponse` + +The outgoing response. + +##### route + +`IBaseRoute` \| `undefined` + +The route to process. + +##### contextIds + +`IContextIds` + +The context IDs of the request. + +##### processorState + +The state handed through the processors. + +#### Returns + +`Promise`\<`void`\> + +A promise that resolves when the tenant override has been applied or skipped. + +#### Implementation of + +`IBaseRouteProcessor.pre` diff --git a/packages/api-auth-service/docs/reference/index.md b/packages/api-auth-service/docs/reference/index.md index 6cece89..7f31602 100644 --- a/packages/api-auth-service/docs/reference/index.md +++ b/packages/api-auth-service/docs/reference/index.md @@ -4,6 +4,7 @@ - [AuthenticationAuditEntry](classes/AuthenticationAuditEntry.md) - [AuthenticationRateEntry](classes/AuthenticationRateEntry.md) +- [TenantOverrideProcessor](classes/TenantOverrideProcessor.md) - [EntityStorageAuthenticationAuditService](classes/EntityStorageAuthenticationAuditService.md) - [EntityStorageAuthenticationRateService](classes/EntityStorageAuthenticationRateService.md) @@ -13,6 +14,8 @@ - [IEntityStorageAuthenticationAuditServiceConstructorOptions](interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md) - [IEntityStorageAuthenticationRateServiceConfig](interfaces/IEntityStorageAuthenticationRateServiceConfig.md) - [IEntityStorageAuthenticationRateServiceConstructorOptions](interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md) +- [ITenantOverrideProcessorConfig](interfaces/ITenantOverrideProcessorConfig.md) +- [ITenantOverrideProcessorConstructorOptions](interfaces/ITenantOverrideProcessorConstructorOptions.md) ## Variables diff --git a/packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConfig.md b/packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConfig.md new file mode 100644 index 0000000..01a2144 --- /dev/null +++ b/packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConfig.md @@ -0,0 +1,33 @@ +# Interface: ITenantOverrideProcessorConfig + +Configuration for the TenantOverrideProcessor. + +## Properties + +### includeErrorStack? {#includeerrorstack} + +> `optional` **includeErrorStack?**: `boolean` + +Include the stack with errors. + +*** + +### escalatedPrivilegeRole? {#escalatedprivilegerole} + +> `optional` **escalatedPrivilegeRole?**: `string` + +The role value that grants escalated privilege for cross-tenant access. Defaults to "global-admin". + +*** + +### authorizationModelId? {#authorizationmodelid} + +> `optional` **authorizationModelId?**: `string` + +The authorization model ID to use for REST requests. + +#### Default + +```ts +rest +``` diff --git a/packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConstructorOptions.md b/packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConstructorOptions.md new file mode 100644 index 0000000..0454111 --- /dev/null +++ b/packages/api-auth-service/docs/reference/interfaces/ITenantOverrideProcessorConstructorOptions.md @@ -0,0 +1,39 @@ +# Interface: ITenantOverrideProcessorConstructorOptions + +Options for the TenantOverrideProcessor constructor. + +## Properties + +### tenantAdminComponentType? {#tenantadmincomponenttype} + +> `optional` **tenantAdminComponentType?**: `string` + +The tenant admin component type. + +#### Default + +```ts +tenant-admin +``` + +*** + +### authorizationComponentType? {#authorizationcomponenttype} + +> `optional` **authorizationComponentType?**: `string` + +The authorization component type. + +#### Default + +```ts +authorization +``` + +*** + +### config? {#config} + +> `optional` **config?**: [`ITenantOverrideProcessorConfig`](ITenantOverrideProcessorConfig.md) + +Configuration for the processor. From d87decb187080df941efbdb7b0048ca0b55437e2 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Thu, 27 Aug 2026 11:45:02 +0800 Subject: [PATCH 15/20] feat: route permissions --- .../entityStorageAuthenticationAdminRoutes.ts | 2 +- .../entityStorageAuthenticationRoutes.ts | 25 +++---------------- .../entityStorageAuthenticationAuditRoutes.ts | 2 +- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts index e03b011..b87f2a7 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts @@ -97,7 +97,7 @@ export function generateRestRoutesAuthenticationAdmin( type: nameof() } ], - defaultAuthorization: DEFAULT_AUTHORIZATION_READER + defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER }; const updateUserRoute: IRestRoute = { diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index a525720..9a08b2e 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -27,19 +27,6 @@ import { HttpStatusCode } from "@twin.org/web"; */ const ROUTES_SOURCE = "authenticationRoutes"; -/** - * The default authorization for the routes, used to seed authorization rules. - */ -const DEFAULT_AUTHORIZATION_READER: IRouteAuthorization = { - permission: "user:read", - role: "user" -}; -const DEFAULT_AUTHORIZATION_WRITER: IRouteAuthorization = { - permission: "user:write", - role: "user", - inherits: [DEFAULT_AUTHORIZATION_READER.permission] -}; - /** * The tag to associate with the routes. */ @@ -102,8 +89,7 @@ export function generateRestRoutesAuthentication( type: nameof() } ], - skipAuth: true, - defaultAuthorization: DEFAULT_AUTHORIZATION_READER + skipAuth: true }; const logoutRoute: IRestRoute = { @@ -132,8 +118,7 @@ export function generateRestRoutesAuthentication( { type: nameof() } - ], - defaultAuthorization: DEFAULT_AUTHORIZATION_READER + ] }; const refreshTokenRoute: IRestRoute = { @@ -176,8 +161,7 @@ export function generateRestRoutesAuthentication( { type: nameof() } - ], - defaultAuthorization: DEFAULT_AUTHORIZATION_READER + ] }; const updatePasswordRoute: IRestRoute = { @@ -210,8 +194,7 @@ export function generateRestRoutesAuthentication( { type: nameof() } - ], - defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER + ] }; return [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute]; diff --git a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts index d95727a..10cc964 100644 --- a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts +++ b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts @@ -161,7 +161,7 @@ export function generateRestRoutesAuthenticationAudit( type: nameof() } ], - defaultAuthorization: DEFAULT_AUTHORIZATION_WRITER + defaultAuthorization: DEFAULT_AUTHORIZATION_READER }; const updateRoute: IRestRoute = { From f23b1712730af61c633e3f51548416e7517f41a9 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Fri, 28 Aug 2026 11:49:51 +0800 Subject: [PATCH 16/20] feat: pr comments --- .../src/index.ts | 1 + .../entityStorageAuthenticationAdminRoutes.ts | 4 +- .../entityStorageAuthenticationRoutes.ts | 1 - .../src/schema.ts | 53 +------------------ .../models/ITenantOverrideProcessorConfig.ts | 3 +- .../entityStorageAuthenticationAuditRoutes.ts | 4 +- .../tenantOverrideProcessor.spec.ts | 35 ++++++++++++ 7 files changed, 43 insertions(+), 58 deletions(-) diff --git a/packages/api-auth-entity-storage-service/src/index.ts b/packages/api-auth-entity-storage-service/src/index.ts index 9c701b5..d7b8c1a 100644 --- a/packages/api-auth-entity-storage-service/src/index.ts +++ b/packages/api-auth-entity-storage-service/src/index.ts @@ -1,6 +1,7 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. export * from "./entities/authenticationUser.js"; +export * from "./entities/authenticationUserV0.js"; export * from "./models/IAuthHeaderProcessorConfig.js"; export * from "./models/IAuthHeaderProcessorConstructorOptions.js"; export * from "./models/IEntityStorageAuthenticationAdminServiceConfig.js"; diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts index b87f2a7..d1b52b7 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationAdminRoutes.ts @@ -36,11 +36,11 @@ const ROUTES_SOURCE = "authenticationAdminRoutes"; * The default authorization for the routes, used to seed authorization rules. */ const DEFAULT_AUTHORIZATION_READER: IRouteAuthorization = { - permission: "user:read", + permission: "user-admin:read", role: "user-admin" }; const DEFAULT_AUTHORIZATION_WRITER: IRouteAuthorization = { - permission: "user:write", + permission: "user-admin:write", role: "user-admin", inherits: [DEFAULT_AUTHORIZATION_READER.permission] }; diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index 9a08b2e..0ac00a7 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -14,7 +14,6 @@ import type { INoContentResponse, IRestRoute, IRestRouteResponseOptions, - IRouteAuthorization, ITag, IUnauthorizedResponse } from "@twin.org/api-models"; diff --git a/packages/api-auth-entity-storage-service/src/schema.ts b/packages/api-auth-entity-storage-service/src/schema.ts index 4e03320..4fc0f01 100644 --- a/packages/api-auth-entity-storage-service/src/schema.ts +++ b/packages/api-auth-entity-storage-service/src/schema.ts @@ -1,13 +1,6 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. -import { ContextIdStore, type IContextIds } from "@twin.org/context"; -import { Is, SharedStore } from "@twin.org/core"; -import { - EntitySchemaFactory, - EntitySchemaHelper, - type IEntitySchemaProperty -} from "@twin.org/entity"; -import { type ISchemaMigration, SchemaMigrationFactory } from "@twin.org/entity-storage-models"; +import { EntitySchemaFactory, EntitySchemaHelper } from "@twin.org/entity"; import { nameof } from "@twin.org/nameof"; import { AuthenticationUser } from "./entities/authenticationUser.js"; import { AuthenticationUserV0 } from "./entities/authenticationUserV0.js"; @@ -22,48 +15,4 @@ export function initSchema(): void { EntitySchemaFactory.register(nameof(), () => EntitySchemaHelper.getSchema(AuthenticationUserV0) ); - - const migrationV0V1: ISchemaMigration = { - removeEntityProperty: async ( - entity: AuthenticationUserV0, - removedProperties: IEntitySchemaProperty[] - ): Promise => { - // If the scope property is not being removed, we don't need to do anything - if (removedProperties.find(prop => prop.property === "scope") === undefined) { - return; - } - - let roles: string[] = []; - if (Is.stringValue(entity.scope)) { - roles = entity.scope - .split(",") - .map(role => role.trim().toLocaleLowerCase()) - .filter(role => role.length > 0); - } - if (roles.length === 0) { - roles.push("user"); - } - - // We need to store the old roles in here if a migration is performed - // they will be picked up by the start method of the AuthenticationService - // and used to populate the new roles table in the correct partition. - // The same identity can exist across multiple partitions so we accumulate - // entries as an array keyed by contextIds rather than by identity alone. - const contextIds: IContextIds | undefined = await ContextIdStore.getContextIds(); - const migratedRoles = - SharedStore.get< - { identity: string; roles: string[]; contextIds: IContextIds | undefined }[] - >("migrationUserRoles") ?? []; - migratedRoles.push({ - identity: entity.identity, - roles, - contextIds - }); - SharedStore.set("migrationUserRoles", migratedRoles); - } - }; - SchemaMigrationFactory.register( - `${nameof()}_0_1`, - () => migrationV0V1 as ISchemaMigration - ); } diff --git a/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts index db45937..53e6216 100644 --- a/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts +++ b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts @@ -11,7 +11,8 @@ export interface ITenantOverrideProcessorConfig { includeErrorStack?: boolean; /** - * The role value that grants escalated privilege for cross-tenant access. Defaults to "global-admin". + * The role value that grants escalated privilege for cross-tenant access. + * @default global-admin */ escalatedPrivilegeRole?: string; diff --git a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts index 10cc964..3b4b25e 100644 --- a/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts +++ b/packages/api-auth-service/src/routes/entityStorageAuthenticationAuditRoutes.ts @@ -36,11 +36,11 @@ const ROUTES_SOURCE = "authenticationAuditRoutes"; * The default authorization for the routes, used to seed authorization rules. */ const DEFAULT_AUTHORIZATION_READER: IRouteAuthorization = { - permission: "user:read", + permission: "user-admin:read", role: "user-admin" }; const DEFAULT_AUTHORIZATION_WRITER: IRouteAuthorization = { - permission: "user:write", + permission: "user-admin:write", role: "user-admin", inherits: [DEFAULT_AUTHORIZATION_READER.permission] }; diff --git a/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts index d8fe1fd..44c2091 100644 --- a/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts +++ b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts @@ -214,6 +214,11 @@ describe("TenantOverrideProcessor", () => { expect(contextIds[ContextIdKeys.Tenant]).toBe(OVERRIDE_TENANT); expect(contextIds[HttpContextIdKeys.OriginalTenant]).toBe(CALLER_TENANT); expect(mockTenantAdminComponent.get).toHaveBeenCalledWith(OVERRIDE_TENANT); + expect(mockAuthorizationComponent.hasRoleForSubject).toHaveBeenCalledWith( + "rest", + CALLER_USER, + TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE + ); }); it("should respond with 403 body containing the error name", async () => { @@ -258,5 +263,35 @@ describe("TenantOverrideProcessor", () => { expect(response.statusCode).toBeUndefined(); expect(contextIds[ContextIdKeys.Tenant]).toBe(OVERRIDE_TENANT); + expect(mockAuthorizationComponent.hasRoleForSubject).toHaveBeenCalledWith( + "rest", + CALLER_USER, + "super-admin" + ); + }); + + it("should use a custom authorizationModelId when configured", async () => { + const customProcessor = new TenantOverrideProcessor({ + config: { authorizationModelId: "custom-model" } + }); + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = {}; + await customProcessor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(response.statusCode).toBeUndefined(); + expect(mockAuthorizationComponent.hasRoleForSubject).toHaveBeenCalledWith( + "custom-model", + CALLER_USER, + TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE + ); }); }); From cbacb1a6a797111d826af018b0d09bc9777f818d Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Fri, 28 Aug 2026 12:00:34 +0800 Subject: [PATCH 17/20] feat: pr comments --- packages/api-auth-entity-storage-rest-client/package.json | 1 - packages/api-auth-entity-storage-service/package.json | 2 -- packages/api-auth-service/package.json | 2 -- .../src/processors/tenantOverrideProcessor.ts | 4 +++- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/api-auth-entity-storage-rest-client/package.json b/packages/api-auth-entity-storage-rest-client/package.json index e952890..7e1ff86 100644 --- a/packages/api-auth-entity-storage-rest-client/package.json +++ b/packages/api-auth-entity-storage-rest-client/package.json @@ -29,7 +29,6 @@ }, "dependencies": { "@twin.org/api-auth-entity-storage-models": "0.9.3-next.0", - "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-entity-storage-service/package.json b/packages/api-auth-entity-storage-service/package.json index fe0daf6..e515777 100644 --- a/packages/api-auth-entity-storage-service/package.json +++ b/packages/api-auth-entity-storage-service/package.json @@ -33,13 +33,11 @@ "@twin.org/api-auth-models": "0.9.3-next.0", "@twin.org/api-core": "next", "@twin.org/api-models": "next", - "@twin.org/background-task-models": "next", "@twin.org/context": "next", "@twin.org/core": "next", "@twin.org/crypto": "next", "@twin.org/entity": "next", "@twin.org/entity-storage-models": "next", - "@twin.org/logging-models": "next", "@twin.org/nameof": "next", "@twin.org/vault-models": "next", "@twin.org/web": "next" diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json index f583387..81e04d4 100644 --- a/packages/api-auth-service/package.json +++ b/packages/api-auth-service/package.json @@ -39,9 +39,7 @@ "@twin.org/crypto": "next", "@twin.org/entity": "next", "@twin.org/entity-storage-models": "next", - "@twin.org/logging-models": "next", "@twin.org/nameof": "next", - "@twin.org/vault-models": "next", "@twin.org/web": "next" }, "devDependencies": { diff --git a/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts index 3b25846..16826da 100644 --- a/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts +++ b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts @@ -14,6 +14,7 @@ import type { IAuthorizationComponent } from "@twin.org/authorization-models"; import { ContextIdKeys, type IContextIds } from "@twin.org/context"; import { BaseError, ComponentFactory, Is, NotFoundError } from "@twin.org/core"; import { nameof } from "@twin.org/nameof"; +import { HttpStatusCode } from "@twin.org/web"; import type { ITenantOverrideProcessorConstructorOptions } from "../models/ITenantOverrideProcessorConstructorOptions.js"; /** @@ -119,7 +120,7 @@ export class TenantOverrideProcessor implements IBaseRouteProcessor { } // If a prior processor already set an error response, skip processing. - if (Is.notEmpty(response.statusCode)) { + if (Is.notEmpty(response.statusCode) && response.statusCode >= HttpStatusCode.badRequest) { return; } @@ -132,6 +133,7 @@ export class TenantOverrideProcessor implements IBaseRouteProcessor { ); } + // Role must be directly assigned to the user, not inherited through a group or other means. const hasRole = await this._authorizationComponent.hasRoleForSubject( this._authorizationModelId, userId, From 54e8b3dd6c411c0db57ed1552408d2318da3cb10 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Wed, 2 Sep 2026 11:55:57 +0800 Subject: [PATCH 18/20] feat: pr comments --- .../package.json | 1 - .../src/entities/authenticationUserV0.ts | 2 +- packages/api-auth-service/package.json | 1 - .../models/ITenantOverrideProcessorConfig.ts | 2 +- .../src/processors/tenantOverrideProcessor.ts | 9 +++++++- .../tenantOverrideProcessor.spec.ts | 23 +++++++++++++++++-- 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/api-auth-entity-storage-service/package.json b/packages/api-auth-entity-storage-service/package.json index e515777..25fe16a 100644 --- a/packages/api-auth-entity-storage-service/package.json +++ b/packages/api-auth-entity-storage-service/package.json @@ -31,7 +31,6 @@ "dependencies": { "@twin.org/api-auth-entity-storage-models": "0.9.3-next.0", "@twin.org/api-auth-models": "0.9.3-next.0", - "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/context": "next", "@twin.org/core": "next", diff --git a/packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts b/packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts index 767ec93..b3e0ef3 100644 --- a/packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts +++ b/packages/api-auth-entity-storage-service/src/entities/authenticationUserV0.ts @@ -1,4 +1,4 @@ -// Copyright 2024 IOTA Stiftung. +// Copyright 2026 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. import { entity, property } from "@twin.org/entity"; diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json index 81e04d4..16b5be0 100644 --- a/packages/api-auth-service/package.json +++ b/packages/api-auth-service/package.json @@ -30,7 +30,6 @@ }, "dependencies": { "@twin.org/api-auth-models": "0.9.3-next.0", - "@twin.org/api-core": "next", "@twin.org/api-models": "next", "@twin.org/authorization-models": "next", "@twin.org/background-task-models": "next", diff --git a/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts index 53e6216..8d559a1 100644 --- a/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts +++ b/packages/api-auth-service/src/models/ITenantOverrideProcessorConfig.ts @@ -18,7 +18,7 @@ export interface ITenantOverrideProcessorConfig { /** * The authorization model ID to use for REST requests. - * @default rest + * @default system */ authorizationModelId?: string; } diff --git a/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts index 16826da..9ffbe56 100644 --- a/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts +++ b/packages/api-auth-service/src/processors/tenantOverrideProcessor.ts @@ -28,6 +28,11 @@ export class TenantOverrideProcessor implements IBaseRouteProcessor { */ public static readonly OVERRIDE_TENANT_PARAM: string = "override-tenant"; + /** + * The model identifier that is reserved for system-only use. + */ + public static readonly DEFAULT_AUTHORIZATION_MODEL_ID: string = "system"; + /** * The role string required to perform a tenant override. */ @@ -83,7 +88,9 @@ export class TenantOverrideProcessor implements IBaseRouteProcessor { this._escalatedPrivilegeRole = options?.config?.escalatedPrivilegeRole ?? TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE; - this._authorizationModelId = options?.config?.authorizationModelId ?? "rest"; + this._authorizationModelId = + options?.config?.authorizationModelId ?? + TenantOverrideProcessor.DEFAULT_AUTHORIZATION_MODEL_ID; } /** diff --git a/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts index 44c2091..29bcc12 100644 --- a/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts +++ b/packages/api-auth-service/tests/processors/tenantOverrideProcessor.spec.ts @@ -119,6 +119,25 @@ describe("TenantOverrideProcessor", () => { expect(mockTenantAdminComponent.get).not.toHaveBeenCalled(); }); + it("should proceed with override when response already has a 2xx status code", async () => { + const contextIds: IContextIds = { + [ContextIdKeys.Tenant]: CALLER_TENANT, + [ContextIdKeys.User]: CALLER_USER + }; + const response: IHttpResponse = { statusCode: HttpStatusCode.ok }; + await processor.pre( + { query: { "override-tenant": OVERRIDE_TENANT } } as unknown as IHttpServerRequest, + response, + { operationId: "tenantGetById", path: "/" }, + contextIds, + {} + ); + + expect(contextIds[ContextIdKeys.Tenant]).toBe(OVERRIDE_TENANT); + expect(contextIds[HttpContextIdKeys.OriginalTenant]).toBe(CALLER_TENANT); + expect(mockTenantAdminComponent.get).toHaveBeenCalledWith(OVERRIDE_TENANT); + }); + it("should return 403 when caller user ID is not set in context", async () => { const contextIds: IContextIds = { [ContextIdKeys.Tenant]: CALLER_TENANT }; const response: IHttpResponse = {}; @@ -215,7 +234,7 @@ describe("TenantOverrideProcessor", () => { expect(contextIds[HttpContextIdKeys.OriginalTenant]).toBe(CALLER_TENANT); expect(mockTenantAdminComponent.get).toHaveBeenCalledWith(OVERRIDE_TENANT); expect(mockAuthorizationComponent.hasRoleForSubject).toHaveBeenCalledWith( - "rest", + "system", CALLER_USER, TenantOverrideProcessor.DEFAULT_ESCALATED_PRIVILEGE_ROLE ); @@ -264,7 +283,7 @@ describe("TenantOverrideProcessor", () => { expect(response.statusCode).toBeUndefined(); expect(contextIds[ContextIdKeys.Tenant]).toBe(OVERRIDE_TENANT); expect(mockAuthorizationComponent.hasRoleForSubject).toHaveBeenCalledWith( - "rest", + "system", CALLER_USER, "super-admin" ); From 5f66f15e3b88e81f2254804cc6903e137ece9bcf Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Thu, 3 Sep 2026 08:50:14 +0800 Subject: [PATCH 19/20] feat: add password policy endpoint --- .../src/index.ts | 2 + .../src/models/IAuthenticationComponent.ts | 7 +++ .../src/models/IPasswordOptions.ts | 17 ++++++ .../src/models/api/IPasswordPolicyResponse.ts | 13 +++++ .../entityStorageAuthenticationRestClient.ts | 14 ++++- ...ityStorageAuthenticationRestClient.spec.ts | 24 ++++++++ ...StorageAuthenticationAdminServiceConfig.ts | 8 ++- ...ntityStorageAuthenticationServiceConfig.ts | 8 ++- .../entityStorageAuthenticationRoutes.ts | 56 ++++++++++++++++++- ...entityStorageAuthenticationAdminService.ts | 15 ++++- .../entityStorageAuthenticationService.ts | 28 +++++++++- .../src/utils/passwordHelper.ts | 10 +++- ...entityStorageAuthenticationService.spec.ts | 4 +- 13 files changed, 193 insertions(+), 13 deletions(-) create mode 100644 packages/api-auth-entity-storage-models/src/models/IPasswordOptions.ts create mode 100644 packages/api-auth-entity-storage-models/src/models/api/IPasswordPolicyResponse.ts diff --git a/packages/api-auth-entity-storage-models/src/index.ts b/packages/api-auth-entity-storage-models/src/index.ts index eb7db4c..4e984d2 100644 --- a/packages/api-auth-entity-storage-models/src/index.ts +++ b/packages/api-auth-entity-storage-models/src/index.ts @@ -10,6 +10,7 @@ export * from "./models/api/IAdminUserUpdateRequest.js"; export * from "./models/api/ILoginRequest.js"; export * from "./models/api/ILoginResponse.js"; export * from "./models/api/ILogoutRequest.js"; +export * from "./models/api/IPasswordPolicyResponse.js"; export * from "./models/api/IRefreshTokenRequest.js"; export * from "./models/api/IRefreshTokenResponse.js"; export * from "./models/api/IUpdatePasswordRequest.js"; @@ -17,3 +18,4 @@ export * from "./models/IAuthenticationAdminComponent.js"; export * from "./models/IAuthenticationComponent.js"; export * from "./models/IAuthenticationUser.js"; export * from "./models/IAuthenticationUserSecure.js"; +export * from "./models/IPasswordOptions.js"; diff --git a/packages/api-auth-entity-storage-models/src/models/IAuthenticationComponent.ts b/packages/api-auth-entity-storage-models/src/models/IAuthenticationComponent.ts index eda2985..5ee77ec 100644 --- a/packages/api-auth-entity-storage-models/src/models/IAuthenticationComponent.ts +++ b/packages/api-auth-entity-storage-models/src/models/IAuthenticationComponent.ts @@ -1,6 +1,7 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. import type { IComponent } from "@twin.org/core"; +import type { IPasswordOptions } from "./IPasswordOptions.js"; /** * Contract definition for authentication component. @@ -44,4 +45,10 @@ export interface IAuthenticationComponent extends IComponent { * @returns A promise that resolves when the password has been updated. */ updatePassword(currentPassword: string, newPassword: string): Promise; + + /** + * Get the password policy used for password validation. + * @returns The password policy options. + */ + passwordPolicy(): Promise; } diff --git a/packages/api-auth-entity-storage-models/src/models/IPasswordOptions.ts b/packages/api-auth-entity-storage-models/src/models/IPasswordOptions.ts new file mode 100644 index 0000000..9a54b7b --- /dev/null +++ b/packages/api-auth-entity-storage-models/src/models/IPasswordOptions.ts @@ -0,0 +1,17 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. + +/** + * Password length validation options. + */ +export interface IPasswordOptions { + /** + * The minimum password length for validation. + */ + minPasswordLength?: number; + + /** + * The maximum password length for validation. + */ + maxPasswordLength?: number; +} diff --git a/packages/api-auth-entity-storage-models/src/models/api/IPasswordPolicyResponse.ts b/packages/api-auth-entity-storage-models/src/models/api/IPasswordPolicyResponse.ts new file mode 100644 index 0000000..d5ea47e --- /dev/null +++ b/packages/api-auth-entity-storage-models/src/models/api/IPasswordPolicyResponse.ts @@ -0,0 +1,13 @@ +// Copyright 2026 IOTA Stiftung. +// SPDX-License-Identifier: Apache-2.0. +import type { IPasswordOptions } from "../IPasswordOptions.js"; + +/** + * Get the password policy. + */ +export interface IPasswordPolicyResponse { + /** + * The body of the response. + */ + body: IPasswordOptions; +} diff --git a/packages/api-auth-entity-storage-rest-client/src/entityStorageAuthenticationRestClient.ts b/packages/api-auth-entity-storage-rest-client/src/entityStorageAuthenticationRestClient.ts index e45c0f0..7bae3de 100644 --- a/packages/api-auth-entity-storage-rest-client/src/entityStorageAuthenticationRestClient.ts +++ b/packages/api-auth-entity-storage-rest-client/src/entityStorageAuthenticationRestClient.ts @@ -5,12 +5,14 @@ import type { ILoginRequest, ILoginResponse, ILogoutRequest, + IPasswordPolicyResponse, + IPasswordOptions, IRefreshTokenRequest, IRefreshTokenResponse, IUpdatePasswordRequest } from "@twin.org/api-auth-entity-storage-models"; import { BaseRestClient } from "@twin.org/api-core"; -import type { INoContentResponse } from "@twin.org/api-models"; +import type { INoContentRequest, INoContentResponse } from "@twin.org/api-models"; import { Guards } from "@twin.org/core"; import { nameof } from "@twin.org/nameof"; import { CookieHelper, HeaderTypes } from "@twin.org/web"; @@ -160,4 +162,14 @@ export class EntityStorageAuthenticationRestClient } }); } + + /** + * Get the password policy used for password validation. + * @returns The password policy options. + */ + public async passwordPolicy(): Promise { + const response = await this.fetch("/policy", "GET"); + + return response.body; + } } diff --git a/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationRestClient.spec.ts b/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationRestClient.spec.ts index acedb21..73d08a8 100644 --- a/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationRestClient.spec.ts +++ b/packages/api-auth-entity-storage-rest-client/tests/entityStorageAuthenticationRestClient.spec.ts @@ -189,4 +189,28 @@ describe("EntityStorageAuthenticationRestClient", () => { ).resolves.toBeUndefined(); }); }); + + describe("passwordPolicy", () => { + test("sends GET to /policy", async () => { + fetchMock.mockResolvedValueOnce( + jsonResponse({ minPasswordLength: 15, maxPasswordLength: 128 }) + ); + + await client.passwordPolicy(); + + const [url, options] = fetchMock.mock.calls[0]; + expect(url).toBe(`${ENDPOINT}/${PREFIX}/policy`); + expect(options.method).toBe(HttpMethod.GET); + }); + + test("returns the policy from the response body", async () => { + fetchMock.mockResolvedValueOnce( + jsonResponse({ minPasswordLength: 15, maxPasswordLength: 128 }) + ); + + const result = await client.passwordPolicy(); + + expect(result).toEqual({ minPasswordLength: 15, maxPasswordLength: 128 }); + }); + }); }); diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConfig.ts b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConfig.ts index 14b5354..8dd6f04 100644 --- a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConfig.ts +++ b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationAdminServiceConfig.ts @@ -7,7 +7,13 @@ export interface IEntityStorageAuthenticationAdminServiceConfig { /** * The minimum password length. - * @default 8 + * @default 15 */ minPasswordLength?: number; + + /** + * The maximum password length. + * @default 128 + */ + maxPasswordLength?: number; } diff --git a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts index a462175..2098764 100644 --- a/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts +++ b/packages/api-auth-entity-storage-service/src/models/IEntityStorageAuthenticationServiceConfig.ts @@ -20,10 +20,16 @@ export interface IEntityStorageAuthenticationServiceConfig { /** * The minimum password length for new password validation. - * @default 8 + * @default 15 */ minPasswordLength?: number; + /** + * The maximum password length for new password validation. + * @default 128 + */ + maxPasswordLength?: number; + /** * Optional override for login failure rate limit. * @default { maxAttempts: 5, windowMinutes: 15 } diff --git a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts index 0ac00a7..0a3f5be 100644 --- a/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts +++ b/packages/api-auth-entity-storage-service/src/routes/entityStorageAuthenticationRoutes.ts @@ -5,12 +5,14 @@ import type { ILoginRequest, ILoginResponse, ILogoutRequest, + IPasswordPolicyResponse, IRefreshTokenRequest, IRefreshTokenResponse, IUpdatePasswordRequest } from "@twin.org/api-auth-entity-storage-models"; import type { IHttpRequestContext, + INoContentRequest, INoContentResponse, IRestRoute, IRestRouteResponseOptions, @@ -196,7 +198,39 @@ export function generateRestRoutesAuthentication( ] }; - return [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute]; + const passwordPolicyRoute: IRestRoute = { + operationId: "authenticationPasswordPolicy", + summary: "Get the password policy", + tag: tagsAuthentication[0].name, + method: "GET", + path: `${baseRouteName}/policy`, + handler: async (httpRequestContext, request) => + authenticationPasswordPolicy(httpRequestContext, componentName, request), + requestType: { + type: nameof() + }, + responseType: [ + { + type: nameof(), + examples: [ + { + id: "passwordPolicyResponseExample", + description: "The response for the password policy request.", + response: { + body: { + minPasswordLength: 15, + maxPasswordLength: 128 + } + } + } + ] + } + ], + skipAuth: true, + skipTenant: true + }; + + return [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute, passwordPolicyRoute]; } /** @@ -311,3 +345,23 @@ export async function authenticationUpdatePassword( statusCode: HttpStatusCode.noContent }; } + +/** + * Get the password policy. + * @param httpRequestContext The request context for the API. + * @param componentName The name of the component to use in the routes. + * @param request The request. + * @returns The response object with additional http response properties. + */ +export async function authenticationPasswordPolicy( + httpRequestContext: IHttpRequestContext, + componentName: string, + request: INoContentRequest +): Promise { + const component = ComponentFactory.get(componentName); + const result = await component.passwordPolicy(); + + return { + body: result + }; +} diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts index a46d60b..84e4698 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts +++ b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationAdminService.ts @@ -53,6 +53,12 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA */ private readonly _minPasswordLength?: number; + /** + * The maximum password length. + * @internal + */ + private readonly _maxPasswordLength?: number; + /** * Create a new instance of EntityStorageAuthentication. * @param options The dependencies for the identity connector. @@ -67,6 +73,7 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA ); this._minPasswordLength = options?.config?.minPasswordLength; + this._maxPasswordLength = options?.config?.maxPasswordLength; } /** @@ -111,7 +118,8 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA try { PasswordValidator.validatePassword(user.password, { - minLength: this._minPasswordLength + minLength: this._minPasswordLength, + maxLength: this._maxPasswordLength }); const existingUser = await this._userEntityStorage.get(user.email); @@ -389,7 +397,10 @@ export class EntityStorageAuthenticationAdminService implements IAuthenticationA user, newPassword, currentPassword, - this._minPasswordLength + { + minPasswordLength: this._minPasswordLength, + maxPasswordLength: this._maxPasswordLength + } ); } catch (error) { throw new GeneralError( diff --git a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts index ccadea1..4c17c98 100644 --- a/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts +++ b/packages/api-auth-entity-storage-service/src/services/entityStorageAuthenticationService.ts @@ -1,6 +1,9 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. -import type { IAuthenticationComponent } from "@twin.org/api-auth-entity-storage-models"; +import type { + IAuthenticationComponent, + IPasswordOptions +} from "@twin.org/api-auth-entity-storage-models"; import type { IAuthenticationRateActionConfig, IAuthenticationRateComponent, @@ -124,6 +127,12 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon */ private readonly _minPasswordLength?: number; + /** + * The maximum password length for validation. + * @internal + */ + private readonly _maxPasswordLength?: number; + /** * Rate limit configuration for login failures. * @internal @@ -181,6 +190,7 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon this._defaultTtlMinutes = options?.config?.defaultTtlMinutes ?? EntityStorageAuthenticationService._DEFAULT_TTL_MINUTES; this._minPasswordLength = options?.config?.minPasswordLength; + this._maxPasswordLength = options?.config?.maxPasswordLength; this._loginRateLimit = { maxAttempts: options?.config?.loginRateLimit?.maxAttempts ?? @@ -468,9 +478,23 @@ export class EntityStorageAuthenticationService implements IAuthenticationCompon user, newPassword, currentPassword, - this._minPasswordLength + { + minPasswordLength: this._minPasswordLength, + maxPasswordLength: this._maxPasswordLength + } ); await this._authenticationRateService.clear("password-change", userIdentity); } + + /** + * Get the password policy used for password validation. + * @returns The password policy options. + */ + public async passwordPolicy(): Promise { + return { + minPasswordLength: this._minPasswordLength, + maxPasswordLength: this._maxPasswordLength + }; + } } diff --git a/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts b/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts index db0440b..828490d 100644 --- a/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts +++ b/packages/api-auth-entity-storage-service/src/utils/passwordHelper.ts @@ -1,5 +1,6 @@ // Copyright 2024 IOTA Stiftung. // SPDX-License-Identifier: Apache-2.0. +import type { IPasswordOptions } from "@twin.org/api-auth-entity-storage-models"; import type { IAuthenticationAuditComponent } from "@twin.org/api-auth-models"; import { AuthAuditEvent } from "@twin.org/api-auth-models"; import { Converter, GeneralError, Is, RandomHelper } from "@twin.org/core"; @@ -25,7 +26,9 @@ export class PasswordHelper { * @param user The user whose password is being updated. * @param newPassword The new password to set. * @param currentPassword The current password to verify against, if supplied. - * @param minPasswordLength Optional minimum password length for validation. + * @param options Optional password length validation options. + * @param options.minPasswordLength Optional minimum password length for validation. + * @param options.maxPasswordLength Optional maximum password length for validation. * @returns A promise that resolves when the new password has been stored and the audit entry recorded. */ public static async updatePassword( @@ -34,10 +37,11 @@ export class PasswordHelper { user: AuthenticationUser, newPassword: string, currentPassword?: string, - minPasswordLength?: number + options?: IPasswordOptions ): Promise { PasswordValidator.validatePassword(newPassword, { - minLength: minPasswordLength + minLength: options?.minPasswordLength, + maxLength: options?.maxPasswordLength }); if (Is.stringValue(currentPassword)) { diff --git a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts index e95ec25..e0e2a18 100644 --- a/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts +++ b/packages/api-auth-entity-storage-service/tests/services/entityStorageAuthenticationService.spec.ts @@ -530,7 +530,7 @@ describe("EntityStorageAuthenticationService", () => { expect.objectContaining({ email: "user@example.com", identity: "did:user:123" }), "new-password", "current-password", - undefined + { minPasswordLength: undefined, maxPasswordLength: undefined } ); expect(mockAuthenticationRateService.clear).toHaveBeenCalledWith( "password-change", @@ -718,7 +718,7 @@ describe("EntityStorageAuthenticationService", () => { expect.objectContaining({ email: "user@example.com", identity: "did:user:123" }), "new-password", "current-password", - undefined + { minPasswordLength: undefined, maxPasswordLength: undefined } ); } ); From 099e820b9fff579b1eac9452ef85bb4c8565e754 Mon Sep 17 00:00:00 2001 From: Martyn Janes Date: Fri, 23 Oct 2026 10:48:27 +0800 Subject: [PATCH 20/20] feat: updated ts compile --- packages/api-auth-models/package.json | 9 ++++----- packages/api-auth-models/tests/tsconfig.json | 2 ++ packages/api-auth-models/tsconfig.json | 2 ++ packages/api-auth-models/vitest.config.ts | 1 + packages/api-auth-rest-client/package.json | 9 ++++----- packages/api-auth-rest-client/tests/tsconfig.json | 2 ++ packages/api-auth-rest-client/tsconfig.json | 2 ++ packages/api-auth-rest-client/vitest.config.ts | 1 + packages/api-auth-service/package.json | 9 ++++----- packages/api-auth-service/tests/tsconfig.json | 2 ++ packages/api-auth-service/tsconfig.json | 2 ++ packages/api-auth-service/vitest.config.ts | 1 + 12 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/api-auth-models/package.json b/packages/api-auth-models/package.json index d3a2c14..fa044d0 100644 --- a/packages/api-auth-models/package.json +++ b/packages/api-auth-models/package.json @@ -15,12 +15,12 @@ }, "scripts": { "clean": "rimraf dist coverage .cache", - "build": "tspc --incremental --tsBuildInfoFile .cache/tsconfig.tsbuildinfo", + "build": "tstransform", "dev": "nodemon --watch src --ext ts --exec \"pnpm run build\"", - "test": "vitest --run --config ./vitest.config.ts --no-cache", - "test:build": "tspc -p ./tests/tsconfig.json --noEmit --incremental --tsBuildInfoFile .cache/tests.tsbuildinfo", + "test": "vitest --run --config ./vitest.config.ts", + "test:build": "tstransform -p ./tests/tsconfig.json", "validate-locales": "validate-locales", - "test:coverage": "vitest --run --coverage --config ./vitest.config.ts --no-cache", + "test:coverage": "vitest --run --coverage --config ./vitest.config.ts", "docs:clean": "rimraf docs/reference", "docs:generate": "typedoc --options typedoc.config.mjs", "docs": "pnpm run docs:clean && pnpm run docs:generate", @@ -41,7 +41,6 @@ "copyfiles": "2.4.1", "nodemon": "3.1.14", "rimraf": "6.1.3", - "ts-patch": "4.0.1", "typedoc": "0.28.20", "typedoc-plugin-markdown": "4.14.1", "typescript": "6.0.3", diff --git a/packages/api-auth-models/tests/tsconfig.json b/packages/api-auth-models/tests/tsconfig.json index 44d8c8f..8f550b2 100644 --- a/packages/api-auth-models/tests/tsconfig.json +++ b/packages/api-auth-models/tests/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "../.cache/tests.tsbuildinfo", "rootDir": "..", "types": ["vitest/globals"] }, diff --git a/packages/api-auth-models/tsconfig.json b/packages/api-auth-models/tsconfig.json index 75bd95c..a8a4cec 100644 --- a/packages/api-auth-models/tsconfig.json +++ b/packages/api-auth-models/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": ".cache/tsconfig.tsbuildinfo", "module": "nodenext", "moduleResolution": "nodenext", "target": "ES2022", diff --git a/packages/api-auth-models/vitest.config.ts b/packages/api-auth-models/vitest.config.ts index a49dd24..d40698f 100644 --- a/packages/api-auth-models/vitest.config.ts +++ b/packages/api-auth-models/vitest.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ test: { include: ["./tests/**/*.spec.ts"], globals: true, + cache: false, testTimeout: 300000, hookTimeout: 300000, bail: 1, diff --git a/packages/api-auth-rest-client/package.json b/packages/api-auth-rest-client/package.json index 02d7b72..2f55f49 100644 --- a/packages/api-auth-rest-client/package.json +++ b/packages/api-auth-rest-client/package.json @@ -15,12 +15,12 @@ }, "scripts": { "clean": "rimraf dist coverage .cache", - "build": "tspc --incremental --tsBuildInfoFile .cache/tsconfig.tsbuildinfo", + "build": "tstransform", "dev": "nodemon --watch src --ext ts --exec \"pnpm run build\"", - "test": "vitest --run --config ./vitest.config.ts --no-cache", - "test:build": "tspc -p ./tests/tsconfig.json --noEmit --incremental --tsBuildInfoFile .cache/tests.tsbuildinfo", + "test": "vitest --run --config ./vitest.config.ts", + "test:build": "tstransform -p ./tests/tsconfig.json", "validate-locales": "validate-locales", - "test:coverage": "vitest --run --coverage --config ./vitest.config.ts --no-cache", + "test:coverage": "vitest --run --coverage --config ./vitest.config.ts", "docs:clean": "rimraf docs/reference", "docs:generate": "typedoc --options typedoc.config.mjs", "docs": "pnpm run docs:clean && pnpm run docs:generate", @@ -45,7 +45,6 @@ "copyfiles": "2.4.1", "nodemon": "3.1.14", "rimraf": "6.1.3", - "ts-patch": "4.0.1", "typedoc": "0.28.20", "typedoc-plugin-markdown": "4.14.1", "typescript": "6.0.3", diff --git a/packages/api-auth-rest-client/tests/tsconfig.json b/packages/api-auth-rest-client/tests/tsconfig.json index 44d8c8f..8f550b2 100644 --- a/packages/api-auth-rest-client/tests/tsconfig.json +++ b/packages/api-auth-rest-client/tests/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "../.cache/tests.tsbuildinfo", "rootDir": "..", "types": ["vitest/globals"] }, diff --git a/packages/api-auth-rest-client/tsconfig.json b/packages/api-auth-rest-client/tsconfig.json index 75bd95c..a8a4cec 100644 --- a/packages/api-auth-rest-client/tsconfig.json +++ b/packages/api-auth-rest-client/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": ".cache/tsconfig.tsbuildinfo", "module": "nodenext", "moduleResolution": "nodenext", "target": "ES2022", diff --git a/packages/api-auth-rest-client/vitest.config.ts b/packages/api-auth-rest-client/vitest.config.ts index a49dd24..d40698f 100644 --- a/packages/api-auth-rest-client/vitest.config.ts +++ b/packages/api-auth-rest-client/vitest.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ test: { include: ["./tests/**/*.spec.ts"], globals: true, + cache: false, testTimeout: 300000, hookTimeout: 300000, bail: 1, diff --git a/packages/api-auth-service/package.json b/packages/api-auth-service/package.json index 2c2ec1e..d8e207a 100644 --- a/packages/api-auth-service/package.json +++ b/packages/api-auth-service/package.json @@ -15,12 +15,12 @@ }, "scripts": { "clean": "rimraf dist coverage .cache", - "build": "tspc --incremental --tsBuildInfoFile .cache/tsconfig.tsbuildinfo", + "build": "tstransform", "dev": "nodemon --watch src --ext ts --exec \"pnpm run build\"", - "test": "vitest --run --config ./vitest.config.ts --no-cache", - "test:build": "tspc -p ./tests/tsconfig.json --noEmit --incremental --tsBuildInfoFile .cache/tests.tsbuildinfo", + "test": "vitest --run --config ./vitest.config.ts", + "test:build": "tstransform -p ./tests/tsconfig.json", "validate-locales": "validate-locales", - "test:coverage": "vitest --run --coverage --config ./vitest.config.ts --no-cache", + "test:coverage": "vitest --run --coverage --config ./vitest.config.ts", "docs:clean": "rimraf docs/reference", "docs:generate": "typedoc --options typedoc.config.mjs", "docs:api": "ts-to-openapi ./ts-to-openapi.json ./docs/open-api/spec.json", @@ -53,7 +53,6 @@ "copyfiles": "2.4.1", "nodemon": "3.1.14", "rimraf": "6.1.3", - "ts-patch": "4.0.1", "typedoc": "0.28.20", "typedoc-plugin-markdown": "4.14.1", "typescript": "6.0.3", diff --git a/packages/api-auth-service/tests/tsconfig.json b/packages/api-auth-service/tests/tsconfig.json index 44d8c8f..8f550b2 100644 --- a/packages/api-auth-service/tests/tsconfig.json +++ b/packages/api-auth-service/tests/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "../tsconfig.json", "compilerOptions": { + "noEmit": true, + "tsBuildInfoFile": "../.cache/tests.tsbuildinfo", "rootDir": "..", "types": ["vitest/globals"] }, diff --git a/packages/api-auth-service/tsconfig.json b/packages/api-auth-service/tsconfig.json index 75bd95c..a8a4cec 100644 --- a/packages/api-auth-service/tsconfig.json +++ b/packages/api-auth-service/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": ".cache/tsconfig.tsbuildinfo", "module": "nodenext", "moduleResolution": "nodenext", "target": "ES2022", diff --git a/packages/api-auth-service/vitest.config.ts b/packages/api-auth-service/vitest.config.ts index a49dd24..d40698f 100644 --- a/packages/api-auth-service/vitest.config.ts +++ b/packages/api-auth-service/vitest.config.ts @@ -8,6 +8,7 @@ export default defineConfig({ test: { include: ["./tests/**/*.spec.ts"], globals: true, + cache: false, testTimeout: 300000, hookTimeout: 300000, bail: 1,