@twin.org/api-auth-entity-storage-service 0.0.3-next.9 → 0.9.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +2 -2
  2. package/dist/es/entities/authenticationAuditEntry.js +101 -0
  3. package/dist/es/entities/authenticationAuditEntry.js.map +1 -0
  4. package/dist/es/entities/authenticationRateEntry.js +37 -0
  5. package/dist/es/entities/authenticationRateEntry.js.map +1 -0
  6. package/dist/es/entities/authenticationUser.js +17 -1
  7. package/dist/es/entities/authenticationUser.js.map +1 -1
  8. package/dist/es/index.js +11 -1
  9. package/dist/es/index.js.map +1 -1
  10. package/dist/es/models/IAuthHeaderProcessorConstructorOptions.js.map +1 -1
  11. package/dist/es/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js.map +1 -1
  12. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConfig.js +4 -0
  13. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConfig.js.map +1 -0
  14. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js +2 -0
  15. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js.map +1 -0
  16. package/dist/es/models/IEntityStorageAuthenticationRateServiceConfig.js +2 -0
  17. package/dist/es/models/IEntityStorageAuthenticationRateServiceConfig.js.map +1 -0
  18. package/dist/es/models/IEntityStorageAuthenticationRateServiceConstructorOptions.js +2 -0
  19. package/dist/es/models/IEntityStorageAuthenticationRateServiceConstructorOptions.js.map +1 -0
  20. package/dist/es/models/IEntityStorageAuthenticationServiceConfig.js +0 -2
  21. package/dist/es/models/IEntityStorageAuthenticationServiceConfig.js.map +1 -1
  22. package/dist/es/models/IEntityStorageAuthenticationServiceConstructorOptions.js.map +1 -1
  23. package/dist/es/processors/authHeaderProcessor.js +68 -11
  24. package/dist/es/processors/authHeaderProcessor.js.map +1 -1
  25. package/dist/es/restEntryPoints.js +17 -0
  26. package/dist/es/restEntryPoints.js.map +1 -1
  27. package/dist/es/routes/entityStorageAuthenticationAdminRoutes.js +362 -0
  28. package/dist/es/routes/entityStorageAuthenticationAdminRoutes.js.map +1 -0
  29. package/dist/es/routes/entityStorageAuthenticationAuditRoutes.js +174 -0
  30. package/dist/es/routes/entityStorageAuthenticationAuditRoutes.js.map +1 -0
  31. package/dist/es/routes/entityStorageAuthenticationRoutes.js +18 -19
  32. package/dist/es/routes/entityStorageAuthenticationRoutes.js.map +1 -1
  33. package/dist/es/schema.js +4 -0
  34. package/dist/es/schema.js.map +1 -1
  35. package/dist/es/services/entityStorageAuthenticationAdminService.js +164 -58
  36. package/dist/es/services/entityStorageAuthenticationAdminService.js.map +1 -1
  37. package/dist/es/services/entityStorageAuthenticationAuditService.js +179 -0
  38. package/dist/es/services/entityStorageAuthenticationAuditService.js.map +1 -0
  39. package/dist/es/services/entityStorageAuthenticationRateService.js +213 -0
  40. package/dist/es/services/entityStorageAuthenticationRateService.js.map +1 -0
  41. package/dist/es/services/entityStorageAuthenticationService.js +200 -19
  42. package/dist/es/services/entityStorageAuthenticationService.js.map +1 -1
  43. package/dist/es/utils/passwordHelper.js +45 -16
  44. package/dist/es/utils/passwordHelper.js.map +1 -1
  45. package/dist/es/utils/tokenHelper.js +50 -10
  46. package/dist/es/utils/tokenHelper.js.map +1 -1
  47. package/dist/types/entities/authenticationAuditEntry.d.ts +49 -0
  48. package/dist/types/entities/authenticationRateEntry.d.ts +17 -0
  49. package/dist/types/entities/authenticationUser.d.ts +8 -0
  50. package/dist/types/index.d.ts +11 -1
  51. package/dist/types/models/IAuthHeaderProcessorConstructorOptions.d.ts +10 -0
  52. package/dist/types/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.d.ts +5 -0
  53. package/dist/types/models/IEntityStorageAuthenticationAuditServiceConfig.d.ts +9 -0
  54. package/dist/types/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.d.ts +15 -0
  55. package/dist/types/models/IEntityStorageAuthenticationRateServiceConfig.d.ts +10 -0
  56. package/dist/types/models/IEntityStorageAuthenticationRateServiceConstructorOptions.d.ts +25 -0
  57. package/dist/types/models/IEntityStorageAuthenticationServiceConfig.d.ts +22 -1
  58. package/dist/types/models/IEntityStorageAuthenticationServiceConstructorOptions.d.ts +13 -3
  59. package/dist/types/processors/authHeaderProcessor.d.ts +4 -2
  60. package/dist/types/restEntryPoints.d.ts +3 -0
  61. package/dist/types/routes/entityStorageAuthenticationAdminRoutes.d.ts +61 -0
  62. package/dist/types/routes/entityStorageAuthenticationAuditRoutes.d.ts +29 -0
  63. package/dist/types/services/entityStorageAuthenticationAdminService.d.ts +26 -9
  64. package/dist/types/services/entityStorageAuthenticationAuditService.d.ts +53 -0
  65. package/dist/types/services/entityStorageAuthenticationRateService.d.ts +60 -0
  66. package/dist/types/services/entityStorageAuthenticationService.d.ts +10 -5
  67. package/dist/types/utils/passwordHelper.d.ts +13 -5
  68. package/dist/types/utils/tokenHelper.d.ts +10 -4
  69. package/docs/changelog.md +845 -85
  70. package/docs/examples.md +178 -1
  71. package/docs/reference/classes/AuthHeaderProcessor.md +15 -11
  72. package/docs/reference/classes/AuthenticationAuditEntry.md +101 -0
  73. package/docs/reference/classes/AuthenticationRateEntry.md +37 -0
  74. package/docs/reference/classes/AuthenticationUser.md +21 -5
  75. package/docs/reference/classes/EntityStorageAuthenticationAdminService.md +80 -20
  76. package/docs/reference/classes/EntityStorageAuthenticationAuditService.md +157 -0
  77. package/docs/reference/classes/EntityStorageAuthenticationRateService.md +227 -0
  78. package/docs/reference/classes/EntityStorageAuthenticationService.md +37 -17
  79. package/docs/reference/classes/PasswordHelper.md +37 -12
  80. package/docs/reference/classes/TokenHelper.md +49 -13
  81. package/docs/reference/functions/authenticationAdminCreateUser.md +31 -0
  82. package/docs/reference/functions/authenticationAdminGetUser.md +31 -0
  83. package/docs/reference/functions/authenticationAdminGetUserByIdentity.md +31 -0
  84. package/docs/reference/functions/authenticationAdminRemoveUser.md +31 -0
  85. package/docs/reference/functions/authenticationAdminUpdateUser.md +31 -0
  86. package/docs/reference/functions/authenticationAdminUpdateUserPassword.md +31 -0
  87. package/docs/reference/functions/authenticationAuditCreate.md +31 -0
  88. package/docs/reference/functions/authenticationAuditQuery.md +31 -0
  89. package/docs/reference/functions/generateRestRoutesAuthenticationAdmin.md +25 -0
  90. package/docs/reference/functions/generateRestRoutesAuthenticationAudit.md +25 -0
  91. package/docs/reference/index.md +20 -0
  92. package/docs/reference/interfaces/IAuthHeaderProcessorConfig.md +4 -4
  93. package/docs/reference/interfaces/IAuthHeaderProcessorConstructorOptions.md +32 -4
  94. package/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConfig.md +2 -2
  95. package/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md +18 -4
  96. package/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md +11 -0
  97. package/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md +25 -0
  98. package/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md +17 -0
  99. package/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md +53 -0
  100. package/docs/reference/interfaces/IEntityStorageAuthenticationServiceConfig.md +61 -5
  101. package/docs/reference/interfaces/IEntityStorageAuthenticationServiceConstructorOptions.md +38 -10
  102. package/docs/reference/variables/restEntryPoints.md +2 -0
  103. package/docs/reference/variables/tagsAuthenticationAdmin.md +5 -0
  104. package/docs/reference/variables/tagsAuthenticationAudit.md +5 -0
  105. package/locales/en.json +23 -6
  106. package/package.json +17 -16
