Browse Source

feat: refactor deleting account (#676)

* fix(api-gateway): TYPES aliases

* feat: refactor account deleting
Karol Sójko 1 năm trước cách đây
mục cha
commit
0d5dcdd8ec
70 tập tin đã thay đổi với 683 bổ sung341 xóa
  1. 2 2
      packages/api-gateway/bin/server.ts
  2. 1 0
      packages/api-gateway/package.json
  3. 31 26
      packages/api-gateway/src/Bootstrap/Container.ts
  4. 23 24
      packages/api-gateway/src/Bootstrap/Types.ts
  5. 4 4
      packages/api-gateway/src/Controller/LegacyController.ts
  6. 6 6
      packages/api-gateway/src/Controller/OptionalCrossServiceTokenMiddleware.ts
  7. 6 6
      packages/api-gateway/src/Controller/RequiredCrossServiceTokenMiddleware.ts
  8. 4 4
      packages/api-gateway/src/Controller/SubscriptionTokenAuthMiddleware.ts
  9. 4 4
      packages/api-gateway/src/Controller/WebSocketAuthMiddleware.ts
  10. 5 5
      packages/api-gateway/src/Controller/v1/ActionsController.ts
  11. 6 6
      packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts
  12. 3 3
      packages/api-gateway/src/Controller/v1/FilesController.ts
  13. 2 2
      packages/api-gateway/src/Controller/v1/InvoicesController.ts
  14. 3 3
      packages/api-gateway/src/Controller/v1/ItemsController.ts
  15. 3 3
      packages/api-gateway/src/Controller/v1/MessagesController.ts
  16. 2 2
      packages/api-gateway/src/Controller/v1/OfflineController.ts
  17. 4 4
      packages/api-gateway/src/Controller/v1/PaymentsController.ts
  18. 1 1
      packages/api-gateway/src/Controller/v1/RevisionsController.ts
  19. 5 5
      packages/api-gateway/src/Controller/v1/SessionsController.ts
  20. 3 3
      packages/api-gateway/src/Controller/v1/SharedVaultInvitesController.ts
  21. 3 3
      packages/api-gateway/src/Controller/v1/SharedVaultUsersController.ts
  22. 3 3
      packages/api-gateway/src/Controller/v1/SharedVaultsController.ts
  23. 6 6
      packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts
  24. 3 3
      packages/api-gateway/src/Controller/v1/TokensController.ts
  25. 33 21
      packages/api-gateway/src/Controller/v1/UsersController.ts
  26. 5 5
      packages/api-gateway/src/Controller/v1/WebSocketsController.ts
  27. 3 3
      packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts
  28. 8 8
      packages/api-gateway/src/Controller/v2/PaymentsControllerV2.ts
  29. 3 3
      packages/api-gateway/src/Controller/v2/RevisionsControllerV2.ts
  30. 1 1
      packages/api-gateway/src/Infra/Redis/RedisCrossServiceTokenCache.ts
  31. 41 18
      packages/api-gateway/src/Service/Http/HttpServiceProxy.ts
  32. 2 1
      packages/api-gateway/src/Service/Http/ServiceProxyInterface.ts
  33. 1 0
      packages/api-gateway/src/Service/Resolver/EndpointResolver.ts
  34. 17 3
      packages/auth/src/Domain/Auth/AuthenticationMethodResolver.spec.ts
  35. 15 2
      packages/auth/src/Domain/Auth/AuthenticationMethodResolver.ts
  36. 11 3
      packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.spec.ts
  37. 13 4
      packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.ts
  38. 14 6
      packages/auth/src/Domain/Handler/FileUploadedEventHandler.spec.ts
  39. 13 4
      packages/auth/src/Domain/Handler/FileUploadedEventHandler.ts
  40. 18 3
      packages/auth/src/Domain/Role/RoleService.spec.ts
  41. 9 2
      packages/auth/src/Domain/Role/RoleService.ts
  42. 22 4
      packages/auth/src/Domain/Setting/SettingDecrypter.spec.ts
  43. 9 2
      packages/auth/src/Domain/Setting/SettingDecrypter.ts
  44. 1 3
      packages/auth/src/Domain/UseCase/AuthenticateSubscriptionToken/AuthenticateSubscriptionToken.spec.ts
  45. 5 2
      packages/auth/src/Domain/UseCase/AuthenticateSubscriptionToken/AuthenticateSubscriptionToken.ts
  46. 22 7
      packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts
  47. 8 1
      packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts
  48. 8 8
      packages/auth/src/Domain/UseCase/DeleteAccount/DeleteAccount.spec.ts
  49. 6 6
      packages/auth/src/Domain/UseCase/DeleteAccount/DeleteAccount.ts
  50. 1 1
      packages/auth/src/Domain/UseCase/DeleteAccount/DeleteAccountDTO.ts
  51. 1 1
      packages/auth/src/Domain/UseCase/DeleteAuthenticator/DeleteAuthenticator.ts
  52. 1 1
      packages/auth/src/Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions.ts
  53. 1 1
      packages/auth/src/Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes.ts
  54. 42 18
      packages/auth/src/Domain/UseCase/GetSettings/GetSettings.spec.ts
  55. 14 4
      packages/auth/src/Domain/UseCase/GetSettings/GetSettings.ts
  56. 26 13
      packages/auth/src/Domain/UseCase/GetUserFeatures/GetUserFeatures.spec.ts
  57. 15 3
      packages/auth/src/Domain/UseCase/GetUserFeatures/GetUserFeatures.ts
  58. 25 4
      packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.spec.ts
  59. 8 2
      packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts
  60. 17 5
      packages/auth/src/Domain/UseCase/GetUserSubscription/GetUserSubscription.spec.ts
  61. 13 3
      packages/auth/src/Domain/UseCase/GetUserSubscription/GetUserSubscription.ts
  62. 1 1
      packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.ts
  63. 34 13
      packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.spec.ts
  64. 19 8
      packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.ts
  65. 1 1
      packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.ts
  66. 2 2
      packages/auth/src/Domain/User/UserRepositoryInterface.ts
  67. 14 2
      packages/auth/src/Infra/InversifyExpressUtils/HomeServer/HomeServerUsersController.ts
  68. 20 3
      packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.spec.ts
  69. 3 3
      packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.ts
  70. 4 4
      packages/auth/src/Infra/TypeORM/TypeORMUserRepository.ts

+ 2 - 2
packages/api-gateway/bin/server.ts

@@ -46,7 +46,7 @@ void container.load().then((container) => {
 
   server.setConfig((app) => {
     app.use((_request: Request, response: Response, next: NextFunction) => {
-      response.setHeader('X-API-Gateway-Version', container.get(TYPES.VERSION))
+      response.setHeader('X-API-Gateway-Version', container.get(TYPES.ApiGateway_VERSION))
       next()
     })
     app.use(
@@ -87,7 +87,7 @@ void container.load().then((container) => {
     )
   })
 
-  const logger: winston.Logger = container.get(TYPES.Logger)
+  const logger: winston.Logger = container.get(TYPES.ApiGateway_Logger)
 
   server.setErrorConfig((app) => {
     app.use((error: Record<string, unknown>, _request: Request, response: Response, _next: NextFunction) => {

+ 1 - 0
packages/api-gateway/package.json

@@ -21,6 +21,7 @@
     "clean": "rm -fr dist",
     "build": "tsc --build",
     "lint": "eslint . --ext .ts",
+    "lint:fix": "eslint . --fix --ext .ts",
     "setup:env": "cp .env.sample .env",
     "start": "yarn node dist/bin/server.js",
     "upgrade:snjs": "yarn ncu -u '@standardnotes/*'"

+ 31 - 26
packages/api-gateway/src/Bootstrap/Container.ts

@@ -57,7 +57,7 @@ export class ContainerConfigLoader {
         defaultMeta: { service: 'api-gateway' },
       })
     }
-    container.bind<winston.Logger>(TYPES.Logger).toConstantValue(logger)
+    container.bind<winston.Logger>(TYPES.ApiGateway_Logger).toConstantValue(logger)
 
     if (!isConfiguredForInMemoryCache) {
       const redisUrl = env.get('REDIS_URL')
@@ -68,36 +68,39 @@ export class ContainerConfigLoader {
       } else {
         redis = new Redis(redisUrl)
       }
-      container.bind(TYPES.Redis).toConstantValue(redis)
+      container.bind(TYPES.ApiGateway_Redis).toConstantValue(redis)
     }
 
-    container.bind<AxiosInstance>(TYPES.HTTPClient).toConstantValue(axios.create())
+    container.bind<AxiosInstance>(TYPES.ApiGateway_HTTPClient).toConstantValue(axios.create())
 
     // env vars
-    container.bind(TYPES.SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL', true))
-    container.bind(TYPES.AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL', true))
-    container.bind(TYPES.REVISIONS_SERVER_URL).toConstantValue(env.get('REVISIONS_SERVER_URL', true))
-    container.bind(TYPES.EMAIL_SERVER_URL).toConstantValue(env.get('EMAIL_SERVER_URL', true))
-    container.bind(TYPES.PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
-    container.bind(TYPES.FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
-    container.bind(TYPES.WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL', true))
-    container.bind(TYPES.AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
+    container.bind(TYPES.ApiGateway_SYNCING_SERVER_JS_URL).toConstantValue(env.get('SYNCING_SERVER_JS_URL', true))
+    container.bind(TYPES.ApiGateway_AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL', true))
+    container.bind(TYPES.ApiGateway_REVISIONS_SERVER_URL).toConstantValue(env.get('REVISIONS_SERVER_URL', true))
+    container.bind(TYPES.ApiGateway_EMAIL_SERVER_URL).toConstantValue(env.get('EMAIL_SERVER_URL', true))
+    container.bind(TYPES.ApiGateway_PAYMENTS_SERVER_URL).toConstantValue(env.get('PAYMENTS_SERVER_URL', true))
+    container.bind(TYPES.ApiGateway_FILES_SERVER_URL).toConstantValue(env.get('FILES_SERVER_URL', true))
+    container.bind(TYPES.ApiGateway_WEB_SOCKET_SERVER_URL).toConstantValue(env.get('WEB_SOCKET_SERVER_URL', true))
+    container.bind(TYPES.ApiGateway_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
     container
-      .bind(TYPES.HTTP_CALL_TIMEOUT)
+      .bind(TYPES.ApiGateway_HTTP_CALL_TIMEOUT)
       .toConstantValue(env.get('HTTP_CALL_TIMEOUT', true) ? +env.get('HTTP_CALL_TIMEOUT', true) : 60_000)
-    container.bind(TYPES.VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
-    container.bind(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL).toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
+    container.bind(TYPES.ApiGateway_VERSION).toConstantValue(env.get('VERSION', true) ?? 'development')
+    container
+      .bind(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL)
+      .toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true))
+    container.bind(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER).toConstantValue(isConfiguredForHomeServer)
 
     // Middleware
     container
-      .bind<RequiredCrossServiceTokenMiddleware>(TYPES.RequiredCrossServiceTokenMiddleware)
+      .bind<RequiredCrossServiceTokenMiddleware>(TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
       .to(RequiredCrossServiceTokenMiddleware)
     container
-      .bind<OptionalCrossServiceTokenMiddleware>(TYPES.OptionalCrossServiceTokenMiddleware)
+      .bind<OptionalCrossServiceTokenMiddleware>(TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
       .to(OptionalCrossServiceTokenMiddleware)
-    container.bind<WebSocketAuthMiddleware>(TYPES.WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
+    container.bind<WebSocketAuthMiddleware>(TYPES.ApiGateway_WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
     container
-      .bind<SubscriptionTokenAuthMiddleware>(TYPES.SubscriptionTokenAuthMiddleware)
+      .bind<SubscriptionTokenAuthMiddleware>(TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
       .to(SubscriptionTokenAuthMiddleware)
 
     // Services
@@ -106,24 +109,26 @@ export class ContainerConfigLoader {
         throw new Error('Service container is required when configured for home server')
       }
       container
-        .bind<ServiceProxyInterface>(TYPES.ServiceProxy)
+        .bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy)
         .toConstantValue(
-          new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.FILES_SERVER_URL)),
+          new DirectCallServiceProxy(configuration.serviceContainer, container.get(TYPES.ApiGateway_FILES_SERVER_URL)),
         )
     } else {
-      container.bind<ServiceProxyInterface>(TYPES.ServiceProxy).to(HttpServiceProxy)
+      container.bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy).to(HttpServiceProxy)
     }
-    container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
+    container.bind<TimerInterface>(TYPES.ApiGateway_Timer).toConstantValue(new Timer())
 
     if (isConfiguredForHomeServer) {
       container
-        .bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache)
-        .toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.Timer)))
+        .bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
+        .toConstantValue(new InMemoryCrossServiceTokenCache(container.get(TYPES.ApiGateway_Timer)))
     } else {
-      container.bind<CrossServiceTokenCacheInterface>(TYPES.CrossServiceTokenCache).to(RedisCrossServiceTokenCache)
+      container
+        .bind<CrossServiceTokenCacheInterface>(TYPES.ApiGateway_CrossServiceTokenCache)
+        .to(RedisCrossServiceTokenCache)
     }
     container
-      .bind<EndpointResolverInterface>(TYPES.EndpointResolver)
+      .bind<EndpointResolverInterface>(TYPES.ApiGateway_EndpointResolver)
       .toConstantValue(new EndpointResolver(isConfiguredForHomeServer))
 
     logger.debug('Configuration complete')

+ 23 - 24
packages/api-gateway/src/Bootstrap/Types.ts

@@ -1,29 +1,28 @@
 export const TYPES = {
-  Logger: Symbol.for('Logger'),
-  Redis: Symbol.for('Redis'),
-  HTTPClient: Symbol.for('HTTPClient'),
+  ApiGateway_Logger: Symbol.for('ApiGateway_Logger'),
+  ApiGateway_Redis: Symbol.for('ApiGateway_Redis'),
+  ApiGateway_HTTPClient: Symbol.for('ApiGateway_HTTPClient'),
   // env vars
-  SYNCING_SERVER_JS_URL: Symbol.for('SYNCING_SERVER_JS_URL'),
-  AUTH_SERVER_URL: Symbol.for('AUTH_SERVER_URL'),
-  PAYMENTS_SERVER_URL: Symbol.for('PAYMENTS_SERVER_URL'),
-  FILES_SERVER_URL: Symbol.for('FILES_SERVER_URL'),
-  REVISIONS_SERVER_URL: Symbol.for('REVISIONS_SERVER_URL'),
-  EMAIL_SERVER_URL: Symbol.for('EMAIL_SERVER_URL'),
-  WEB_SOCKET_SERVER_URL: Symbol.for('WEB_SOCKET_SERVER_URL'),
-  AUTH_JWT_SECRET: Symbol.for('AUTH_JWT_SECRET'),
-  HTTP_CALL_TIMEOUT: Symbol.for('HTTP_CALL_TIMEOUT'),
-  VERSION: Symbol.for('VERSION'),
-  CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('CROSS_SERVICE_TOKEN_CACHE_TTL'),
+  ApiGateway_SYNCING_SERVER_JS_URL: Symbol.for('ApiGateway_SYNCING_SERVER_JS_URL'),
+  ApiGateway_AUTH_SERVER_URL: Symbol.for('ApiGateway_AUTH_SERVER_URL'),
+  ApiGateway_PAYMENTS_SERVER_URL: Symbol.for('ApiGateway_PAYMENTS_SERVER_URL'),
+  ApiGateway_FILES_SERVER_URL: Symbol.for('ApiGateway_FILES_SERVER_URL'),
+  ApiGateway_REVISIONS_SERVER_URL: Symbol.for('ApiGateway_REVISIONS_SERVER_URL'),
+  ApiGateway_EMAIL_SERVER_URL: Symbol.for('ApiGateway_EMAIL_SERVER_URL'),
+  ApiGateway_WEB_SOCKET_SERVER_URL: Symbol.for('ApiGateway_WEB_SOCKET_SERVER_URL'),
+  ApiGateway_AUTH_JWT_SECRET: Symbol.for('ApiGateway_AUTH_JWT_SECRET'),
+  ApiGateway_HTTP_CALL_TIMEOUT: Symbol.for('ApiGateway_HTTP_CALL_TIMEOUT'),
+  ApiGateway_VERSION: Symbol.for('ApiGateway_VERSION'),
+  ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL'),
+  ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER: Symbol.for('ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER'),
   // Middleware
-  RequiredCrossServiceTokenMiddleware: Symbol.for('RequiredCrossServiceTokenMiddleware'),
-  OptionalCrossServiceTokenMiddleware: Symbol.for('OptionalCrossServiceTokenMiddleware'),
-  WebSocketAuthMiddleware: Symbol.for('WebSocketAuthMiddleware'),
-  SubscriptionTokenAuthMiddleware: Symbol.for('SubscriptionTokenAuthMiddleware'),
+  ApiGateway_RequiredCrossServiceTokenMiddleware: Symbol.for('ApiGateway_RequiredCrossServiceTokenMiddleware'),
+  ApiGateway_OptionalCrossServiceTokenMiddleware: Symbol.for('ApiGateway_OptionalCrossServiceTokenMiddleware'),
+  ApiGateway_WebSocketAuthMiddleware: Symbol.for('ApiGateway_WebSocketAuthMiddleware'),
+  ApiGateway_SubscriptionTokenAuthMiddleware: Symbol.for('ApiGateway_SubscriptionTokenAuthMiddleware'),
   // Services
-  ServiceProxy: Symbol.for('ServiceProxy'),
-  CrossServiceTokenCache: Symbol.for('CrossServiceTokenCache'),
-  Timer: Symbol.for('Timer'),
-  EndpointResolver: Symbol.for('EndpointResolver'),
+  ApiGateway_ServiceProxy: Symbol.for('ApiGateway_ServiceProxy'),
+  ApiGateway_CrossServiceTokenCache: Symbol.for('ApiGateway_CrossServiceTokenCache'),
+  ApiGateway_Timer: Symbol.for('ApiGateway_Timer'),
+  ApiGateway_EndpointResolver: Symbol.for('ApiGateway_EndpointResolver'),
 }
-
-// export default TYPES

+ 4 - 4
packages/api-gateway/src/Controller/LegacyController.ts

@@ -9,7 +9,7 @@ export class LegacyController extends BaseHttpController {
   private AUTH_ROUTES: Map<string, string>
   private PARAMETRIZED_AUTH_ROUTES: Map<string, string>
 
-  constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
+  constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
     super()
 
     this.AUTH_ROUTES = new Map([
@@ -29,17 +29,17 @@ export class LegacyController extends BaseHttpController {
     ])
   }
 
-  @httpPost('/items/sync', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/items/sync', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async legacyItemsSync(request: Request, response: Response): Promise<void> {
     await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
   }
 
-  @httpGet('/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/items/:item_id/revisions', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async legacyGetRevisions(request: Request, response: Response): Promise<void> {
     await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
   }
 
-  @httpGet('/items/:item_id/revisions/:id', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/items/:item_id/revisions/:id', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async legacyGetRevision(request: Request, response: Response): Promise<void> {
     await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
   }

+ 6 - 6
packages/api-gateway/src/Controller/OptionalCrossServiceTokenMiddleware.ts

@@ -11,12 +11,12 @@ import { AuthMiddleware } from './AuthMiddleware'
 @injectable()
 export class OptionalCrossServiceTokenMiddleware extends AuthMiddleware {
   constructor(
-    @inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
-    @inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
-    @inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
-    @inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
-    @inject(TYPES.Timer) timer: TimerInterface,
-    @inject(TYPES.Logger) logger: Logger,
+    @inject(TYPES.ApiGateway_ServiceProxy) serviceProxy: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_AUTH_JWT_SECRET) jwtSecret: string,
+    @inject(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
+    @inject(TYPES.ApiGateway_CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
+    @inject(TYPES.ApiGateway_Timer) timer: TimerInterface,
+    @inject(TYPES.ApiGateway_Logger) logger: Logger,
   ) {
     super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
   }

+ 6 - 6
packages/api-gateway/src/Controller/RequiredCrossServiceTokenMiddleware.ts

@@ -11,12 +11,12 @@ import { AuthMiddleware } from './AuthMiddleware'
 @injectable()
 export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware {
   constructor(
-    @inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface,
-    @inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string,
-    @inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
-    @inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
-    @inject(TYPES.Timer) timer: TimerInterface,
-    @inject(TYPES.Logger) logger: Logger,
+    @inject(TYPES.ApiGateway_ServiceProxy) serviceProxy: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_AUTH_JWT_SECRET) jwtSecret: string,
+    @inject(TYPES.ApiGateway_CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number,
+    @inject(TYPES.ApiGateway_CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface,
+    @inject(TYPES.ApiGateway_Timer) timer: TimerInterface,
+    @inject(TYPES.ApiGateway_Logger) logger: Logger,
   ) {
     super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger)
   }

+ 4 - 4
packages/api-gateway/src/Controller/SubscriptionTokenAuthMiddleware.ts

@@ -11,10 +11,10 @@ import { TokenAuthenticationMethod } from './TokenAuthenticationMethod'
 @injectable()
 export class SubscriptionTokenAuthMiddleware extends BaseMiddleware {
   constructor(
-    @inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
-    @inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
-    @inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
-    @inject(TYPES.Logger) private logger: Logger,
+    @inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
+    @inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
+    @inject(TYPES.ApiGateway_AUTH_JWT_SECRET) private jwtSecret: string,
+    @inject(TYPES.ApiGateway_Logger) private logger: Logger,
   ) {
     super()
   }

+ 4 - 4
packages/api-gateway/src/Controller/WebSocketAuthMiddleware.ts

@@ -12,10 +12,10 @@ import { TYPES } from '../Bootstrap/Types'
 @injectable()
 export class WebSocketAuthMiddleware extends BaseMiddleware {
   constructor(
-    @inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
-    @inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
-    @inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string,
-    @inject(TYPES.Logger) private logger: Logger,
+    @inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
+    @inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
+    @inject(TYPES.ApiGateway_AUTH_JWT_SECRET) private jwtSecret: string,
+    @inject(TYPES.ApiGateway_Logger) private logger: Logger,
   ) {
     super()
   }

+ 5 - 5
packages/api-gateway/src/Controller/v1/ActionsController.ts

@@ -8,8 +8,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1')
 export class ActionsController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }
@@ -24,7 +24,7 @@ export class ActionsController extends BaseHttpController {
     )
   }
 
-  @httpGet('/login-params', TYPES.OptionalCrossServiceTokenMiddleware)
+  @httpGet('/login-params', TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
   async loginParams(request: Request, response: Response): Promise<void> {
     await this.serviceProxy.callAuthServer(
       request,
@@ -34,7 +34,7 @@ export class ActionsController extends BaseHttpController {
     )
   }
 
-  @httpPost('/logout', TYPES.OptionalCrossServiceTokenMiddleware)
+  @httpPost('/logout', TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
   async logout(request: Request, response: Response): Promise<void> {
     await this.serviceProxy.callAuthServer(
       request,
@@ -54,7 +54,7 @@ export class ActionsController extends BaseHttpController {
     )
   }
 
-  @httpPost('/recovery/codes', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/recovery/codes', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async recoveryCodes(request: Request, response: Response): Promise<void> {
     await this.serviceProxy.callAuthServer(
       request,

+ 6 - 6
packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts

@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/authenticators')
 export class AuthenticatorsController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }
 
-  @httpDelete('/:authenticatorId', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpDelete('/:authenticatorId', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async delete(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -29,7 +29,7 @@ export class AuthenticatorsController extends BaseHttpController {
     )
   }
 
-  @httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async list(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -39,7 +39,7 @@ export class AuthenticatorsController extends BaseHttpController {
     )
   }
 
-  @httpGet('/generate-registration-options', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/generate-registration-options', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async generateRegistrationOptions(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -59,7 +59,7 @@ export class AuthenticatorsController extends BaseHttpController {
     )
   }
 
-  @httpPost('/verify-registration', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/verify-registration', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async verifyRegistration(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,

+ 3 - 3
packages/api-gateway/src/Controller/v1/FilesController.ts

@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/files')
 export class FilesController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }
 
-  @httpPost('/valet-tokens', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/valet-tokens', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async createToken(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,

+ 2 - 2
packages/api-gateway/src/Controller/v1/InvoicesController.ts

@@ -6,11 +6,11 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 
 @controller('/v1')
 export class InvoicesController extends BaseHttpController {
-  constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
+  constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
     super()
   }
 
-  @httpPost('/invoices/send-latest', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPost('/invoices/send-latest', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async sendLatestInvoice(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-invoice', request.body)
   }

+ 3 - 3
packages/api-gateway/src/Controller/v1/ItemsController.ts

@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
 import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
 
-@controller('/v1/items', TYPES.RequiredCrossServiceTokenMiddleware)
+@controller('/v1/items', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
 export class ItemsController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }

+ 3 - 3
packages/api-gateway/src/Controller/v1/MessagesController.ts

@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
 import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
 
-@controller('/v1/messages', TYPES.RequiredCrossServiceTokenMiddleware)
+@controller('/v1/messages', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
 export class MessagesController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }

+ 2 - 2
packages/api-gateway/src/Controller/v1/OfflineController.ts

@@ -9,8 +9,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/offline')
 export class OfflineController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }

+ 4 - 4
packages/api-gateway/src/Controller/v1/PaymentsController.ts

@@ -6,7 +6,7 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 
 @controller('/v1')
 export class PaymentsController extends BaseHttpController {
-  constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
+  constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
     super()
   }
 
@@ -40,12 +40,12 @@ export class PaymentsController extends BaseHttpController {
     await this.httpService.callPaymentsServer(request, response, 'api/extensions', request.body)
   }
 
-  @httpPost('/subscriptions/tiered', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPost('/subscriptions/tiered', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async createTieredSubscription(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/tiered', request.body)
   }
 
-  @httpPost('/subscriptions/apple_iap_confirm', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPost('/subscriptions/apple_iap_confirm', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async appleIAPConfirm(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/apple_iap_confirm', request.body)
   }
@@ -140,7 +140,7 @@ export class PaymentsController extends BaseHttpController {
     )
   }
 
-  @httpPost('/payments/stripe-setup-intent', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPost('/payments/stripe-setup-intent', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async createStripeSetupIntent(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/pro_users/stripe-setup-intent', request.body)
   }

+ 1 - 1
packages/api-gateway/src/Controller/v1/RevisionsController.ts

@@ -1,7 +1,7 @@
 import { BaseHttpController, controller, httpDelete, httpGet, results } from 'inversify-express-utils'
 import { TYPES } from '../../Bootstrap/Types'
 
-@controller('/v1/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
+@controller('/v1/items/:item_id/revisions', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
 export class RevisionsController extends BaseHttpController {
   @httpGet('/')
   async getRevisions(): Promise<results.JsonResult> {

+ 5 - 5
packages/api-gateway/src/Controller/v1/SessionsController.ts

@@ -8,13 +8,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/sessions')
 export class SessionsController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }
 
-  @httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async getSessions(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -23,7 +23,7 @@ export class SessionsController extends BaseHttpController {
     )
   }
 
-  @httpDelete('/:uuid', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpDelete('/:uuid', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async deleteSession(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -35,7 +35,7 @@ export class SessionsController extends BaseHttpController {
     )
   }
 
-  @httpDelete('/', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpDelete('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async deleteSessions(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,

+ 3 - 3
packages/api-gateway/src/Controller/v1/SharedVaultInvitesController.ts

@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
 import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
 
-@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
+@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
 export class SharedVaultInvitesController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }

+ 3 - 3
packages/api-gateway/src/Controller/v1/SharedVaultUsersController.ts

@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
 import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
 
-@controller('/v1/shared-vaults/:sharedVaultUuid/users', TYPES.RequiredCrossServiceTokenMiddleware)
+@controller('/v1/shared-vaults/:sharedVaultUuid/users', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
 export class SharedVaultUsersController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }

+ 3 - 3
packages/api-gateway/src/Controller/v1/SharedVaultsController.ts

@@ -5,11 +5,11 @@ import { TYPES } from '../../Bootstrap/Types'
 import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
 
-@controller('/v1/shared-vaults', TYPES.RequiredCrossServiceTokenMiddleware)
+@controller('/v1/shared-vaults', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
 export class SharedVaultsController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }

+ 6 - 6
packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts

@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/subscription-invites')
 export class SubscriptionInvitesController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }
 
-  @httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async inviteToSubscriptionSharing(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -25,7 +25,7 @@ export class SubscriptionInvitesController extends BaseHttpController {
     )
   }
 
-  @httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async listInvites(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -35,7 +35,7 @@ export class SubscriptionInvitesController extends BaseHttpController {
     )
   }
 
-  @httpDelete('/:inviteUuid', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpDelete('/:inviteUuid', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async cancelSubscriptionSharing(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -48,7 +48,7 @@ export class SubscriptionInvitesController extends BaseHttpController {
     )
   }
 
-  @httpPost('/:inviteUuid/accept', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/:inviteUuid/accept', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async acceptInvite(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,

+ 3 - 3
packages/api-gateway/src/Controller/v1/TokensController.ts

@@ -9,13 +9,13 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/subscription-tokens')
 export class TokensController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }
 
-  @httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async createToken(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,

+ 33 - 21
packages/api-gateway/src/Controller/v1/UsersController.ts

@@ -20,9 +20,10 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/users')
 export class UsersController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
-    @inject(TYPES.Logger) private logger: Logger,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_Logger) private logger: Logger,
+    @inject(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER) private isConfiguredForHomeServer: boolean,
   ) {
     super()
   }
@@ -32,12 +33,12 @@ export class UsersController extends BaseHttpController {
     await this.httpService.callPaymentsServer(request, response, 'api/pro_users/claim-account', request.body)
   }
 
-  @httpPost('/send-activation-code', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPost('/send-activation-code', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async sendActivationCode(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-activation-code', request.body)
   }
 
-  @httpPatch('/:userId', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPatch('/:userId', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async updateUser(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -47,7 +48,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpPut('/:userUuid/password', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPut('/:userUuid/password', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async changePassword(request: Request, response: Response): Promise<void> {
     this.logger.debug(
       '[DEPRECATED] use endpoint /v1/users/:userUuid/attributes/credentials instead of /v1/users/:userUuid/password',
@@ -65,7 +66,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpPut('/:userUuid/attributes/credentials', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPut('/:userUuid/attributes/credentials', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async changeCredentials(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -79,7 +80,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpGet('/:userId/params', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/:userId/params', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async getKeyParams(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -88,12 +89,12 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @all('/:userId/mfa', TYPES.RequiredCrossServiceTokenMiddleware)
+  @all('/:userId/mfa', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async blockMFA(): Promise<results.StatusCodeResult> {
     return this.statusCode(401)
   }
 
-  @httpPost('/:userUuid/integrations/listed', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/:userUuid/integrations/listed', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async createListedAccount(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -113,7 +114,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpGet('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/:userUuid/settings', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async listSettings(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -126,7 +127,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpPut('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPut('/:userUuid/settings', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async putSetting(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -140,7 +141,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpGet('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/:userUuid/settings/:settingName', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async getSetting(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -154,7 +155,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpDelete('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpDelete('/:userUuid/settings/:settingName', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async deleteSetting(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -169,7 +170,10 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpGet('/:userUuid/subscription-settings/:subscriptionSettingName', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet(
+    '/:userUuid/subscription-settings/:subscriptionSettingName',
+    TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware,
+  )
   async getSubscriptionSetting(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -183,7 +187,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpGet('/:userUuid/features', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/:userUuid/features', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async getFeatures(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -196,7 +200,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpGet('/:userUuid/subscription', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpGet('/:userUuid/subscription', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async getSubscription(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,
@@ -209,7 +213,7 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpGet('/subscription', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpGet('/subscription', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async getSubscriptionBySubscriptionToken(request: Request, response: Response): Promise<void> {
     if (response.locals.tokenAuthenticationMethod === TokenAuthenticationMethod.OfflineSubscriptionToken) {
       await this.httpService.callAuthServer(
@@ -232,12 +236,20 @@ export class UsersController extends BaseHttpController {
     )
   }
 
-  @httpDelete('/:userUuid', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpDelete('/:userUuid', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async deleteUser(request: Request, response: Response): Promise<void> {
-    await this.httpService.callPaymentsServer(request, response, 'api/account', request.body)
+    if (!this.isConfiguredForHomeServer) {
+      await this.httpService.callPaymentsServer(request, response, 'api/account', request.body, true)
+    }
+
+    await this.httpService.callAuthServer(
+      request,
+      response,
+      this.endpointResolver.resolveEndpointOrMethodIdentifier('DELETE', 'users/:userUuid', request.params.userUuid),
+    )
   }
 
-  @httpPost('/:userUuid/requests', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/:userUuid/requests', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async submitRequest(request: Request, response: Response): Promise<void> {
     await this.httpService.callAuthServer(
       request,

+ 5 - 5
packages/api-gateway/src/Controller/v1/WebSocketsController.ts

@@ -10,14 +10,14 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v1/sockets')
 export class WebSocketsController extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
-    @inject(TYPES.Logger) private logger: Logger,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_Logger) private logger: Logger,
   ) {
     super()
   }
 
-  @httpPost('/tokens', TYPES.RequiredCrossServiceTokenMiddleware)
+  @httpPost('/tokens', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
   async createWebSocketConnectionToken(request: Request, response: Response): Promise<void> {
     await this.httpService.callWebSocketServer(
       request,
@@ -27,7 +27,7 @@ export class WebSocketsController extends BaseHttpController {
     )
   }
 
-  @httpPost('/connections', TYPES.WebSocketAuthMiddleware)
+  @httpPost('/connections', TYPES.ApiGateway_WebSocketAuthMiddleware)
   async createWebSocketConnection(request: Request, response: Response): Promise<void> {
     if (!request.headers.connectionid) {
       this.logger.error('Could not create a websocket connection. Missing connection id header.')

+ 3 - 3
packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts

@@ -9,8 +9,8 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv
 @controller('/v2')
 export class ActionsControllerV2 extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private serviceProxy: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }
@@ -25,7 +25,7 @@ export class ActionsControllerV2 extends BaseHttpController {
     )
   }
 
-  @httpPost('/login-params', TYPES.OptionalCrossServiceTokenMiddleware)
+  @httpPost('/login-params', TYPES.ApiGateway_OptionalCrossServiceTokenMiddleware)
   async loginParams(request: Request, response: Response): Promise<void> {
     await this.serviceProxy.callAuthServer(
       request,

+ 8 - 8
packages/api-gateway/src/Controller/v2/PaymentsControllerV2.ts

@@ -6,7 +6,7 @@ import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 
 @controller('/v2')
 export class PaymentsControllerV2 extends BaseHttpController {
-  constructor(@inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface) {
+  constructor(@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface) {
     super()
   }
 
@@ -15,22 +15,22 @@ export class PaymentsControllerV2 extends BaseHttpController {
     await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
   }
 
-  @httpGet('/subscriptions/tailored', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpGet('/subscriptions/tailored', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async getTailoredSubscriptionsWithFeatures(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/features', request.body)
   }
 
-  @httpGet('/subscriptions/deltas', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpGet('/subscriptions/deltas', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async getSubscriptionDeltasForChangingPlan(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas', request.body)
   }
 
-  @httpPost('/subscriptions/deltas/apply', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPost('/subscriptions/deltas/apply', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async applySubscriptionDelta(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(request, response, 'api/subscriptions/deltas/apply', request.body)
   }
 
-  @httpPost('/subscriptions/change-payment-method', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPost('/subscriptions/change-payment-method', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async changePaymentMethod(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(
       request,
@@ -40,7 +40,7 @@ export class PaymentsControllerV2 extends BaseHttpController {
     )
   }
 
-  @httpGet('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpGet('/subscriptions/:subscriptionId', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async getSubscription(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(
       request,
@@ -50,7 +50,7 @@ export class PaymentsControllerV2 extends BaseHttpController {
     )
   }
 
-  @httpDelete('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpDelete('/subscriptions/:subscriptionId', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async cancelSubscription(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(
       request,
@@ -60,7 +60,7 @@ export class PaymentsControllerV2 extends BaseHttpController {
     )
   }
 
-  @httpPatch('/subscriptions/:subscriptionId', TYPES.SubscriptionTokenAuthMiddleware)
+  @httpPatch('/subscriptions/:subscriptionId', TYPES.ApiGateway_SubscriptionTokenAuthMiddleware)
   async updateSubscription(request: Request, response: Response): Promise<void> {
     await this.httpService.callPaymentsServer(
       request,

+ 3 - 3
packages/api-gateway/src/Controller/v2/RevisionsControllerV2.ts

@@ -6,11 +6,11 @@ import { TYPES } from '../../Bootstrap/Types'
 import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
 import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface'
 
-@controller('/v2/items/:itemUuid/revisions', TYPES.RequiredCrossServiceTokenMiddleware)
+@controller('/v2/items/:itemUuid/revisions', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
 export class RevisionsControllerV2 extends BaseHttpController {
   constructor(
-    @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface,
-    @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface,
+    @inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
+    @inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
   ) {
     super()
   }

+ 1 - 1
packages/api-gateway/src/Infra/Redis/RedisCrossServiceTokenCache.ts

@@ -9,7 +9,7 @@ export class RedisCrossServiceTokenCache implements CrossServiceTokenCacheInterf
   private readonly PREFIX = 'cst'
   private readonly USER_CST_PREFIX = 'user-cst'
 
-  constructor(@inject(TYPES.Redis) private redisClient: IORedis.Redis) {}
+  constructor(@inject(TYPES.ApiGateway_Redis) private redisClient: IORedis.Redis) {}
 
   async set(dto: {
     authorizationHeaderValue: string

+ 41 - 18
packages/api-gateway/src/Service/Http/HttpServiceProxy.ts

@@ -11,17 +11,17 @@ import { ServiceProxyInterface } from './ServiceProxyInterface'
 @injectable()
 export class HttpServiceProxy implements ServiceProxyInterface {
   constructor(
-    @inject(TYPES.HTTPClient) private httpClient: AxiosInstance,
-    @inject(TYPES.AUTH_SERVER_URL) private authServerUrl: string,
-    @inject(TYPES.SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
-    @inject(TYPES.PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
-    @inject(TYPES.FILES_SERVER_URL) private filesServerUrl: string,
-    @inject(TYPES.WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
-    @inject(TYPES.REVISIONS_SERVER_URL) private revisionsServerUrl: string,
-    @inject(TYPES.EMAIL_SERVER_URL) private emailServerUrl: string,
-    @inject(TYPES.HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
-    @inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
-    @inject(TYPES.Logger) private logger: Logger,
+    @inject(TYPES.ApiGateway_HTTPClient) private httpClient: AxiosInstance,
+    @inject(TYPES.ApiGateway_AUTH_SERVER_URL) private authServerUrl: string,
+    @inject(TYPES.ApiGateway_SYNCING_SERVER_JS_URL) private syncingServerJsUrl: string,
+    @inject(TYPES.ApiGateway_PAYMENTS_SERVER_URL) private paymentsServerUrl: string,
+    @inject(TYPES.ApiGateway_FILES_SERVER_URL) private filesServerUrl: string,
+    @inject(TYPES.ApiGateway_WEB_SOCKET_SERVER_URL) private webSocketServerUrl: string,
+    @inject(TYPES.ApiGateway_REVISIONS_SERVER_URL) private revisionsServerUrl: string,
+    @inject(TYPES.ApiGateway_EMAIL_SERVER_URL) private emailServerUrl: string,
+    @inject(TYPES.ApiGateway_HTTP_CALL_TIMEOUT) private httpCallTimeout: number,
+    @inject(TYPES.ApiGateway_CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface,
+    @inject(TYPES.ApiGateway_Logger) private logger: Logger,
   ) {}
 
   async validateSession(
@@ -130,19 +130,29 @@ export class HttpServiceProxy implements ServiceProxyInterface {
     response: Response,
     endpointOrMethodIdentifier: string,
     payload?: Record<string, unknown> | string,
-  ): Promise<void> {
+    returnRawResponse?: boolean,
+  ): Promise<void | Response<unknown, Record<string, unknown>>> {
     if (!this.paymentsServerUrl) {
       this.logger.debug('Payments Server URL not defined. Skipped request to Payments API.')
 
       return
     }
-    await this.callServerWithLegacyFormat(
+
+    const requestDuplicate = Object.assign({}, request)
+    const responseDuplicate = Object.assign({}, response)
+
+    const rawResponse = await this.callServerWithLegacyFormat(
       this.paymentsServerUrl,
-      request,
-      response,
+      returnRawResponse ? requestDuplicate : request,
+      returnRawResponse ? responseDuplicate : response,
       endpointOrMethodIdentifier,
       payload,
+      returnRawResponse,
     )
+
+    if (returnRawResponse) {
+      return rawResponse
+    }
   }
 
   async callAuthServerWithLegacyFormat(
@@ -279,7 +289,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
     response: Response,
     endpointOrMethodIdentifier: string,
     payload?: Record<string, unknown> | string,
-  ): Promise<void> {
+    returnRawResponse?: boolean,
+  ): Promise<void | Response<unknown, Record<string, unknown>>> {
     const serviceResponse = await this.getServerResponse(
       serverUrl,
       request,
@@ -295,9 +306,21 @@ export class HttpServiceProxy implements ServiceProxyInterface {
     this.applyResponseHeaders(serviceResponse, response)
 
     if (serviceResponse.request._redirectable._redirectCount > 0) {
-      response.status(302).redirect(serviceResponse.request.res.responseUrl)
+      response.status(302)
+
+      if (returnRawResponse) {
+        return response
+      }
+
+      response.redirect(serviceResponse.request.res.responseUrl)
     } else {
-      response.status(serviceResponse.status).send(serviceResponse.data)
+      response.status(serviceResponse.status)
+
+      if (returnRawResponse) {
+        return response
+      }
+
+      response.send(serviceResponse.data)
     }
   }
 

+ 2 - 1
packages/api-gateway/src/Service/Http/ServiceProxyInterface.ts

@@ -42,7 +42,8 @@ export interface ServiceProxyInterface {
     response: Response,
     endpointOrMethodIdentifier: string,
     payload?: Record<string, unknown> | string,
-  ): Promise<void>
+    returnRawResponse?: boolean,
+  ): Promise<void | Response<unknown, Record<string, unknown>>>
   callWebSocketServer(
     request: Request,
     response: Response,

+ 1 - 0
packages/api-gateway/src/Service/Resolver/EndpointResolver.ts

@@ -43,6 +43,7 @@ export class EndpointResolver implements EndpointResolverInterface {
     ['[PATCH]:users/:userId', 'auth.users.update'],
     ['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'],
     ['[PUT]:auth/params', 'auth.users.getKeyParams'],
+    ['[DELETE]:users/:userUuid', 'auth.users.delete'],
     ['[POST]:listed', 'auth.users.createListedAccount'],
     ['[POST]:auth', 'auth.users.register'],
     ['[GET]:users/:userUuid/settings', 'auth.users.getSettings'],

+ 17 - 3
packages/auth/src/Domain/Auth/AuthenticationMethodResolver.spec.ts

@@ -30,7 +30,9 @@ describe('AuthenticationMethodResolver', () => {
 
     user = {} as jest.Mocked<User>
 
-    session = {} as jest.Mocked<Session>
+    session = {
+      userUuid: '00000000-0000-0000-0000-000000000000',
+    } as jest.Mocked<Session>
 
     revokedSession = {} as jest.Mocked<RevokedSession>
 
@@ -50,17 +52,23 @@ describe('AuthenticationMethodResolver', () => {
   })
 
   it('should resolve jwt authentication method', async () => {
-    sessionTokenDecoder.decodeToken = jest.fn().mockReturnValue({ user_uuid: '123' })
+    sessionTokenDecoder.decodeToken = jest.fn().mockReturnValue({ user_uuid: '00000000-0000-0000-0000-000000000000' })
 
     expect(await createResolver().resolve('test')).toEqual({
       claims: {
-        user_uuid: '123',
+        user_uuid: '00000000-0000-0000-0000-000000000000',
       },
       type: 'jwt',
       user,
     })
   })
 
+  it('should not resolve jwt authentication method with invalid user uuid', async () => {
+    sessionTokenDecoder.decodeToken = jest.fn().mockReturnValue({ user_uuid: 'invalid' })
+
+    expect(await createResolver().resolve('test')).toBeUndefined
+  })
+
   it('should resolve session authentication method', async () => {
     sessionService.getSessionFromToken = jest.fn().mockReturnValue(session)
 
@@ -71,6 +79,12 @@ describe('AuthenticationMethodResolver', () => {
     })
   })
 
+  it('should not resolve session authentication method with invalid user uuid on session', async () => {
+    sessionService.getSessionFromToken = jest.fn().mockReturnValue({ userUuid: 'invalid' })
+
+    expect(await createResolver().resolve('test')).toBeUndefined
+  })
+
   it('should resolve archvied session authentication method', async () => {
     sessionService.getRevokedSessionFromToken = jest.fn().mockReturnValue(revokedSession)
 

+ 15 - 2
packages/auth/src/Domain/Auth/AuthenticationMethodResolver.ts

@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
 import { AuthenticationMethod } from './AuthenticationMethod'
 import { AuthenticationMethodResolverInterface } from './AuthenticationMethodResolverInterface'
 import { Logger } from 'winston'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class AuthenticationMethodResolver implements AuthenticationMethodResolverInterface {
@@ -29,9 +30,15 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
     if (decodedToken) {
       this.logger.debug('Token decoded successfully. User found.')
 
+      const userUuidOrError = Uuid.create(decodedToken.user_uuid as string)
+      if (userUuidOrError.isFailed()) {
+        return undefined
+      }
+      const userUuid = userUuidOrError.getValue()
+
       return {
         type: 'jwt',
-        user: await this.userRepository.findOneByUuid(<string>decodedToken.user_uuid),
+        user: await this.userRepository.findOneByUuid(userUuid),
         claims: decodedToken,
       }
     }
@@ -40,9 +47,15 @@ export class AuthenticationMethodResolver implements AuthenticationMethodResolve
     if (session) {
       this.logger.debug('Token decoded successfully. Session found.')
 
+      const userUuidOrError = Uuid.create(session.userUuid)
+      if (userUuidOrError.isFailed()) {
+        return undefined
+      }
+      const userUuid = userUuidOrError.getValue()
+
       return {
         type: 'session_token',
-        user: await this.userRepository.findOneByUuid(session.userUuid),
+        user: await this.userRepository.findOneByUuid(userUuid),
         session: session,
       }
     }

+ 11 - 3
packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.spec.ts

@@ -50,7 +50,7 @@ describe('AccountDeletionRequestedEventHandler', () => {
 
     ephemeralSession = {
       uuid: '2-3-4',
-      userUuid: '1-2-3',
+      userUuid: '00000000-0000-0000-0000-000000000000',
     } as jest.Mocked<EphemeralSession>
 
     ephemeralSessionRepository = {} as jest.Mocked<EphemeralSessionRepositoryInterface>
@@ -68,7 +68,7 @@ describe('AccountDeletionRequestedEventHandler', () => {
     event = {} as jest.Mocked<AccountDeletionRequestedEvent>
     event.createdAt = new Date(1)
     event.payload = {
-      userUuid: '1-2-3',
+      userUuid: '00000000-0000-0000-0000-000000000000',
       userCreatedAtTimestamp: 1,
       regularSubscriptionUuid: '2-3-4',
     }
@@ -84,6 +84,14 @@ describe('AccountDeletionRequestedEventHandler', () => {
     expect(userRepository.remove).toHaveBeenCalledWith(user)
   })
 
+  it('should not remove a user with invalid uuid', async () => {
+    event.payload.userUuid = 'invalid'
+
+    await createHandler().handle(event)
+
+    expect(userRepository.remove).not.toHaveBeenCalled()
+  })
+
   it('should not remove a user if one does not exist', async () => {
     userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
 
@@ -100,6 +108,6 @@ describe('AccountDeletionRequestedEventHandler', () => {
 
     expect(sessionRepository.remove).toHaveBeenCalledWith(session)
     expect(revokedSessionRepository.remove).toHaveBeenCalledWith(revokedSession)
-    expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '1-2-3')
+    expect(ephemeralSessionRepository.deleteOne).toHaveBeenCalledWith('2-3-4', '00000000-0000-0000-0000-000000000000')
   })
 })

+ 13 - 4
packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.ts

@@ -6,6 +6,7 @@ import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSession
 import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface'
 import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface'
 import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
@@ -19,19 +20,27 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
   ) {}
 
   async handle(event: AccountDeletionRequestedEvent): Promise<void> {
-    const user = await this.userRepository.findOneByUuid(event.payload.userUuid)
+    const userUuidOrError = Uuid.create(event.payload.userUuid)
+    if (userUuidOrError.isFailed()) {
+      this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
+
+      return
+    }
+    const userUuid = userUuidOrError.getValue()
+
+    const user = await this.userRepository.findOneByUuid(userUuid)
 
     if (user === null) {
-      this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
+      this.logger.warn(`Could not find user with uuid: ${userUuid.value}`)
 
       return
     }
 
-    await this.removeSessions(event.payload.userUuid)
+    await this.removeSessions(userUuid.value)
 
     await this.userRepository.remove(user)
 
-    this.logger.info(`Finished account cleanup for user: ${event.payload.userUuid}`)
+    this.logger.info(`Finished account cleanup for user: ${userUuid.value}`)
   }
 
   private async removeSessions(userUuid: string): Promise<void> {

+ 14 - 6
packages/auth/src/Domain/Handler/FileUploadedEventHandler.spec.ts

@@ -33,7 +33,7 @@ describe('FileUploadedEventHandler', () => {
     userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
 
     regularSubscription = {
-      uuid: '1-2-3',
+      uuid: '00000000-0000-0000-0000-000000000000',
       subscriptionType: UserSubscriptionType.Regular,
       user: Promise.resolve(user),
     } as jest.Mocked<UserSubscription>
@@ -56,9 +56,9 @@ describe('FileUploadedEventHandler', () => {
     event = {} as jest.Mocked<FileUploadedEvent>
     event.createdAt = new Date(1)
     event.payload = {
-      userUuid: '1-2-3',
+      userUuid: '00000000-0000-0000-0000-000000000000',
       fileByteSize: 123,
-      filePath: '1-2-3/2-3-4',
+      filePath: '00000000-0000-0000-0000-000000000000/2-3-4',
       fileName: '2-3-4',
     }
 
@@ -78,13 +78,21 @@ describe('FileUploadedEventHandler', () => {
       },
       user,
       userSubscription: {
-        uuid: '1-2-3',
+        uuid: '00000000-0000-0000-0000-000000000000',
         subscriptionType: 'regular',
         user: Promise.resolve(user),
       },
     })
   })
 
+  it('should not do anything if a user uuid is invalid', async () => {
+    event.payload.userUuid = 'invalid'
+
+    await createHandler().handle(event)
+
+    expect(subscriptionSettingService.createOrReplace).not.toHaveBeenCalled()
+  })
+
   it('should not do anything if a user is not found', async () => {
     userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
 
@@ -121,7 +129,7 @@ describe('FileUploadedEventHandler', () => {
       },
       user,
       userSubscription: {
-        uuid: '1-2-3',
+        uuid: '00000000-0000-0000-0000-000000000000',
         subscriptionType: 'regular',
         user: Promise.resolve(user),
       },
@@ -147,7 +155,7 @@ describe('FileUploadedEventHandler', () => {
       },
       user,
       userSubscription: {
-        uuid: '1-2-3',
+        uuid: '00000000-0000-0000-0000-000000000000',
         subscriptionType: 'regular',
         user: Promise.resolve(user),
       },

+ 13 - 4
packages/auth/src/Domain/Handler/FileUploadedEventHandler.ts

@@ -10,6 +10,7 @@ import { UserSubscription } from '../Subscription/UserSubscription'
 import { UserSubscriptionServiceInterface } from '../Subscription/UserSubscriptionServiceInterface'
 import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
 import { User } from '../User/User'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class FileUploadedEventHandler implements DomainEventHandlerInterface {
@@ -22,17 +23,25 @@ export class FileUploadedEventHandler implements DomainEventHandlerInterface {
   ) {}
 
   async handle(event: FileUploadedEvent): Promise<void> {
-    const user = await this.userRepository.findOneByUuid(event.payload.userUuid)
+    const userUuidOrError = Uuid.create(event.payload.userUuid)
+    if (userUuidOrError.isFailed()) {
+      this.logger.warn(userUuidOrError.getError())
+
+      return
+    }
+    const userUuid = userUuidOrError.getValue()
+
+    const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {
-      this.logger.warn(`Could not find user with uuid: ${event.payload.userUuid}`)
+      this.logger.warn(`Could not find user with uuid: ${userUuid.value}`)
 
       return
     }
 
     const { regularSubscription, sharedSubscription } =
-      await this.userSubscriptionService.findRegularSubscriptionForUserUuid(event.payload.userUuid)
+      await this.userSubscriptionService.findRegularSubscriptionForUserUuid(userUuid.value)
     if (regularSubscription === null) {
-      this.logger.warn(`Could not find regular user subscription for user with uuid: ${event.payload.userUuid}`)
+      this.logger.warn(`Could not find regular user subscription for user with uuid: ${userUuid.value}`)
 
       return
     }

+ 18 - 3
packages/auth/src/Domain/Role/RoleService.spec.ts

@@ -214,13 +214,25 @@ describe('RoleService', () => {
     })
 
     it('should indicate if a user has given permission', async () => {
-      const userHasPermission = await createService().userHasPermission('1-2-3', PermissionName.DailyEmailBackup)
+      const userHasPermission = await createService().userHasPermission(
+        '00000000-0000-0000-0000-000000000000',
+        PermissionName.DailyEmailBackup,
+      )
 
       expect(userHasPermission).toBeTruthy()
     })
 
+    it('should not indiciate if a user has permission if the user uuid is invalid', async () => {
+      const userHasPermission = await createService().userHasPermission('invalid', PermissionName.DailyEmailBackup)
+
+      expect(userHasPermission).toBeFalsy()
+    })
+
     it('should indicate if a user does not have a given permission', async () => {
-      const userHasPermission = await createService().userHasPermission('1-2-3', PermissionName.MarkdownProEditor)
+      const userHasPermission = await createService().userHasPermission(
+        '00000000-0000-0000-0000-000000000000',
+        PermissionName.MarkdownProEditor,
+      )
 
       expect(userHasPermission).toBeFalsy()
     })
@@ -228,7 +240,10 @@ describe('RoleService', () => {
     it('should indicate user does not have a permission if user could not be found', async () => {
       userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
 
-      const userHasPermission = await createService().userHasPermission('1-2-3', PermissionName.MarkdownProEditor)
+      const userHasPermission = await createService().userHasPermission(
+        '00000000-0000-0000-0000-000000000000',
+        PermissionName.MarkdownProEditor,
+      )
 
       expect(userHasPermission).toBeFalsy()
     })

+ 9 - 2
packages/auth/src/Domain/Role/RoleService.ts

@@ -13,6 +13,7 @@ import { RoleToSubscriptionMapInterface } from './RoleToSubscriptionMapInterface
 import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
 import { Role } from './Role'
 import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class RoleService implements RoleServiceInterface {
@@ -26,10 +27,16 @@ export class RoleService implements RoleServiceInterface {
     @inject(TYPES.Auth_Logger) private logger: Logger,
   ) {}
 
-  async userHasPermission(userUuid: string, permissionName: PermissionName): Promise<boolean> {
+  async userHasPermission(userUuidString: string, permissionName: PermissionName): Promise<boolean> {
+    const userUuidOrError = Uuid.create(userUuidString)
+    if (userUuidOrError.isFailed()) {
+      return false
+    }
+    const userUuid = userUuidOrError.getValue()
+
     const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {
-      this.logger.warn(`Could not find user with uuid ${userUuid} for permissions check`)
+      this.logger.warn(`Could not find user with uuid ${userUuid.value} for permissions check`)
 
       return false
     }

+ 22 - 4
packages/auth/src/Domain/Setting/SettingDecrypter.spec.ts

@@ -32,7 +32,9 @@ describe('SettingDecrypter', () => {
       serverEncryptionVersion: EncryptionVersion.Default,
     } as jest.Mocked<Setting>
 
-    expect(await createDecrypter().decryptSettingValue(setting, '1-2-3')).toEqual('decrypted')
+    expect(await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')).toEqual(
+      'decrypted',
+    )
   })
 
   it('should return null if the setting value is null', async () => {
@@ -41,7 +43,7 @@ describe('SettingDecrypter', () => {
       serverEncryptionVersion: EncryptionVersion.Default,
     } as jest.Mocked<Setting>
 
-    expect(await createDecrypter().decryptSettingValue(setting, '1-2-3')).toBeNull()
+    expect(await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')).toBeNull()
   })
 
   it('should return unencrypted value if the setting value is unencrypted', async () => {
@@ -50,7 +52,7 @@ describe('SettingDecrypter', () => {
       serverEncryptionVersion: EncryptionVersion.Unencrypted,
     } as jest.Mocked<Setting>
 
-    expect(await createDecrypter().decryptSettingValue(setting, '1-2-3')).toEqual('test')
+    expect(await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')).toEqual('test')
   })
 
   it('should throw if the user could not be found', async () => {
@@ -62,7 +64,23 @@ describe('SettingDecrypter', () => {
 
     let caughtError = null
     try {
-      await createDecrypter().decryptSettingValue(setting, '1-2-3')
+      await createDecrypter().decryptSettingValue(setting, '00000000-0000-0000-0000-000000000000')
+    } catch (error) {
+      caughtError = error
+    }
+
+    expect(caughtError).not.toBeNull()
+  })
+
+  it('should throw if the user uuid is invalid', async () => {
+    const setting = {
+      value: 'encrypted',
+      serverEncryptionVersion: EncryptionVersion.Default,
+    } as jest.Mocked<Setting>
+
+    let caughtError = null
+    try {
+      await createDecrypter().decryptSettingValue(setting, 'invalid')
     } catch (error) {
       caughtError = error
     }

+ 9 - 2
packages/auth/src/Domain/Setting/SettingDecrypter.ts

@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
 import { Setting } from './Setting'
 import { SettingDecrypterInterface } from './SettingDecrypterInterface'
 import { SubscriptionSetting } from './SubscriptionSetting'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class SettingDecrypter implements SettingDecrypterInterface {
@@ -14,12 +15,18 @@ export class SettingDecrypter implements SettingDecrypterInterface {
     @inject(TYPES.Auth_Crypter) private crypter: CrypterInterface,
   ) {}
 
-  async decryptSettingValue(setting: Setting | SubscriptionSetting, userUuid: string): Promise<string | null> {
+  async decryptSettingValue(setting: Setting | SubscriptionSetting, userUuidString: string): Promise<string | null> {
     if (setting.value !== null && setting.serverEncryptionVersion === EncryptionVersion.Default) {
+      const userUuidOrError = Uuid.create(userUuidString)
+      if (userUuidOrError.isFailed()) {
+        throw new Error(userUuidOrError.getError())
+      }
+      const userUuid = userUuidOrError.getValue()
+
       const user = await this.userRepository.findOneByUuid(userUuid)
 
       if (user === null) {
-        throw new Error(`Could not find user with uuid: ${userUuid}`)
+        throw new Error(`Could not find user with uuid: ${userUuid.value}`)
       }
 
       return this.crypter.decryptForUser(setting.value, user)

+ 1 - 3
packages/auth/src/Domain/UseCase/AuthenticateSubscriptionToken/AuthenticateSubscriptionToken.spec.ts

@@ -17,7 +17,7 @@ describe('AuthenticateSubscriptionToken', () => {
 
   beforeEach(() => {
     subscriptionTokenRepository = {} as jest.Mocked<SubscriptionTokenRepositoryInterface>
-    subscriptionTokenRepository.getUserUuidByToken = jest.fn().mockReturnValue('1-2-3')
+    subscriptionTokenRepository.getUserUuidByToken = jest.fn().mockReturnValue('00000000-0000-0000-0000-000000000000')
 
     user = {
       roles: Promise.resolve([{ name: RoleName.NAMES.CoreUser }]),
@@ -30,8 +30,6 @@ describe('AuthenticateSubscriptionToken', () => {
   it('should authenticate an subscription token', async () => {
     const response = await createUseCase().execute({ token: 'test' })
 
-    expect(userRepository.findOneByUuid).toHaveBeenCalledWith('1-2-3')
-
     expect(response.success).toBeTruthy()
 
     expect(response.user).toEqual(user)

+ 5 - 2
packages/auth/src/Domain/UseCase/AuthenticateSubscriptionToken/AuthenticateSubscriptionToken.ts

@@ -6,6 +6,7 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
 import { UseCaseInterface } from '../UseCaseInterface'
 import { AuthenticateSubscriptionTokenDTO } from './AuthenticateSubscriptionTokenDTO'
 import { AuthenticateSubscriptionTokenResponse } from './AuthenticateSubscriptionTokenResponse'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class AuthenticateSubscriptionToken implements UseCaseInterface {
@@ -16,12 +17,14 @@ export class AuthenticateSubscriptionToken implements UseCaseInterface {
   ) {}
 
   async execute(dto: AuthenticateSubscriptionTokenDTO): Promise<AuthenticateSubscriptionTokenResponse> {
-    const userUuid = await this.subscriptionTokenRepository.getUserUuidByToken(dto.token)
-    if (userUuid === undefined) {
+    const userUuidString = await this.subscriptionTokenRepository.getUserUuidByToken(dto.token)
+    const userUuidOrError = Uuid.create(userUuidString as string)
+    if (userUuidOrError.isFailed()) {
       return {
         success: false,
       }
     }
+    const userUuid = userUuidOrError.getValue()
 
     const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {

+ 22 - 7
packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts

@@ -28,13 +28,15 @@ describe('CreateCrossServiceToken', () => {
     session = {} as jest.Mocked<Session>
 
     user = {
-      uuid: '1-2-3',
+      uuid: '00000000-0000-0000-0000-000000000000',
       email: 'test@test.te',
     } as jest.Mocked<User>
     user.roles = Promise.resolve([role])
 
     userProjector = {} as jest.Mocked<ProjectorInterface<User>>
-    userProjector.projectSimple = jest.fn().mockReturnValue({ uuid: '1-2-3', email: 'test@test.te' })
+    userProjector.projectSimple = jest
+      .fn()
+      .mockReturnValue({ uuid: '00000000-0000-0000-0000-000000000000', email: 'test@test.te' })
 
     roleProjector = {} as jest.Mocked<ProjectorInterface<Role>>
     roleProjector.projectSimple = jest.fn().mockReturnValue({ name: 'role1', uuid: '1-3-4' })
@@ -69,7 +71,7 @@ describe('CreateCrossServiceToken', () => {
         },
         user: {
           email: 'test@test.te',
-          uuid: '1-2-3',
+          uuid: '00000000-0000-0000-0000-000000000000',
         },
       },
       60,
@@ -91,7 +93,7 @@ describe('CreateCrossServiceToken', () => {
         ],
         user: {
           email: 'test@test.te',
-          uuid: '1-2-3',
+          uuid: '00000000-0000-0000-0000-000000000000',
         },
       },
       60,
@@ -100,7 +102,7 @@ describe('CreateCrossServiceToken', () => {
 
   it('should create a cross service token for user by user uuid', async () => {
     await createUseCase().execute({
-      userUuid: '1-2-3',
+      userUuid: '00000000-0000-0000-0000-000000000000',
     })
 
     expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
@@ -113,7 +115,7 @@ describe('CreateCrossServiceToken', () => {
         ],
         user: {
           email: 'test@test.te',
-          uuid: '1-2-3',
+          uuid: '00000000-0000-0000-0000-000000000000',
         },
       },
       60,
@@ -126,7 +128,20 @@ describe('CreateCrossServiceToken', () => {
     let caughtError = null
     try {
       await createUseCase().execute({
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
+      })
+    } catch (error) {
+      caughtError = error
+    }
+
+    expect(caughtError).not.toBeNull()
+  })
+
+  it('should throw an error if user uuid is invalid', async () => {
+    let caughtError = null
+    try {
+      await createUseCase().execute({
+        userUuid: 'invalid',
       })
     } catch (error) {
       caughtError = error

+ 8 - 1
packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts

@@ -11,6 +11,7 @@ import { UseCaseInterface } from '../UseCaseInterface'
 
 import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
 import { CreateCrossServiceTokenResponse } from './CreateCrossServiceTokenResponse'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class CreateCrossServiceToken implements UseCaseInterface {
@@ -26,7 +27,13 @@ export class CreateCrossServiceToken implements UseCaseInterface {
   async execute(dto: CreateCrossServiceTokenDTO): Promise<CreateCrossServiceTokenResponse> {
     let user: User | undefined | null = dto.user
     if (user === undefined && dto.userUuid !== undefined) {
-      user = await this.userRepository.findOneByUuid(dto.userUuid)
+      const userUuidOrError = Uuid.create(dto.userUuid)
+      if (userUuidOrError.isFailed()) {
+        throw new Error(userUuidOrError.getError())
+      }
+      const userUuid = userUuidOrError.getValue()
+
+      user = await this.userRepository.findOneByUuid(userUuid)
     }
 
     if (!user) {

+ 8 - 8
packages/auth/src/Domain/UseCase/DeleteAccount/DeleteAccount.spec.ts

@@ -34,7 +34,7 @@ describe('DeleteAccount', () => {
     } as jest.Mocked<UserSubscription>
 
     userRepository = {} as jest.Mocked<UserRepositoryInterface>
-    userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(user)
+    userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
 
     userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
     userSubscriptionService.findRegularSubscriptionForUserUuid = jest
@@ -58,7 +58,7 @@ describe('DeleteAccount', () => {
       .fn()
       .mockReturnValue({ regularSubscription: null, sharedSubscription: null })
 
-    expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
+    expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
       message: 'Successfully deleted user',
       responseCode: 200,
       success: true,
@@ -77,7 +77,7 @@ describe('DeleteAccount', () => {
       .fn()
       .mockReturnValue({ regularSubscription, sharedSubscription: null })
 
-    expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
+    expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
       message: 'Successfully deleted user',
       responseCode: 200,
       success: true,
@@ -92,9 +92,9 @@ describe('DeleteAccount', () => {
   })
 
   it('should not trigger account deletion if user is not found', async () => {
-    userRepository.findOneByUsernameOrEmail = jest.fn().mockReturnValue(null)
+    userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
 
-    expect(await createUseCase().execute({ email: 'test@test.te' })).toEqual({
+    expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
       message: 'User not found',
       responseCode: 404,
       success: false,
@@ -104,9 +104,9 @@ describe('DeleteAccount', () => {
     expect(domainEventFactory.createAccountDeletionRequestedEvent).not.toHaveBeenCalled()
   })
 
-  it('should not trigger account deletion if username is invalid', async () => {
-    expect(await createUseCase().execute({ email: '' })).toEqual({
-      message: 'Username cannot be empty',
+  it('should not trigger account deletion if user uuid is invalid', async () => {
+    expect(await createUseCase().execute({ userUuid: '' })).toEqual({
+      message: 'Given value is not a valid uuid: ',
       responseCode: 400,
       success: false,
     })

+ 6 - 6
packages/auth/src/Domain/UseCase/DeleteAccount/DeleteAccount.ts

@@ -1,4 +1,4 @@
-import { Username } from '@standardnotes/domain-core'
+import { Uuid } from '@standardnotes/domain-core'
 import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
 import { TimerInterface } from '@standardnotes/time'
 import { inject, injectable } from 'inversify'
@@ -23,17 +23,17 @@ export class DeleteAccount implements UseCaseInterface {
   ) {}
 
   async execute(dto: DeleteAccountDTO): Promise<DeleteAccountResponse> {
-    const usernameOrError = Username.create(dto.email)
-    if (usernameOrError.isFailed()) {
+    const uuidOrError = Uuid.create(dto.userUuid)
+    if (uuidOrError.isFailed()) {
       return {
         success: false,
         responseCode: 400,
-        message: usernameOrError.getError(),
+        message: uuidOrError.getError(),
       }
     }
-    const username = usernameOrError.getValue()
+    const uuid = uuidOrError.getValue()
 
-    const user = await this.userRepository.findOneByUsernameOrEmail(username)
+    const user = await this.userRepository.findOneByUuid(uuid)
 
     if (user === null) {
       return {

+ 1 - 1
packages/auth/src/Domain/UseCase/DeleteAccount/DeleteAccountDTO.ts

@@ -1,3 +1,3 @@
 export type DeleteAccountDTO = {
-  email: string
+  userUuid: string
 }

+ 1 - 1
packages/auth/src/Domain/UseCase/DeleteAuthenticator/DeleteAuthenticator.ts

@@ -20,7 +20,7 @@ export class DeleteAuthenticator implements UseCaseInterface<string> {
     }
     const userUuid = userUuidOrError.getValue()
 
-    const user = await this.userRepository.findOneByUuid(userUuid.value)
+    const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {
       return Result.fail('Could not delete authenticator: user not found.')
     }

+ 1 - 1
packages/auth/src/Domain/UseCase/GenerateAuthenticatorRegistrationOptions/GenerateAuthenticatorRegistrationOptions.ts

@@ -37,7 +37,7 @@ export class GenerateAuthenticatorRegistrationOptions
     }
     const username = usernameOrError.getValue()
 
-    const user = await this.userRepository.findOneByUuid(userUuid.value)
+    const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {
       return Result.fail('Could not generate authenticator registration options: user not found.')
     }

+ 1 - 1
packages/auth/src/Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes.ts

@@ -19,7 +19,7 @@ export class GenerateRecoveryCodes implements UseCaseInterface<string> {
     }
     const userUuid = userUuidOrError.getValue()
 
-    const user = await this.userRepository.findOneByUuid(userUuid.value)
+    const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {
       return Result.fail('Could not generate recovery codes: user not found')
     }

+ 42 - 18
packages/auth/src/Domain/UseCase/GetSettings/GetSettings.spec.ts

@@ -45,6 +45,10 @@ describe('GetSettings', () => {
     )
 
   beforeEach(() => {
+    user = {
+      uuid: '00000000-0000-0000-0000-000000000000',
+    } as jest.Mocked<User>
+
     setting = new Setting()
     setting.name = 'test'
     setting.updatedAt = 345
@@ -90,8 +94,6 @@ describe('GetSettings', () => {
     subscriptionSettingProjector = {} as jest.Mocked<SubscriptionSettingProjector>
     subscriptionSettingProjector.projectSimple = jest.fn().mockReturnValue({ foo: 'sub-bar' })
 
-    user = {} as jest.Mocked<User>
-
     userRepository = {} as jest.Mocked<UserRepositoryInterface>
     userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
 
@@ -103,18 +105,27 @@ describe('GetSettings', () => {
     it('should fail if a user is not found', async () => {
       userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
 
-      expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
+      expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
+        success: false,
+        error: {
+          message: 'User 00000000-0000-0000-0000-000000000000 not found.',
+        },
+      })
+    })
+
+    it('should fail if a user uuid is invalid', async () => {
+      expect(await createUseCase().execute({ userUuid: 'invalid' })).toEqual({
         success: false,
         error: {
-          message: 'User 1-2-3 not found.',
+          message: 'Given value is not a valid uuid: invalid',
         },
       })
     })
 
     it('should return all user settings except mfa', async () => {
-      expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
+      expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
         success: true,
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
         settings: [{ foo: 'bar' }],
       })
 
@@ -131,9 +142,9 @@ describe('GetSettings', () => {
       } as jest.Mocked<Setting>
       settingRepository.findAllByUserUuid = jest.fn().mockReturnValue([setting])
 
-      expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
+      expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
         success: true,
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
         settings: [{ foo: 'bar' }],
       })
 
@@ -147,10 +158,14 @@ describe('GetSettings', () => {
 
     it('should return all user settings of certain name', async () => {
       expect(
-        await createUseCase().execute({ userUuid: '1-2-3', settingName: 'test', allowSensitiveRetrieval: true }),
+        await createUseCase().execute({
+          userUuid: '00000000-0000-0000-0000-000000000000',
+          settingName: 'test',
+          allowSensitiveRetrieval: true,
+        }),
       ).toEqual({
         success: true,
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
         settings: [{ foo: 'bar' }],
       })
 
@@ -159,10 +174,14 @@ describe('GetSettings', () => {
 
     it('should return all user settings updated after', async () => {
       expect(
-        await createUseCase().execute({ userUuid: '1-2-3', allowSensitiveRetrieval: true, updatedAfter: 123 }),
+        await createUseCase().execute({
+          userUuid: '00000000-0000-0000-0000-000000000000',
+          allowSensitiveRetrieval: true,
+          updatedAfter: 123,
+        }),
       ).toEqual({
         success: true,
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
         settings: [{ foo: 'bar' }],
       })
 
@@ -170,9 +189,14 @@ describe('GetSettings', () => {
     })
 
     it('should return all sensitive user settings if explicit', async () => {
-      expect(await createUseCase().execute({ userUuid: '1-2-3', allowSensitiveRetrieval: true })).toEqual({
+      expect(
+        await createUseCase().execute({
+          userUuid: '00000000-0000-0000-0000-000000000000',
+          allowSensitiveRetrieval: true,
+        }),
+      ).toEqual({
         success: true,
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
         settings: [{ foo: 'bar' }, { foo: 'bar' }],
       })
 
@@ -190,9 +214,9 @@ describe('GetSettings', () => {
     })
 
     it('should return all user settings except mfa', async () => {
-      expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
+      expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
         success: true,
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
         settings: [{ foo: 'bar' }, { foo: 'sub-bar' }],
       })
 
@@ -210,9 +234,9 @@ describe('GetSettings', () => {
     })
 
     it('should return all user settings except mfa', async () => {
-      expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({
+      expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
         success: true,
-        userUuid: '1-2-3',
+        userUuid: '00000000-0000-0000-0000-000000000000',
         settings: [{ foo: 'bar' }, { foo: 'sub-bar' }],
       })
 

+ 14 - 4
packages/auth/src/Domain/UseCase/GetSettings/GetSettings.ts

@@ -15,6 +15,7 @@ import { SubscriptionSetting } from '../../Setting/SubscriptionSetting'
 import { SimpleSetting } from '../../Setting/SimpleSetting'
 import { SimpleSubscriptionSetting } from '../../Setting/SimpleSubscriptionSetting'
 import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class GetSettings implements UseCaseInterface {
@@ -30,7 +31,16 @@ export class GetSettings implements UseCaseInterface {
   ) {}
 
   async execute(dto: GetSettingsDto): Promise<GetSettingsResponse> {
-    const { userUuid } = dto
+    const userUuidOrError = Uuid.create(dto.userUuid)
+    if (userUuidOrError.isFailed()) {
+      return {
+        success: false,
+        error: {
+          message: userUuidOrError.getError(),
+        },
+      }
+    }
+    const userUuid = userUuidOrError.getValue()
 
     const user = await this.userRepository.findOneByUuid(userUuid)
 
@@ -38,13 +48,13 @@ export class GetSettings implements UseCaseInterface {
       return {
         success: false,
         error: {
-          message: `User ${userUuid} not found.`,
+          message: `User ${userUuid.value} not found.`,
         },
       }
     }
 
     let settings: Array<Setting | SubscriptionSetting>
-    settings = await this.settingRepository.findAllByUserUuid(userUuid)
+    settings = await this.settingRepository.findAllByUserUuid(user.uuid)
 
     const { regularSubscription, sharedSubscription } =
       await this.userSubscriptionService.findRegularSubscriptionForUserUuid(user.uuid)
@@ -83,7 +93,7 @@ export class GetSettings implements UseCaseInterface {
 
     return {
       success: true,
-      userUuid,
+      userUuid: user.uuid,
       settings: simpleSettings,
     }
   }

+ 26 - 13
packages/auth/src/Domain/UseCase/GetUserFeatures/GetUserFeatures.spec.ts

@@ -31,23 +31,36 @@ describe('GetUserFeatures', () => {
   it('should fail if a user is not found', async () => {
     userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
 
-    expect(await createUseCase().execute({ userUuid: 'user-1-1-1', offline: false })).toEqual({
-      success: false,
-      error: {
-        message: 'User user-1-1-1 not found.',
+    expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', offline: false })).toEqual(
+      {
+        success: false,
+        error: {
+          message: 'User 00000000-0000-0000-0000-000000000000 not found.',
+        },
       },
-    })
+    )
   })
 
   it('should return user features', async () => {
-    expect(await createUseCase().execute({ userUuid: 'user-1-1-1', offline: false })).toEqual({
-      success: true,
-      userUuid: 'user-1-1-1',
-      features: [
-        {
-          name: 'foobar',
-        },
-      ],
+    expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', offline: false })).toEqual(
+      {
+        success: true,
+        userUuid: '00000000-0000-0000-0000-000000000000',
+        features: [
+          {
+            name: 'foobar',
+          },
+        ],
+      },
+    )
+  })
+
+  it('should fail if a user uuid is invalid', async () => {
+    expect(await createUseCase().execute({ userUuid: 'invalid', offline: false })).toEqual({
+      success: false,
+      error: {
+        message: 'Given value is not a valid uuid: invalid',
+      },
     })
   })
 

+ 15 - 3
packages/auth/src/Domain/UseCase/GetUserFeatures/GetUserFeatures.ts

@@ -5,6 +5,7 @@ import { GetUserFeaturesDto } from './GetUserFeaturesDto'
 import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
 import { GetUserFeaturesResponse } from './GetUserFeaturesResponse'
 import { FeatureServiceInterface } from '../../Feature/FeatureServiceInterface'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class GetUserFeatures implements UseCaseInterface {
@@ -24,13 +25,24 @@ export class GetUserFeatures implements UseCaseInterface {
       }
     }
 
-    const user = await this.userRepository.findOneByUuid(dto.userUuid)
+    const userUuidOrError = Uuid.create(dto.userUuid)
+    if (userUuidOrError.isFailed()) {
+      return {
+        success: false,
+        error: {
+          message: userUuidOrError.getError(),
+        },
+      }
+    }
+    const userUuid = userUuidOrError.getValue()
+
+    const user = await this.userRepository.findOneByUuid(userUuid)
 
     if (user === null) {
       return {
         success: false,
         error: {
-          message: `User ${dto.userUuid} not found.`,
+          message: `User ${userUuid.value} not found.`,
         },
       }
     }
@@ -39,7 +51,7 @@ export class GetUserFeatures implements UseCaseInterface {
 
     return {
       success: true,
-      userUuid: dto.userUuid,
+      userUuid: userUuid.value,
       features: userFeatures,
     }
   }

+ 25 - 4
packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.spec.ts

@@ -61,7 +61,9 @@ describe('GetUserKeyParams', () => {
   })
 
   it('should get key params for an authenticated user - searching by uuid', async () => {
-    expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: true })).toEqual({
+    expect(
+      await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', authenticated: true }),
+    ).toEqual({
       keyParams: {
         foo: 'bar',
       },
@@ -71,7 +73,9 @@ describe('GetUserKeyParams', () => {
   })
 
   it('should get key params for an unauthenticated user - searching by uuid', async () => {
-    expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: false })).toEqual({
+    expect(
+      await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', authenticated: false }),
+    ).toEqual({
       keyParams: {
         foo: 'bar',
       },
@@ -81,7 +85,13 @@ describe('GetUserKeyParams', () => {
   })
 
   it("should get key params for an unauthenticated user and store it's code challenge", async () => {
-    expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: false, codeChallenge: 'test' })).toEqual({
+    expect(
+      await createUseCase().execute({
+        userUuid: '00000000-0000-0000-0000-000000000000',
+        authenticated: false,
+        codeChallenge: 'test',
+      }),
+    ).toEqual({
       keyParams: {
         foo: 'bar',
       },
@@ -107,7 +117,18 @@ describe('GetUserKeyParams', () => {
 
     let error = null
     try {
-      await createUseCase().execute({ userUuid: '1-2-3', authenticated: false })
+      await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000', authenticated: false })
+    } catch (e) {
+      error = e
+    }
+
+    expect(error).not.toBeNull()
+  })
+
+  it('should throw error for an invalid user uuid', async () => {
+    let error = null
+    try {
+      await createUseCase().execute({ userUuid: 'invalid', authenticated: false })
     } catch (e) {
       error = e
     }

+ 8 - 2
packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts

@@ -10,7 +10,7 @@ import { User } from '../../User/User'
 import { PKCERepositoryInterface } from '../../User/PKCERepositoryInterface'
 import { GetUserKeyParamsDTOV2Challenged } from './GetUserKeyParamsDTOV2Challenged'
 import { KeyParamsData } from '@standardnotes/responses'
-import { Username } from '@standardnotes/domain-core'
+import { Username, Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class GetUserKeyParams implements UseCaseInterface {
@@ -39,7 +39,13 @@ export class GetUserKeyParams implements UseCaseInterface {
         }
       }
     } else if (dto.userUuid) {
-      user = await this.userRepository.findOneByUuid(dto.userUuid)
+      const userUuidOrError = Uuid.create(dto.userUuid)
+      if (userUuidOrError.isFailed()) {
+        throw Error(userUuidOrError.getError())
+      }
+      const userUuid = userUuidOrError.getValue()
+
+      user = await this.userRepository.findOneByUuid(userUuid)
     }
 
     if (!user) {

+ 17 - 5
packages/auth/src/Domain/UseCase/GetUserSubscription/GetUserSubscription.spec.ts

@@ -15,7 +15,10 @@ describe('GetUserSubscription', () => {
   const createUseCase = () => new GetUserSubscription(userRepository, userSubscriptionRepository)
 
   beforeEach(() => {
-    user = { uuid: 'user-1-1-1', email: 'user-1-1-1@example.com' } as jest.Mocked<User>
+    user = {
+      uuid: '00000000-0000-0000-0000-000000000000',
+      email: '00000000-0000-0000-0000-000000000000@example.com',
+    } as jest.Mocked<User>
     userRepository = {} as jest.Mocked<UserRepositoryInterface>
     userRepository.findOneByUuid = jest.fn().mockReturnValue(user)
 
@@ -29,18 +32,27 @@ describe('GetUserSubscription', () => {
   it('should fail if a user is not found', async () => {
     userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
 
-    expect(await createUseCase().execute({ userUuid: 'user-1-1-1' })).toEqual({
+    expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
       success: false,
       error: {
-        message: 'User user-1-1-1 not found.',
+        message: 'User 00000000-0000-0000-0000-000000000000 not found.',
+      },
+    })
+  })
+
+  it('should fail if a user uuid is invalid', async () => {
+    expect(await createUseCase().execute({ userUuid: 'invalid' })).toEqual({
+      success: false,
+      error: {
+        message: 'Given value is not a valid uuid: invalid',
       },
     })
   })
 
   it('should return user subscription', async () => {
-    expect(await createUseCase().execute({ userUuid: 'user-1-1-1' })).toEqual({
+    expect(await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })).toEqual({
       success: true,
-      user: { uuid: 'user-1-1-1', email: 'user-1-1-1@example.com' },
+      user: { uuid: '00000000-0000-0000-0000-000000000000', email: '00000000-0000-0000-0000-000000000000@example.com' },
       subscription: {
         planName: SubscriptionName.ProPlan,
       },

+ 13 - 3
packages/auth/src/Domain/UseCase/GetUserSubscription/GetUserSubscription.ts

@@ -5,6 +5,7 @@ import { GetUserSubscriptionDto } from './GetUserSubscriptionDto'
 import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
 import { GetUserSubscriptionResponse } from './GetUserSubscriptionResponse'
 import { UserSubscriptionRepositoryInterface } from '../../Subscription/UserSubscriptionRepositoryInterface'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class GetUserSubscription implements UseCaseInterface {
@@ -15,7 +16,16 @@ export class GetUserSubscription implements UseCaseInterface {
   ) {}
 
   async execute(dto: GetUserSubscriptionDto): Promise<GetUserSubscriptionResponse> {
-    const { userUuid } = dto
+    const userUuidOrError = Uuid.create(dto.userUuid)
+    if (userUuidOrError.isFailed()) {
+      return {
+        success: false,
+        error: {
+          message: userUuidOrError.getError(),
+        },
+      }
+    }
+    const userUuid = userUuidOrError.getValue()
 
     const user = await this.userRepository.findOneByUuid(userUuid)
 
@@ -23,12 +33,12 @@ export class GetUserSubscription implements UseCaseInterface {
       return {
         success: false,
         error: {
-          message: `User ${userUuid} not found.`,
+          message: `User ${userUuid.value} not found.`,
         },
       }
     }
 
-    const userSubscription = await this.userSubscriptionRepository.findOneByUserUuid(userUuid)
+    const userSubscription = await this.userSubscriptionRepository.findOneByUserUuid(userUuid.value)
 
     return {
       success: true,

+ 1 - 1
packages/auth/src/Domain/UseCase/ListAuthenticators/ListAuthenticators.ts

@@ -21,7 +21,7 @@ export class ListAuthenticators implements UseCaseInterface<Authenticator[]> {
     }
     const userUuid = userUuidOrError.getValue()
 
-    const user = await this.userRepository.findOneByUuid(userUuid.value)
+    const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {
       return Result.fail('Could not list authenticators: user not found.')
     }

+ 34 - 13
packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.spec.ts

@@ -62,7 +62,7 @@ describe('UpdateSetting', () => {
     settingProjector.projectSimple = jest.fn().mockReturnValue(settingProjection)
 
     regularSubscription = {
-      uuid: '1-2-3',
+      uuid: '00000000-0000-0000-0000-000000000000',
       subscriptionType: UserSubscriptionType.Regular,
       user: Promise.resolve(user),
     } as jest.Mocked<UserSubscription>
@@ -109,7 +109,7 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(settingService.createOrReplace).toHaveBeenCalledWith({
         props: {
@@ -138,19 +138,40 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(settingService.createOrReplace).not.toHaveBeenCalled()
 
       expect(response).toEqual({
         success: false,
         error: {
-          message: 'User 1-2-3 not found.',
+          message: 'User 00000000-0000-0000-0000-000000000000 not found.',
         },
         statusCode: 404,
       })
     })
 
+    it('should not create a setting if user uuid is invalid', async () => {
+      const props = {
+        name: SettingName.NAMES.ExtensionKey,
+        unencryptedValue: 'test-setting-value',
+        serverEncryptionVersion: EncryptionVersion.Unencrypted,
+        sensitive: false,
+      }
+
+      const response = await createUseCase().execute({ props, userUuid: 'invalid' })
+
+      expect(settingService.createOrReplace).not.toHaveBeenCalled()
+
+      expect(response).toEqual({
+        success: false,
+        error: {
+          message: 'Given value is not a valid uuid: invalid',
+        },
+        statusCode: 400,
+      })
+    })
+
     it('should not create a subscription setting', async () => {
       const props = {
         name: SettingName.NAMES.MuteSignInEmails,
@@ -159,14 +180,14 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(settingService.createOrReplace).not.toHaveBeenCalled()
 
       expect(response).toEqual({
         success: false,
         error: {
-          message: 'User 1-2-3 has no subscription to change a subscription setting.',
+          message: 'User 00000000-0000-0000-0000-000000000000 has no subscription to change a subscription setting.',
         },
         statusCode: 400,
       })
@@ -180,7 +201,7 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(settingService.createOrReplace).not.toHaveBeenCalled()
 
@@ -207,14 +228,14 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(settingService.createOrReplace).not.toHaveBeenCalled()
 
       expect(response).toEqual({
         success: false,
         error: {
-          message: 'User 1-2-3 is not permitted to change the setting.',
+          message: 'User 00000000-0000-0000-0000-000000000000 is not permitted to change the setting.',
         },
         statusCode: 401,
       })
@@ -230,14 +251,14 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(settingService.createOrReplace).not.toHaveBeenCalled()
 
       expect(response).toEqual({
         success: false,
         error: {
-          message: 'User 1-2-3 is not permitted to change the setting.',
+          message: 'User 00000000-0000-0000-0000-000000000000 is not permitted to change the setting.',
         },
         statusCode: 401,
       })
@@ -259,7 +280,7 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(subscriptionSettingService.createOrReplace).toHaveBeenCalledWith({
         props: {
@@ -295,7 +316,7 @@ describe('UpdateSetting', () => {
         sensitive: false,
       }
 
-      const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
+      const response = await createUseCase().execute({ props, userUuid: '00000000-0000-0000-0000-000000000000' })
 
       expect(subscriptionSettingService.createOrReplace).toHaveBeenCalledWith({
         props: {

+ 19 - 8
packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.ts

@@ -16,6 +16,7 @@ import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionS
 import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
 import { CreateOrReplaceSubscriptionSettingResponse } from '../../Setting/CreateOrReplaceSubscriptionSettingResponse'
 import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
+import { Uuid } from '@standardnotes/domain-core'
 
 @injectable()
 export class UpdateSetting implements UseCaseInterface {
@@ -48,7 +49,17 @@ export class UpdateSetting implements UseCaseInterface {
 
     this.logger.debug('[%s] Updating setting: %O', dto.userUuid, dto)
 
-    const { userUuid, props } = dto
+    const userUuidOrError = Uuid.create(dto.userUuid)
+    if (userUuidOrError.isFailed()) {
+      return {
+        success: false,
+        error: {
+          message: userUuidOrError.getError(),
+        },
+        statusCode: 400,
+      }
+    }
+    const userUuid = userUuidOrError.getValue()
 
     const user = await this.userRepository.findOneByUuid(userUuid)
 
@@ -56,7 +67,7 @@ export class UpdateSetting implements UseCaseInterface {
       return {
         success: false,
         error: {
-          message: `User ${userUuid} not found.`,
+          message: `User ${userUuid.value} not found.`,
         },
         statusCode: 404,
       }
@@ -66,14 +77,14 @@ export class UpdateSetting implements UseCaseInterface {
       return {
         success: false,
         error: {
-          message: `User ${userUuid} is not permitted to change the setting.`,
+          message: `User ${userUuid.value} is not permitted to change the setting.`,
         },
         statusCode: 401,
       }
     }
 
-    props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(settingName)
-    props.sensitive = this.settingsAssociationService.getSensitivityForSetting(settingName)
+    dto.props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(settingName)
+    dto.props.sensitive = this.settingsAssociationService.getSensitivityForSetting(settingName)
 
     if (settingName.isASubscriptionSetting()) {
       const { regularSubscription, sharedSubscription } =
@@ -83,7 +94,7 @@ export class UpdateSetting implements UseCaseInterface {
         return {
           success: false,
           error: {
-            message: `User ${userUuid} has no subscription to change a subscription setting.`,
+            message: `User ${userUuid.value} has no subscription to change a subscription setting.`,
           },
           statusCode: 400,
         }
@@ -92,7 +103,7 @@ export class UpdateSetting implements UseCaseInterface {
       const response = await this.subscriptionSettingService.createOrReplace({
         userSubscription: subscription,
         user,
-        props,
+        props: dto.props,
       })
 
       return {
@@ -104,7 +115,7 @@ export class UpdateSetting implements UseCaseInterface {
 
     const response = await this.settingService.createOrReplace({
       user,
-      props,
+      props: dto.props,
     })
 
     return {

+ 1 - 1
packages/auth/src/Domain/UseCase/VerifyAuthenticatorRegistrationResponse/VerifyAuthenticatorRegistrationResponse.ts

@@ -27,7 +27,7 @@ export class VerifyAuthenticatorRegistrationResponse implements UseCaseInterface
     }
     const userUuid = userUuidOrError.getValue()
 
-    const user = await this.userRepository.findOneByUuid(userUuid.value)
+    const user = await this.userRepository.findOneByUuid(userUuid)
     if (user === null) {
       return Result.fail('Could not verify authenticator registration response: user not found.')
     }

+ 2 - 2
packages/auth/src/Domain/User/UserRepositoryInterface.ts

@@ -1,4 +1,4 @@
-import { Email, Username } from '@standardnotes/domain-core'
+import { Email, Username, Uuid } from '@standardnotes/domain-core'
 
 import { ReadStream } from 'fs'
 import { User } from './User'
@@ -6,7 +6,7 @@ import { User } from './User'
 export interface UserRepositoryInterface {
   streamAll(): Promise<ReadStream>
   streamTeam(memberEmail?: Email): Promise<ReadStream>
-  findOneByUuid(uuid: string): Promise<User | null>
+  findOneByUuid(uuid: Uuid): Promise<User | null>
   findOneByUsernameOrEmail(usernameOrEmail: Email | Username): Promise<User | null>
   save(user: User): Promise<User>
   remove(user: User): Promise<User>

+ 14 - 2
packages/auth/src/Infra/InversifyExpressUtils/HomeServer/HomeServerUsersController.ts

@@ -29,6 +29,7 @@ export class HomeServerUsersController extends BaseHttpController {
       this.controllerContainer.register('auth.users.getKeyParams', this.keyParams.bind(this))
       this.controllerContainer.register('auth.users.getSubscription', this.getSubscription.bind(this))
       this.controllerContainer.register('auth.users.updateCredentials', this.changeCredentials.bind(this))
+      this.controllerContainer.register('auth.users.delete', this.deleteAccount.bind(this))
     }
   }
 
@@ -102,9 +103,20 @@ export class HomeServerUsersController extends BaseHttpController {
     return this.json(result.keyParams)
   }
 
-  async deleteAccount(request: Request): Promise<results.JsonResult> {
+  async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
+    if (request.params.userUuid !== response.locals.user.uuid) {
+      return this.json(
+        {
+          error: {
+            message: 'Operation not allowed.',
+          },
+        },
+        401,
+      )
+    }
+
     const result = await this.doDeleteAccount.execute({
-      email: request.params.email,
+      userUuid: request.params.userUuid,
     })
 
     return this.json({ message: result.message }, result.responseCode)

+ 20 - 3
packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.spec.ts

@@ -170,17 +170,34 @@ describe('InversifyExpressUsersController', () => {
   })
 
   it('should delete user', async () => {
-    request.params.email = 'test@test.te'
+    request.params.userUuid = '1-2-3'
+    response.locals.user = {
+      uuid: '1-2-3',
+    }
 
-    const httpResponse = <results.JsonResult>await createController().deleteAccount(request)
+    const httpResponse = <results.JsonResult>await createController().deleteAccount(request, response)
     const result = await httpResponse.executeAsync()
 
-    expect(deleteAccount.execute).toHaveBeenCalledWith({ email: 'test@test.te' })
+    expect(deleteAccount.execute).toHaveBeenCalledWith({ userUuid: '1-2-3' })
 
     expect(result.statusCode).toEqual(200)
     expect(await result.content.readAsStringAsync()).toEqual('{"message":"A OK"}')
   })
 
+  it('should not delete user if user uuid is different than the one in the session', async () => {
+    request.params.userUuid = '1-2-3'
+    response.locals.user = {
+      uuid: '2-3-4',
+    }
+
+    const httpResponse = <results.JsonResult>await createController().deleteAccount(request, response)
+    const result = await httpResponse.executeAsync()
+
+    expect(deleteAccount.execute).not.toHaveBeenCalled()
+
+    expect(result.statusCode).toEqual(401)
+  })
+
   it('should get user key params', async () => {
     request.query = {
       email: 'test@test.te',

+ 3 - 3
packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.ts

@@ -51,9 +51,9 @@ export class InversifyExpressUsersController extends HomeServerUsersController {
     return super.keyParams(request)
   }
 
-  @httpDelete('/:email')
-  override async deleteAccount(request: Request): Promise<results.JsonResult> {
-    return super.deleteAccount(request)
+  @httpDelete('/:userUuid')
+  override async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
+    return super.deleteAccount(request, response)
   }
 
   @httpGet('/:userUuid/subscription', TYPES.Auth_RequiredCrossServiceTokenMiddleware)

+ 4 - 4
packages/auth/src/Infra/TypeORM/TypeORMUserRepository.ts

@@ -1,4 +1,4 @@
-import { Email, Username } from '@standardnotes/domain-core'
+import { Email, Username, Uuid } from '@standardnotes/domain-core'
 import { ReadStream } from 'fs'
 import { inject, injectable } from 'inversify'
 import { Repository } from 'typeorm'
@@ -40,11 +40,11 @@ export class TypeORMUserRepository implements UserRepositoryInterface {
     return queryBuilder.stream()
   }
 
-  async findOneByUuid(uuid: string): Promise<User | null> {
+  async findOneByUuid(uuid: Uuid): Promise<User | null> {
     return this.ormRepository
       .createQueryBuilder('user')
-      .where('user.uuid = :uuid', { uuid })
-      .cache(`user_uuid_${uuid}`, 60000)
+      .where('user.uuid = :uuid', { uuid: uuid.value })
+      .cache(`user_uuid_${uuid.value}`, 60000)
       .getOne()
   }