@@ -67,7 +67,7 @@ export function generateRestRoutesAuthentication(baseRouteName, componentName) {
67
67
  operationId: "authenticationLogout",
68
68
  summary: "Logout from the server",
69
69
  tag: tagsAuthentication[0].name,
70
- method: "GET",
70
+ method: "POST",
71
71
  path: `${baseRouteName}/logout`,
72
72
  handler: async (httpRequestContext, request) => authenticationLogout(httpRequestContext, componentName, request),
73
73
  requestType: {
@@ -77,7 +77,7 @@ export function generateRestRoutesAuthentication(baseRouteName, componentName) {
77
77
  id: "logoutRequestExample",
78
78
  description: "The request to logout from the server.",
79
79
  request: {
80
- query: {
80
+ body: {
81
81
  token: "eyJhbGciOiJIU...sw5c"
82
82
  }
83
83
  }
@@ -88,14 +88,13 @@ export function generateRestRoutesAuthentication(baseRouteName, componentName) {
88
88
  {
89
89
  type: "INoContentResponse"
90
90
  }
91
- ],
92
- skipAuth: true
91
+ ]
93
92
  };
94
93
  const refreshTokenRoute = {
95
94
  operationId: "authenticationRefreshToken",
96
95
  summary: "Refresh an authentication token",
97
96
  tag: tagsAuthentication[0].name,
98
- method: "GET",
97
+ method: "POST",
99
98
  path: `${baseRouteName}/refresh`,
100
99
  handler: async (httpRequestContext, request) => authenticationRefreshToken(httpRequestContext, componentName, request),
101
100
  requestType: {
@@ -105,7 +104,7 @@ export function generateRestRoutesAuthentication(baseRouteName, componentName) {
105
104
  id: "refreshTokenRequestExample",
106
105
  description: "The request to refresh an auth token.",
107
106
  request: {
108
- query: {
107
+ body: {
109
108
  token: "eyJhbGciOiJIU...sw5c"
110
109
  }
111
110
  }
@@ -134,21 +133,18 @@ export function generateRestRoutesAuthentication(baseRouteName, componentName) {
134
133
  };
135
134
  const updatePasswordRoute = {
136
135
  operationId: "authenticationUpdatePassword",
137
- summary: "Update the user's password",
136
+ summary: "Update the current user's password",
138
137
  tag: tagsAuthentication[0].name,
139
138
  method: "PUT",
140
- path: `${baseRouteName}/:email/password`,
139
+ path: `${baseRouteName}/password`,
141
140
  handler: async (httpRequestContext, request) => authenticationUpdatePassword(httpRequestContext, componentName, request),
142
141
  requestType: {
143
142
  type: "IUpdatePasswordRequest",
144
143
  examples: [
145
144
  {
146
145
  id: "updatePasswordRequestExample",
147
- description: "The request to update the user's password.",
146
+ description: "The request to update the current user's password.",
148
147
  request: {
149
- pathParams: {
150
- email: "john:example.com"
151
- },
152
148
  body: {
153
149
  currentPassword: "MyNewPassword123!",
154
150
  newPassword: "MyNewPassword123!"
@@ -185,7 +181,9 @@ export async function authenticationLogin(httpRequestContext, componentName, req
185
181
  httpRequestContext.processorState.authOperation = "login";
186
182
  httpRequestContext.processorState.authToken = result.token;
187
183
  return {
188
- body: result
184
+ body: {
185
+ expiry: result.expiry
186
+ }
189
187
  };
190
188
  }
191
189
  /**
@@ -198,7 +196,7 @@ export async function authenticationLogin(httpRequestContext, componentName, req
198
196
  export async function authenticationLogout(httpRequestContext, componentName, request) {
199
197
  Guards.object(ROUTES_SOURCE, "request", request);
200
198
  const component = ComponentFactory.get(componentName);
201
- await component.logout(request.query?.token);
199
+ await component.logout(request.body?.token);
202
200
  // Need to give a hint to any auth processors about the operation
203
201
  // in case they need to manipulate the response
204
202
  httpRequestContext.processorState.authOperation = "logout";
@@ -216,16 +214,18 @@ export async function authenticationLogout(httpRequestContext, componentName, re
216
214
  export async function authenticationRefreshToken(httpRequestContext, componentName, request) {
217
215
  Guards.object(ROUTES_SOURCE, "request", request);
218
216
  const component = ComponentFactory.get(componentName);
219
- // If the token is not in the query, then maybe an auth processor has extracted it
217
+ // If the token is not in the body, then maybe an auth processor has extracted it
220
218
  // and stored it in the processor state
221
- const token = request.query?.token ?? httpRequestContext.processorState.authToken;
219
+ const token = request.body?.token ?? httpRequestContext.processorState.authToken;
222
220
  const result = await component.refresh(token);
223
221
  // Need to give a hint to any auth processors about the operation
224
222
  // in case they need to manipulate the response
225
223
  httpRequestContext.processorState.authOperation = "refresh";
226
224
  httpRequestContext.processorState.authToken = result.token;
227
225
  return {
228
- body: result
226
+ body: {
227
+ expiry: result.expiry
228
+ }
229
229
  };
230
230
  }
231
231
  /**
@@ -237,10 +237,9 @@ export async function authenticationRefreshToken(httpRequestContext, componentNa
237
237
  */
238
238
  export async function authenticationUpdatePassword(httpRequestContext, componentName, request) {
239
239
  Guards.object(ROUTES_SOURCE, "request", request);
240
- Guards.object(ROUTES_SOURCE, "request.pathParams", request.pathParams);
241
240
  Guards.object(ROUTES_SOURCE, "request.body", request.body);
242
241
  const component = ComponentFactory.get(componentName);
243
- await component.updatePassword(request.pathParams.email, request.body.currentPassword, request.body.newPassword);
242
+ await component.updatePassword(request.body.currentPassword, request.body.newPassword);
244
243
  return {
245
244
  statusCode: HttpStatusCode.noContent
246
245
  };
@@ -1 +1 @@
1
- {"version":3,"file":"entityStorageAuthenticationRoutes.js","sourceRoot":"","sources":["../../../src/routes/entityStorageAuthenticationRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationComponent,\n\tILoginRequest,\n\tILoginResponse,\n\tILogoutRequest,\n\tIRefreshTokenRequest,\n\tIRefreshTokenResponse,\n\tIUpdatePasswordRequest\n} from \"@twin.org/api-auth-entity-storage-models\";\nimport type {\n\tIHttpRequestContext,\n\tINoContentResponse,\n\tIRestRoute,\n\tIRestRouteResponseOptions,\n\tITag,\n\tIUnauthorizedResponse\n} from \"@twin.org/api-models\";\nimport { ComponentFactory, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HttpStatusCode } from \"@twin.org/web\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"authenticationRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsAuthentication: ITag[] = [\n\t{\n\t\tname: \"Authentication\",\n\t\tdescription: \"Authentication endpoints for the REST server.\"\n\t}\n];\n\n/**\n * The REST routes for authentication.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesAuthentication(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst loginRoute: IRestRoute<ILoginRequest, ILoginResponse> = {\n\t\toperationId: \"authenticationLogin\",\n\t\tsummary: \"Login to the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/login`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogin(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILoginRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"loginRequestExample\",\n\t\t\t\t\tdescription: \"The request to login to the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\temail: \"user@example.com\",\n\t\t\t\t\t\t\tpassword: \"MyPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ILoginResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"loginResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the login request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst logoutRoute: IRestRoute<ILogoutRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationLogout\",\n\t\tsummary: \"Logout from the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/logout`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogout(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILogoutRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"logoutRequestExample\",\n\t\t\t\t\tdescription: \"The request to logout from the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tquery: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst refreshTokenRoute: IRestRoute<IRefreshTokenRequest, IRefreshTokenResponse> = {\n\t\toperationId: \"authenticationRefreshToken\",\n\t\tsummary: \"Refresh an authentication token\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"GET\",\n\t\tpath: `${baseRouteName}/refresh`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationRefreshToken(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IRefreshTokenRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"refreshTokenRequestExample\",\n\t\t\t\t\tdescription: \"The request to refresh an auth token.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tquery: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IRefreshTokenResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"refreshTokenResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the refresh token request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\tconst updatePasswordRoute: IRestRoute<IUpdatePasswordRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationUpdatePassword\",\n\t\tsummary: \"Update the user's password\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"PUT\",\n\t\tpath: `${baseRouteName}/:email/password`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationUpdatePassword(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IUpdatePasswordRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"updatePasswordRequestExample\",\n\t\t\t\t\tdescription: \"The request to update the user's password.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tpathParams: {\n\t\t\t\t\t\t\temail: \"john:example.com\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\tcurrentPassword: \"MyNewPassword123!\",\n\t\t\t\t\t\t\tnewPassword: \"MyNewPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\treturn [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute];\n}\n\n/**\n * Login to the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogin(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILoginRequest\n): Promise<ILoginResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILoginRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<ILoginRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tconst result = await component.login(request.body.email, request.body.password);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"login\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: result\n\t};\n}\n\n/**\n * Logout from the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogout(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILogoutRequest\n): Promise<INoContentResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILogoutRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tawait component.logout(request.query?.token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"logout\";\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n\n/**\n * Refresh the login token.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationRefreshToken(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IRefreshTokenRequest\n): Promise<IRefreshTokenResponse & IRestRouteResponseOptions> {\n\tGuards.object<IRefreshTokenRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\t// If the token is not in the query, then maybe an auth processor has extracted it\n\t// and stored it in the processor state\n\tconst token = request.query?.token ?? (httpRequestContext.processorState.authToken as string);\n\tconst result = await component.refresh(token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"refresh\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: result\n\t};\n}\n\n/**\n * Update the user's password.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationUpdatePassword(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IUpdatePasswordRequest\n): Promise<INoContentResponse> {\n\tGuards.object<IUpdatePasswordRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<IUpdatePasswordRequest[\"pathParams\"]>(\n\t\tROUTES_SOURCE,\n\t\tnameof(request.pathParams),\n\t\trequest.pathParams\n\t);\n\tGuards.object<IUpdatePasswordRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\tawait component.updatePassword(\n\t\trequest.pathParams.email,\n\t\trequest.body.currentPassword,\n\t\trequest.body.newPassword\n\t);\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n"]}
1
+ {"version":3,"file":"entityStorageAuthenticationRoutes.js","sourceRoot":"","sources":["../../../src/routes/entityStorageAuthenticationRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationComponent,\n\tILoginRequest,\n\tILoginResponse,\n\tILogoutRequest,\n\tIRefreshTokenRequest,\n\tIRefreshTokenResponse,\n\tIUpdatePasswordRequest\n} from \"@twin.org/api-auth-entity-storage-models\";\nimport type {\n\tIHttpRequestContext,\n\tINoContentResponse,\n\tIRestRoute,\n\tIRestRouteResponseOptions,\n\tITag,\n\tIUnauthorizedResponse\n} from \"@twin.org/api-models\";\nimport { ComponentFactory, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { HttpStatusCode } from \"@twin.org/web\";\n\n/**\n * The source used when communicating about these routes.\n */\nconst ROUTES_SOURCE = \"authenticationRoutes\";\n\n/**\n * The tag to associate with the routes.\n */\nexport const tagsAuthentication: ITag[] = [\n\t{\n\t\tname: \"Authentication\",\n\t\tdescription: \"Authentication endpoints for the REST server.\"\n\t}\n];\n\n/**\n * The REST routes for authentication.\n * @param baseRouteName Prefix to prepend to the paths.\n * @param componentName The name of the component to use in the routes stored in the ComponentFactory.\n * @returns The generated routes.\n */\nexport function generateRestRoutesAuthentication(\n\tbaseRouteName: string,\n\tcomponentName: string\n): IRestRoute[] {\n\tconst loginRoute: IRestRoute<ILoginRequest, ILoginResponse> = {\n\t\toperationId: \"authenticationLogin\",\n\t\tsummary: \"Login to the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/login`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogin(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILoginRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"loginRequestExample\",\n\t\t\t\t\tdescription: \"The request to login to the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\temail: \"user@example.com\",\n\t\t\t\t\t\t\tpassword: \"MyPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<ILoginResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"loginResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the login request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t],\n\t\tskipAuth: true\n\t};\n\n\tconst logoutRoute: IRestRoute<ILogoutRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationLogout\",\n\t\tsummary: \"Logout from the server\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/logout`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationLogout(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<ILogoutRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"logoutRequestExample\",\n\t\t\t\t\tdescription: \"The request to logout from the server.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\tconst refreshTokenRoute: IRestRoute<IRefreshTokenRequest, IRefreshTokenResponse> = {\n\t\toperationId: \"authenticationRefreshToken\",\n\t\tsummary: \"Refresh an authentication token\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"POST\",\n\t\tpath: `${baseRouteName}/refresh`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationRefreshToken(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IRefreshTokenRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"refreshTokenRequestExample\",\n\t\t\t\t\tdescription: \"The request to refresh an auth token.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\ttoken: \"eyJhbGciOiJIU...sw5c\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<IRefreshTokenResponse>(),\n\t\t\t\texamples: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"refreshTokenResponseExample\",\n\t\t\t\t\t\tdescription: \"The response for the refresh token request.\",\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\texpiry: 1722514341067\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\tconst updatePasswordRoute: IRestRoute<IUpdatePasswordRequest, INoContentResponse> = {\n\t\toperationId: \"authenticationUpdatePassword\",\n\t\tsummary: \"Update the current user's password\",\n\t\ttag: tagsAuthentication[0].name,\n\t\tmethod: \"PUT\",\n\t\tpath: `${baseRouteName}/password`,\n\t\thandler: async (httpRequestContext, request) =>\n\t\t\tauthenticationUpdatePassword(httpRequestContext, componentName, request),\n\t\trequestType: {\n\t\t\ttype: nameof<IUpdatePasswordRequest>(),\n\t\t\texamples: [\n\t\t\t\t{\n\t\t\t\t\tid: \"updatePasswordRequestExample\",\n\t\t\t\t\tdescription: \"The request to update the current user's password.\",\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\tcurrentPassword: \"MyNewPassword123!\",\n\t\t\t\t\t\t\tnewPassword: \"MyNewPassword123!\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\tresponseType: [\n\t\t\t{\n\t\t\t\ttype: nameof<INoContentResponse>()\n\t\t\t},\n\t\t\t{\n\t\t\t\ttype: nameof<IUnauthorizedResponse>()\n\t\t\t}\n\t\t]\n\t};\n\n\treturn [loginRoute, logoutRoute, refreshTokenRoute, updatePasswordRoute];\n}\n\n/**\n * Login to the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogin(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILoginRequest\n): Promise<ILoginResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILoginRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<ILoginRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tconst result = await component.login(request.body.email, request.body.password);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"login\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: {\n\t\t\texpiry: result.expiry\n\t\t}\n\t};\n}\n\n/**\n * Logout from the server.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationLogout(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: ILogoutRequest\n): Promise<INoContentResponse & IRestRouteResponseOptions> {\n\tGuards.object<ILogoutRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\tawait component.logout(request.body?.token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"logout\";\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n\n/**\n * Refresh the login token.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationRefreshToken(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IRefreshTokenRequest\n): Promise<IRefreshTokenResponse & IRestRouteResponseOptions> {\n\tGuards.object<IRefreshTokenRequest>(ROUTES_SOURCE, nameof(request), request);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\t// If the token is not in the body, then maybe an auth processor has extracted it\n\t// and stored it in the processor state\n\tconst token = request.body?.token ?? (httpRequestContext.processorState.authToken as string);\n\tconst result = await component.refresh(token);\n\n\t// Need to give a hint to any auth processors about the operation\n\t// in case they need to manipulate the response\n\thttpRequestContext.processorState.authOperation = \"refresh\";\n\thttpRequestContext.processorState.authToken = result.token;\n\n\treturn {\n\t\tbody: {\n\t\t\texpiry: result.expiry\n\t\t}\n\t};\n}\n\n/**\n * Update the user's password.\n * @param httpRequestContext The request context for the API.\n * @param componentName The name of the component to use in the routes.\n * @param request The request.\n * @returns The response object with additional http response properties.\n */\nexport async function authenticationUpdatePassword(\n\thttpRequestContext: IHttpRequestContext,\n\tcomponentName: string,\n\trequest: IUpdatePasswordRequest\n): Promise<INoContentResponse> {\n\tGuards.object<IUpdatePasswordRequest>(ROUTES_SOURCE, nameof(request), request);\n\tGuards.object<IUpdatePasswordRequest[\"body\"]>(ROUTES_SOURCE, nameof(request.body), request.body);\n\n\tconst component = ComponentFactory.get<IAuthenticationComponent>(componentName);\n\n\tawait component.updatePassword(request.body.currentPassword, request.body.newPassword);\n\n\treturn {\n\t\tstatusCode: HttpStatusCode.noContent\n\t};\n}\n"]}
package/dist/es/schema.js CHANGED
@@ -1,11 +1,15 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { EntitySchemaFactory, EntitySchemaHelper } from "@twin.org/entity";
4
+ import { AuthenticationAuditEntry } from "./entities/authenticationAuditEntry.js";
5
+ import { AuthenticationRateEntry } from "./entities/authenticationRateEntry.js";
4
6
  import { AuthenticationUser } from "./entities/authenticationUser.js";
5
7
  /**
6
8
  * Initialize the schema for the authentication service.
7
9
  */
8
10
  export function initSchema() {
9
11
  EntitySchemaFactory.register("AuthenticationUser", () => EntitySchemaHelper.getSchema(AuthenticationUser));
12
+ EntitySchemaFactory.register("AuthenticationAuditEntry", () => EntitySchemaHelper.getSchema(AuthenticationAuditEntry));
13
+ EntitySchemaFactory.register("AuthenticationRateEntry", () => EntitySchemaHelper.getSchema(AuthenticationRateEntry));
10
14
  }
11
15
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE;;GAEG;AACH,MAAM,UAAU,UAAU;IACzB,mBAAmB,CAAC,QAAQ,uBAA+B,GAAG,EAAE,CAC/D,kBAAkB,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAChD,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { EntitySchemaFactory, EntitySchemaHelper } from \"@twin.org/entity\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { AuthenticationUser } from \"./entities/authenticationUser.js\";\n\n/**\n * Initialize the schema for the authentication service.\n */\nexport function initSchema(): void {\n\tEntitySchemaFactory.register(nameof<AuthenticationUser>(), () =>\n\t\tEntitySchemaHelper.getSchema(AuthenticationUser)\n\t);\n}\n"]}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE;;GAEG;AACH,MAAM,UAAU,UAAU;IACzB,mBAAmB,CAAC,QAAQ,uBAA+B,GAAG,EAAE,CAC/D,kBAAkB,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAChD,CAAC;IACF,mBAAmB,CAAC,QAAQ,6BAAqC,GAAG,EAAE,CACrE,kBAAkB,CAAC,SAAS,CAAC,wBAAwB,CAAC,CACtD,CAAC;IACF,mBAAmB,CAAC,QAAQ,4BAAoC,GAAG,EAAE,CACpE,kBAAkB,CAAC,SAAS,CAAC,uBAAuB,CAAC,CACrD,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { EntitySchemaFactory, EntitySchemaHelper } from \"@twin.org/entity\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { AuthenticationAuditEntry } from \"./entities/authenticationAuditEntry.js\";\nimport { AuthenticationRateEntry } from \"./entities/authenticationRateEntry.js\";\nimport { AuthenticationUser } from \"./entities/authenticationUser.js\";\n\n/**\n * Initialize the schema for the authentication service.\n */\nexport function initSchema(): void {\n\tEntitySchemaFactory.register(nameof<AuthenticationUser>(), () =>\n\t\tEntitySchemaHelper.getSchema(AuthenticationUser)\n\t);\n\tEntitySchemaFactory.register(nameof<AuthenticationAuditEntry>(), () =>\n\t\tEntitySchemaHelper.getSchema(AuthenticationAuditEntry)\n\t);\n\tEntitySchemaFactory.register(nameof<AuthenticationRateEntry>(), () =>\n\t\tEntitySchemaHelper.getSchema(AuthenticationRateEntry)\n\t);\n}\n"]}
@@ -1,4 +1,7 @@
1
- import { Converter, GeneralError, Guards, Is, NotFoundError, RandomHelper } from "@twin.org/core";
1
+ import { AuthAuditEvent } from "@twin.org/api-auth-entity-storage-models";
2
+ import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
3
+ import { ComponentFactory, Converter, GeneralError, Guards, Is, NotFoundError, RandomHelper } from "@twin.org/core";
4
+ import { PasswordGenerator, PasswordValidator } from "@twin.org/crypto";
2
5
  import { EntityStorageConnectorFactory } from "@twin.org/entity-storage-models";
3
6
  import { PasswordHelper } from "../utils/passwordHelper.js";
4
7
  /**
@@ -10,15 +13,15 @@ export class EntityStorageAuthenticationAdminService {
10
13
  */
11
14
  static CLASS_NAME = "EntityStorageAuthenticationAdminService";
12
15
  /**
13
- * The minimum password length.
16
+ * The entity storage for users.
14
17
  * @internal
15
18
  */
16
- static _DEFAULT_MIN_PASSWORD_LENGTH = 8;
19
+ _userEntityStorage;
17
20
  /**
18
- * The entity storage for users.
21
+ * The audit service.
19
22
  * @internal
20
23
  */
21
- _userEntityStorage;
24
+ _authenticationAuditService;
22
25
  /**
23
26
  * The minimum password length.
24
27
  * @internal
@@ -30,9 +33,8 @@ export class EntityStorageAuthenticationAdminService {
30
33
  */
31
34
  constructor(options) {
32
35
  this._userEntityStorage = EntityStorageConnectorFactory.get(options?.userEntityStorageType ?? "authentication-user");
33
- this._minPasswordLength =
34
- options?.config?.minPasswordLength ??
35
- EntityStorageAuthenticationAdminService._DEFAULT_MIN_PASSWORD_LENGTH;
36
+ this._authenticationAuditService = ComponentFactory.getIfExists(options?.authenticationAuditServiceType ?? "authentication-audit");
37
+ this._minPasswordLength = options?.config?.minPasswordLength;
36
38
  }
37
39
  /**
38
40
  * Returns the class name of the component.
@@ -43,56 +45,183 @@ export class EntityStorageAuthenticationAdminService {
43
45
  }
44
46
  /**
45
47
  * Create a login for the user.
46
- * @param email The email address for the user.
47
- * @param password The password for the user.
48
- * @param userIdentity The DID to associate with the account.
49
- * @param organizationIdentity The organization of the user.
50
- * @returns Nothing.
48
+ * @param user The user to create.
49
+ * @returns A promise that resolves when the user account has been created and the audit entry recorded.
51
50
  */
52
- async create(email, password, userIdentity, organizationIdentity) {
53
- Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "email", email);
54
- Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "password", password);
55
- Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "userIdentity", userIdentity);
56
- Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "organizationIdentity", organizationIdentity);
51
+ async create(user) {
52
+ Guards.object(EntityStorageAuthenticationAdminService.CLASS_NAME, "user", user);
53
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.email", user.email);
54
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.password", user.password);
55
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.userIdentity", user.userIdentity);
56
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.organizationIdentity", user.organizationIdentity);
57
+ Guards.array(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.scope", user.scope);
57
58
  try {
58
- if (password.length < this._minPasswordLength) {
59
- throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "passwordTooShort", {
60
- minLength: this._minPasswordLength
61
- });
62
- }
63
- const user = await this._userEntityStorage.get(email);
64
- if (user) {
59
+ PasswordValidator.validatePassword(user.password, {
60
+ minLength: this._minPasswordLength
61
+ });
62
+ const existingUser = await this._userEntityStorage.get(user.email);
63
+ if (Is.object(existingUser)) {
65
64
  throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "userExists");
66
65
  }
67
66
  const saltBytes = RandomHelper.generate(16);
68
- const passwordBytes = Converter.utf8ToBytes(password);
69
- const hashedPassword = await PasswordHelper.hashPassword(passwordBytes, saltBytes);
67
+ const passwordBytes = Converter.utf8ToBytes(user.password);
68
+ const hashedPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);
70
69
  const newUser = {
71
- email,
70
+ email: user.email,
72
71
  salt: Converter.bytesToBase64(saltBytes),
73
72
  password: hashedPassword,
74
- identity: userIdentity,
75
- organization: organizationIdentity
73
+ identity: user.userIdentity,
74
+ organization: user.organizationIdentity,
75
+ scope: user.scope.map(s => s.trim().toLocaleLowerCase()).join(","),
76
+ passwordVersion: 0
76
77
  };
77
78
  await this._userEntityStorage.set(newUser);
79
+ const contextIds = await ContextIdStore.getContextIds();
80
+ const requestorTenantId = contextIds?.[ContextIdKeys.Tenant];
81
+ await this._authenticationAuditService?.create({
82
+ actorId: user.email,
83
+ event: AuthAuditEvent.AccountCreated,
84
+ data: {
85
+ userIdentity: user.userIdentity,
86
+ organizationIdentity: user.organizationIdentity,
87
+ tenantId: requestorTenantId,
88
+ scope: user.scope
89
+ }
90
+ });
78
91
  }
79
92
  catch (error) {
80
93
  throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "createUserFailed", undefined, error);
81
94
  }
82
95
  }
96
+ /**
97
+ * Update a login for the user.
98
+ * @param user The user to update.
99
+ * @returns A promise that resolves when the user account has been updated and the audit entry recorded.
100
+ */
101
+ async update(user) {
102
+ Guards.object(EntityStorageAuthenticationAdminService.CLASS_NAME, "user", user);
103
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.email", user.email);
104
+ if (!Is.empty(user.userIdentity)) {
105
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.userIdentity", user.userIdentity);
106
+ }
107
+ if (!Is.empty(user.organizationIdentity)) {
108
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.organizationIdentity", user.organizationIdentity);
109
+ }
110
+ if (!Is.empty(user.scope)) {
111
+ Guards.array(EntityStorageAuthenticationAdminService.CLASS_NAME, "user.scope", user.scope);
112
+ }
113
+ try {
114
+ const existingUser = await this._userEntityStorage.get(user.email);
115
+ if (!Is.object(existingUser)) {
116
+ throw new NotFoundError(EntityStorageAuthenticationAdminService.CLASS_NAME, "userNotFound", user.email);
117
+ }
118
+ const updatedFields = [];
119
+ const updatedScope = Is.array(user.scope)
120
+ ? user.scope.map(s => s.trim().toLocaleLowerCase()).join(",")
121
+ : existingUser.scope;
122
+ if (user.userIdentity !== undefined && user.userIdentity !== existingUser.identity) {
123
+ updatedFields.push("userIdentity");
124
+ }
125
+ if (user.organizationIdentity !== undefined &&
126
+ user.organizationIdentity !== existingUser.organization) {
127
+ updatedFields.push("organizationIdentity");
128
+ }
129
+ if (Is.array(user.scope) && updatedScope !== existingUser.scope) {
130
+ updatedFields.push("scope");
131
+ }
132
+ existingUser.identity = user.userIdentity ?? existingUser.identity;
133
+ existingUser.organization = user.organizationIdentity ?? existingUser.organization;
134
+ existingUser.scope = Is.array(user.scope) ? updatedScope : existingUser.scope;
135
+ await this._userEntityStorage.set(existingUser);
136
+ const contextIds = await ContextIdStore.getContextIds();
137
+ const requestorTenantId = contextIds?.[ContextIdKeys.Tenant];
138
+ await this._authenticationAuditService?.create({
139
+ actorId: existingUser.email,
140
+ event: AuthAuditEvent.AccountUpdated,
141
+ data: {
142
+ updatedFields,
143
+ userIdentity: existingUser.identity,
144
+ organizationIdentity: existingUser.organization,
145
+ tenantId: requestorTenantId,
146
+ scope: existingUser.scope.split(",")
147
+ }
148
+ });
149
+ }
150
+ catch (error) {
151
+ throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "updateUserFailed", undefined, error);
152
+ }
153
+ }
154
+ /**
155
+ * Get a user by email.
156
+ * @param email The email address of the user to get.
157
+ * @returns The user details.
158
+ */
159
+ async get(email) {
160
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "email", email);
161
+ try {
162
+ const user = await this._userEntityStorage.get(email);
163
+ if (!Is.object(user)) {
164
+ throw new NotFoundError(EntityStorageAuthenticationAdminService.CLASS_NAME, "userNotFound", email);
165
+ }
166
+ return {
167
+ email: user.email,
168
+ userIdentity: user.identity,
169
+ organizationIdentity: user.organization,
170
+ scope: user.scope.split(",")
171
+ };
172
+ }
173
+ catch (error) {
174
+ throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "getUserFailed", undefined, error);
175
+ }
176
+ }
177
+ /**
178
+ * Get a user by identity.
179
+ * @param identity The identity of the user to get.
180
+ * @returns The user details.
181
+ */
182
+ async getByIdentity(identity) {
183
+ Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "identity", identity);
184
+ try {
185
+ const user = await this._userEntityStorage.get(identity, "identity");
186
+ if (!Is.object(user)) {
187
+ throw new NotFoundError(EntityStorageAuthenticationAdminService.CLASS_NAME, "userNotFound", identity);
188
+ }
189
+ return {
190
+ email: user.email,
191
+ userIdentity: user.identity,
192
+ organizationIdentity: user.organization,
193
+ scope: user.scope.split(",")
194
+ };
195
+ }
196
+ catch (error) {
197
+ throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "getUserFailed", undefined, error);
198
+ }
199
+ }
83
200
  /**
84
201
  * Remove the current user.
85
202
  * @param email The email address of the user to remove.
86
- * @returns Nothing.
203
+ * @returns A promise that resolves when the user account has been removed and the audit entry recorded.
87
204
  */
88
205
  async remove(email) {
89
206
  Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "email", email);
90
207
  try {
91
208
  const user = await this._userEntityStorage.get(email);
92
- if (!user) {
209
+ if (!Is.object(user)) {
93
210
  throw new NotFoundError(EntityStorageAuthenticationAdminService.CLASS_NAME, "userNotFound", email);
94
211
  }
95
212
  await this._userEntityStorage.remove(email);
213
+ const contextIds = await ContextIdStore.getContextIds();
214
+ const requestorTenantId = contextIds?.[ContextIdKeys.Tenant];
215
+ await this._authenticationAuditService?.create({
216
+ actorId: email,
217
+ event: AuthAuditEvent.AccountDeleted,
218
+ data: {
219
+ userIdentity: user.identity,
220
+ organizationIdentity: user.organization,
221
+ tenantId: requestorTenantId,
222
+ scope: user.scope.split(",")
223
+ }
224
+ });
96
225
  }
97
226
  catch (error) {
98
227
  throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "removeUserFailed", undefined, error);
@@ -103,40 +232,17 @@ export class EntityStorageAuthenticationAdminService {
103
232
  * @param email The email address of the user to update.
104
233
  * @param newPassword The new password for the user.
105
234
  * @param currentPassword The current password, optional, if supplied will check against existing.
106
- * @returns Nothing.
235
+ * @returns A promise that resolves when the password has been updated.
107
236
  */
108
237
  async updatePassword(email, newPassword, currentPassword) {
109
238
  Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "email", email);
110
239
  Guards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, "newPassword", newPassword);
111
240
  try {
112
- if (newPassword.length < this._minPasswordLength) {
113
- throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "passwordTooShort", {
114
- minLength: this._minPasswordLength
115
- });
116
- }
117
241
  const user = await this._userEntityStorage.get(email);
118
- if (!user) {
242
+ if (!Is.object(user)) {
119
243
  throw new NotFoundError(EntityStorageAuthenticationAdminService.CLASS_NAME, "userNotFound", email);
120
244
  }
121
- if (Is.stringValue(currentPassword)) {
122
- const saltBytes = Converter.base64ToBytes(user.salt);
123
- const passwordBytes = Converter.utf8ToBytes(currentPassword);
124
- const hashedPassword = await PasswordHelper.hashPassword(passwordBytes, saltBytes);
125
- if (hashedPassword !== user.password) {
126
- throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "currentPasswordMismatch");
127
- }
128
- }
129
- const saltBytes = RandomHelper.generate(16);
130
- const passwordBytes = Converter.utf8ToBytes(newPassword);
131
- const hashedPassword = await PasswordHelper.hashPassword(passwordBytes, saltBytes);
132
- const updatedUser = {
133
- email,
134
- salt: Converter.bytesToBase64(saltBytes),
135
- password: hashedPassword,
136
- identity: user.identity,
137
- organization: user.organization
138
- };
139
- await this._userEntityStorage.set(updatedUser);
245
+ await PasswordHelper.updatePassword(this._userEntityStorage, this._authenticationAuditService, user, newPassword, currentPassword, this._minPasswordLength);
140
246
  }
141
247
  catch (error) {
142
248
  throw new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, "updatePasswordFailed", undefined, error);
@@ -1 +1 @@
1
- {"version":3,"file":"entityStorageAuthenticationAdminService.js","sourceRoot":"","sources":["../../../src/services/entityStorageAuthenticationAdminService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAuthenticationAdminComponent } from \"@twin.org/api-auth-entity-storage-models\";\nimport { Converter, GeneralError, Guards, Is, NotFoundError, RandomHelper } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IEntityStorageAuthenticationAdminServiceConstructorOptions } from \"../models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js\";\nimport { PasswordHelper } from \"../utils/passwordHelper.js\";\n\n/**\n * Implementation of the authentication component using entity storage.\n */\nexport class EntityStorageAuthenticationAdminService implements IAuthenticationAdminComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageAuthenticationAdminService>();\n\n\t/**\n\t * The minimum password length.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_MIN_PASSWORD_LENGTH: number = 8;\n\n\t/**\n\t * The entity storage for users.\n\t * @internal\n\t */\n\tprivate readonly _userEntityStorage: IEntityStorageConnector<AuthenticationUser>;\n\n\t/**\n\t * The minimum password length.\n\t * @internal\n\t */\n\tprivate readonly _minPasswordLength: number;\n\n\t/**\n\t * Create a new instance of EntityStorageAuthentication.\n\t * @param options The dependencies for the identity connector.\n\t */\n\tconstructor(options?: IEntityStorageAuthenticationAdminServiceConstructorOptions) {\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\n\t\tthis._minPasswordLength =\n\t\t\toptions?.config?.minPasswordLength ??\n\t\t\tEntityStorageAuthenticationAdminService._DEFAULT_MIN_PASSWORD_LENGTH;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn EntityStorageAuthenticationAdminService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create a login for the user.\n\t * @param email The email address for the user.\n\t * @param password The password for the user.\n\t * @param userIdentity The DID to associate with the account.\n\t * @param organizationIdentity The organization of the user.\n\t * @returns Nothing.\n\t */\n\tpublic async create(\n\t\temail: string,\n\t\tpassword: string,\n\t\tuserIdentity: string,\n\t\torganizationIdentity: string\n\t): Promise<void> {\n\t\tGuards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, nameof(email), email);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(password),\n\t\t\tpassword\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(userIdentity),\n\t\t\tuserIdentity\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(organizationIdentity),\n\t\t\torganizationIdentity\n\t\t);\n\n\t\ttry {\n\t\t\tif (password.length < this._minPasswordLength) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"passwordTooShort\",\n\t\t\t\t\t{\n\t\t\t\t\t\tminLength: this._minPasswordLength\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (user) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, \"userExists\");\n\t\t\t}\n\n\t\t\tconst saltBytes = RandomHelper.generate(16);\n\t\t\tconst passwordBytes = Converter.utf8ToBytes(password);\n\n\t\t\tconst hashedPassword = await PasswordHelper.hashPassword(passwordBytes, saltBytes);\n\n\t\t\tconst newUser: AuthenticationUser = {\n\t\t\t\temail,\n\t\t\t\tsalt: Converter.bytesToBase64(saltBytes),\n\t\t\t\tpassword: hashedPassword,\n\t\t\t\tidentity: userIdentity,\n\t\t\t\torganization: organizationIdentity\n\t\t\t};\n\n\t\t\tawait this._userEntityStorage.set(newUser);\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"createUserFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the current user.\n\t * @param email The email address of the user to remove.\n\t * @returns Nothing.\n\t */\n\tpublic async remove(email: string): Promise<void> {\n\t\tGuards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, nameof(email), email);\n\n\t\ttry {\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!user) {\n\t\t\t\tthrow new NotFoundError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"userNotFound\",\n\t\t\t\t\temail\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait this._userEntityStorage.remove(email);\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"removeUserFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Update the user's password.\n\t * @param email The email address of the user to update.\n\t * @param newPassword The new password for the user.\n\t * @param currentPassword The current password, optional, if supplied will check against existing.\n\t * @returns Nothing.\n\t */\n\tpublic async updatePassword(\n\t\temail: string,\n\t\tnewPassword: string,\n\t\tcurrentPassword?: string\n\t): Promise<void> {\n\t\tGuards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, nameof(email), email);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(newPassword),\n\t\t\tnewPassword\n\t\t);\n\n\t\ttry {\n\t\t\tif (newPassword.length < this._minPasswordLength) {\n\t\t\t\tthrow new GeneralError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"passwordTooShort\",\n\t\t\t\t\t{\n\t\t\t\t\t\tminLength: this._minPasswordLength\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!user) {\n\t\t\t\tthrow new NotFoundError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"userNotFound\",\n\t\t\t\t\temail\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (Is.stringValue(currentPassword)) {\n\t\t\t\tconst saltBytes = Converter.base64ToBytes(user.salt);\n\t\t\t\tconst passwordBytes = Converter.utf8ToBytes(currentPassword);\n\n\t\t\t\tconst hashedPassword = await PasswordHelper.hashPassword(passwordBytes, saltBytes);\n\n\t\t\t\tif (hashedPassword !== user.password) {\n\t\t\t\t\tthrow new GeneralError(\n\t\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\t\"currentPasswordMismatch\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst saltBytes = RandomHelper.generate(16);\n\t\t\tconst passwordBytes = Converter.utf8ToBytes(newPassword);\n\n\t\t\tconst hashedPassword = await PasswordHelper.hashPassword(passwordBytes, saltBytes);\n\n\t\t\tconst updatedUser: AuthenticationUser = {\n\t\t\t\temail,\n\t\t\t\tsalt: Converter.bytesToBase64(saltBytes),\n\t\t\t\tpassword: hashedPassword,\n\t\t\t\tidentity: user.identity,\n\t\t\t\torganization: user.organization\n\t\t\t};\n\n\t\t\tawait this._userEntityStorage.set(updatedUser);\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"updatePasswordFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"entityStorageAuthenticationAdminService.js","sourceRoot":"","sources":["../../../src/services/entityStorageAuthenticationAdminService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationAdminComponent,\n\tIAuthenticationAuditComponent,\n\tIAuthenticationUser\n} from \"@twin.org/api-auth-entity-storage-models\";\nimport { AuthAuditEvent } from \"@twin.org/api-auth-entity-storage-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport {\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tNotFoundError,\n\tRandomHelper\n} from \"@twin.org/core\";\nimport { PasswordGenerator, PasswordValidator } from \"@twin.org/crypto\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IEntityStorageAuthenticationAdminServiceConstructorOptions } from \"../models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js\";\nimport { PasswordHelper } from \"../utils/passwordHelper.js\";\n\n/**\n * Implementation of the authentication component using entity storage.\n */\nexport class EntityStorageAuthenticationAdminService implements IAuthenticationAdminComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageAuthenticationAdminService>();\n\n\t/**\n\t * The entity storage for users.\n\t * @internal\n\t */\n\tprivate readonly _userEntityStorage: IEntityStorageConnector<AuthenticationUser>;\n\n\t/**\n\t * The audit service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationAuditService?: IAuthenticationAuditComponent;\n\n\t/**\n\t * The minimum password length.\n\t * @internal\n\t */\n\tprivate readonly _minPasswordLength?: number;\n\n\t/**\n\t * Create a new instance of EntityStorageAuthentication.\n\t * @param options The dependencies for the identity connector.\n\t */\n\tconstructor(options?: IEntityStorageAuthenticationAdminServiceConstructorOptions) {\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\n\t\tthis._authenticationAuditService = ComponentFactory.getIfExists<IAuthenticationAuditComponent>(\n\t\t\toptions?.authenticationAuditServiceType ?? \"authentication-audit\"\n\t\t);\n\n\t\tthis._minPasswordLength = options?.config?.minPasswordLength;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn EntityStorageAuthenticationAdminService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create a login for the user.\n\t * @param user The user to create.\n\t * @returns A promise that resolves when the user account has been created and the audit entry recorded.\n\t */\n\tpublic async create(user: IAuthenticationUser & { password: string }): Promise<void> {\n\t\tGuards.object<IAuthenticationUser>(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user),\n\t\t\tuser\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user.email),\n\t\t\tuser.email\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user.password),\n\t\t\tuser.password\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user.userIdentity),\n\t\t\tuser.userIdentity\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user.organizationIdentity),\n\t\t\tuser.organizationIdentity\n\t\t);\n\t\tGuards.array<string>(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user.scope),\n\t\t\tuser.scope\n\t\t);\n\n\t\ttry {\n\t\t\tPasswordValidator.validatePassword(user.password, {\n\t\t\t\tminLength: this._minPasswordLength\n\t\t\t});\n\n\t\t\tconst existingUser = await this._userEntityStorage.get(user.email);\n\t\t\tif (Is.object<AuthenticationUser>(existingUser)) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationAdminService.CLASS_NAME, \"userExists\");\n\t\t\t}\n\n\t\t\tconst saltBytes = RandomHelper.generate(16);\n\t\t\tconst passwordBytes = Converter.utf8ToBytes(user.password);\n\n\t\t\tconst hashedPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);\n\n\t\t\tconst newUser: AuthenticationUser = {\n\t\t\t\temail: user.email,\n\t\t\t\tsalt: Converter.bytesToBase64(saltBytes),\n\t\t\t\tpassword: hashedPassword,\n\t\t\t\tidentity: user.userIdentity,\n\t\t\t\torganization: user.organizationIdentity,\n\t\t\t\tscope: user.scope.map(s => s.trim().toLocaleLowerCase()).join(\",\"),\n\t\t\t\tpasswordVersion: 0\n\t\t\t};\n\n\t\t\tawait this._userEntityStorage.set(newUser);\n\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst requestorTenantId = contextIds?.[ContextIdKeys.Tenant];\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: user.email,\n\t\t\t\tevent: AuthAuditEvent.AccountCreated,\n\t\t\t\tdata: {\n\t\t\t\t\tuserIdentity: user.userIdentity,\n\t\t\t\t\torganizationIdentity: user.organizationIdentity,\n\t\t\t\t\ttenantId: requestorTenantId,\n\t\t\t\t\tscope: user.scope\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"createUserFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Update a login for the user.\n\t * @param user The user to update.\n\t * @returns A promise that resolves when the user account has been updated and the audit entry recorded.\n\t */\n\tpublic async update(user: Partial<IAuthenticationUser>): Promise<void> {\n\t\tGuards.object<IAuthenticationUser>(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user),\n\t\t\tuser\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(user.email),\n\t\t\tuser.email\n\t\t);\n\n\t\tif (!Is.empty(user.userIdentity)) {\n\t\t\tGuards.stringValue(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\tnameof(user.userIdentity),\n\t\t\t\tuser.userIdentity\n\t\t\t);\n\t\t}\n\t\tif (!Is.empty(user.organizationIdentity)) {\n\t\t\tGuards.stringValue(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\tnameof(user.organizationIdentity),\n\t\t\t\tuser.organizationIdentity\n\t\t\t);\n\t\t}\n\t\tif (!Is.empty(user.scope)) {\n\t\t\tGuards.array<string>(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\tnameof(user.scope),\n\t\t\t\tuser.scope\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst existingUser = await this._userEntityStorage.get(user.email);\n\t\t\tif (!Is.object<AuthenticationUser>(existingUser)) {\n\t\t\t\tthrow new NotFoundError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"userNotFound\",\n\t\t\t\t\tuser.email\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst updatedFields: string[] = [];\n\t\t\tconst updatedScope = Is.array(user.scope)\n\t\t\t\t? user.scope.map(s => s.trim().toLocaleLowerCase()).join(\",\")\n\t\t\t\t: existingUser.scope;\n\n\t\t\tif (user.userIdentity !== undefined && user.userIdentity !== existingUser.identity) {\n\t\t\t\tupdatedFields.push(\"userIdentity\");\n\t\t\t}\n\t\t\tif (\n\t\t\t\tuser.organizationIdentity !== undefined &&\n\t\t\t\tuser.organizationIdentity !== existingUser.organization\n\t\t\t) {\n\t\t\t\tupdatedFields.push(\"organizationIdentity\");\n\t\t\t}\n\t\t\tif (Is.array(user.scope) && updatedScope !== existingUser.scope) {\n\t\t\t\tupdatedFields.push(\"scope\");\n\t\t\t}\n\n\t\t\texistingUser.identity = user.userIdentity ?? existingUser.identity;\n\t\t\texistingUser.organization = user.organizationIdentity ?? existingUser.organization;\n\t\t\texistingUser.scope = Is.array(user.scope) ? updatedScope : existingUser.scope;\n\n\t\t\tawait this._userEntityStorage.set(existingUser);\n\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst requestorTenantId = contextIds?.[ContextIdKeys.Tenant];\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: existingUser.email,\n\t\t\t\tevent: AuthAuditEvent.AccountUpdated,\n\t\t\t\tdata: {\n\t\t\t\t\tupdatedFields,\n\t\t\t\t\tuserIdentity: existingUser.identity,\n\t\t\t\t\torganizationIdentity: existingUser.organization,\n\t\t\t\t\ttenantId: requestorTenantId,\n\t\t\t\t\tscope: existingUser.scope.split(\",\")\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"updateUserFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get a user by email.\n\t * @param email The email address of the user to get.\n\t * @returns The user details.\n\t */\n\tpublic async get(email: string): Promise<IAuthenticationUser> {\n\t\tGuards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, nameof(email), email);\n\n\t\ttry {\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!Is.object<AuthenticationUser>(user)) {\n\t\t\t\tthrow new NotFoundError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"userNotFound\",\n\t\t\t\t\temail\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\temail: user.email,\n\t\t\t\tuserIdentity: user.identity,\n\t\t\t\torganizationIdentity: user.organization,\n\t\t\t\tscope: user.scope.split(\",\")\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"getUserFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get a user by identity.\n\t * @param identity The identity of the user to get.\n\t * @returns The user details.\n\t */\n\tpublic async getByIdentity(identity: string): Promise<IAuthenticationUser> {\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(identity),\n\t\t\tidentity\n\t\t);\n\n\t\ttry {\n\t\t\tconst user = await this._userEntityStorage.get(identity, \"identity\");\n\t\t\tif (!Is.object<AuthenticationUser>(user)) {\n\t\t\t\tthrow new NotFoundError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"userNotFound\",\n\t\t\t\t\tidentity\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\temail: user.email,\n\t\t\t\tuserIdentity: user.identity,\n\t\t\t\torganizationIdentity: user.organization,\n\t\t\t\tscope: user.scope.split(\",\")\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"getUserFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Remove the current user.\n\t * @param email The email address of the user to remove.\n\t * @returns A promise that resolves when the user account has been removed and the audit entry recorded.\n\t */\n\tpublic async remove(email: string): Promise<void> {\n\t\tGuards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, nameof(email), email);\n\n\t\ttry {\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!Is.object<AuthenticationUser>(user)) {\n\t\t\t\tthrow new NotFoundError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"userNotFound\",\n\t\t\t\t\temail\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait this._userEntityStorage.remove(email);\n\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tconst requestorTenantId = contextIds?.[ContextIdKeys.Tenant];\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: email,\n\t\t\t\tevent: AuthAuditEvent.AccountDeleted,\n\t\t\t\tdata: {\n\t\t\t\t\tuserIdentity: user.identity,\n\t\t\t\t\torganizationIdentity: user.organization,\n\t\t\t\t\ttenantId: requestorTenantId,\n\t\t\t\t\tscope: user.scope.split(\",\")\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"removeUserFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Update the user's password.\n\t * @param email The email address of the user to update.\n\t * @param newPassword The new password for the user.\n\t * @param currentPassword The current password, optional, if supplied will check against existing.\n\t * @returns A promise that resolves when the password has been updated.\n\t */\n\tpublic async updatePassword(\n\t\temail: string,\n\t\tnewPassword: string,\n\t\tcurrentPassword?: string\n\t): Promise<void> {\n\t\tGuards.stringValue(EntityStorageAuthenticationAdminService.CLASS_NAME, nameof(email), email);\n\t\tGuards.stringValue(\n\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\tnameof(newPassword),\n\t\t\tnewPassword\n\t\t);\n\n\t\ttry {\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!Is.object<AuthenticationUser>(user)) {\n\t\t\t\tthrow new NotFoundError(\n\t\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\t\"userNotFound\",\n\t\t\t\t\temail\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tawait PasswordHelper.updatePassword(\n\t\t\t\tthis._userEntityStorage,\n\t\t\t\tthis._authenticationAuditService,\n\t\t\t\tuser,\n\t\t\t\tnewPassword,\n\t\t\t\tcurrentPassword,\n\t\t\t\tthis._minPasswordLength\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tEntityStorageAuthenticationAdminService.CLASS_NAME,\n\t\t\t\t\"updatePasswordFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n}\n"]}