@solidxai/core 0.1.4 → 0.1.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/dist/constants/error-messages.d.ts +1 -0
  3. package/dist/constants/error-messages.d.ts.map +1 -1
  4. package/dist/constants/error-messages.js +1 -0
  5. package/dist/constants/error-messages.js.map +1 -1
  6. package/dist/constants.d.ts +3 -3
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +12 -12
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controllers/otp-authentication.controller.d.ts +1 -4
  11. package/dist/controllers/otp-authentication.controller.d.ts.map +1 -1
  12. package/dist/controllers/otp-authentication.controller.js +1 -1
  13. package/dist/controllers/role-metadata.controller.d.ts +1 -0
  14. package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
  15. package/dist/controllers/role-metadata.controller.js +15 -0
  16. package/dist/controllers/role-metadata.controller.js.map +1 -1
  17. package/dist/controllers/view-metadata.controller.d.ts +1 -0
  18. package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
  19. package/dist/dtos/create-email-template.dto.d.ts.map +1 -1
  20. package/dist/dtos/create-email-template.dto.js.map +1 -1
  21. package/dist/dtos/create-list-of-values.dto.d.ts.map +1 -1
  22. package/dist/dtos/create-list-of-values.dto.js.map +1 -1
  23. package/dist/dtos/create-menu-item-metadata.dto.d.ts.map +1 -1
  24. package/dist/dtos/create-menu-item-metadata.dto.js.map +1 -1
  25. package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
  26. package/dist/dtos/create-role-metadata.dto.js.map +1 -1
  27. package/dist/dtos/create-saved-filters.dto.d.ts +1 -0
  28. package/dist/dtos/create-saved-filters.dto.d.ts.map +1 -1
  29. package/dist/dtos/create-saved-filters.dto.js +8 -1
  30. package/dist/dtos/create-saved-filters.dto.js.map +1 -1
  31. package/dist/dtos/create-scheduled-job.dto.d.ts.map +1 -1
  32. package/dist/dtos/create-scheduled-job.dto.js.map +1 -1
  33. package/dist/dtos/create-security-rule.dto.d.ts.map +1 -1
  34. package/dist/dtos/create-security-rule.dto.js.map +1 -1
  35. package/dist/dtos/create-sms-template.dto.d.ts.map +1 -1
  36. package/dist/dtos/create-sms-template.dto.js.map +1 -1
  37. package/dist/dtos/create-view-metadata.dto.d.ts.map +1 -1
  38. package/dist/dtos/create-view-metadata.dto.js.map +1 -1
  39. package/dist/dtos/otp-sign-in.dto.d.ts +1 -1
  40. package/dist/dtos/otp-sign-in.dto.d.ts.map +1 -1
  41. package/dist/dtos/otp-sign-in.dto.js +2 -2
  42. package/dist/dtos/otp-sign-in.dto.js.map +1 -1
  43. package/dist/dtos/otp-sign-up.dto.d.ts +2 -2
  44. package/dist/dtos/otp-sign-up.dto.d.ts.map +1 -1
  45. package/dist/dtos/otp-sign-up.dto.js +2 -2
  46. package/dist/dtos/otp-sign-up.dto.js.map +1 -1
  47. package/dist/dtos/resolve-s3-url.dto.d.ts +2 -5
  48. package/dist/dtos/resolve-s3-url.dto.d.ts.map +1 -1
  49. package/dist/dtos/resolve-s3-url.dto.js +1 -13
  50. package/dist/dtos/resolve-s3-url.dto.js.map +1 -1
  51. package/dist/dtos/sign-up.dto.d.ts.map +1 -1
  52. package/dist/dtos/sign-up.dto.js.map +1 -1
  53. package/dist/dtos/update-email-template.dto.d.ts.map +1 -1
  54. package/dist/dtos/update-email-template.dto.js.map +1 -1
  55. package/dist/dtos/update-list-of-values.dto.d.ts.map +1 -1
  56. package/dist/dtos/update-list-of-values.dto.js.map +1 -1
  57. package/dist/dtos/update-menu-item-metadata.dto.d.ts.map +1 -1
  58. package/dist/dtos/update-menu-item-metadata.dto.js.map +1 -1
  59. package/dist/dtos/update-saved-filters.dto.d.ts +1 -0
  60. package/dist/dtos/update-saved-filters.dto.d.ts.map +1 -1
  61. package/dist/dtos/update-saved-filters.dto.js +10 -1
  62. package/dist/dtos/update-saved-filters.dto.js.map +1 -1
  63. package/dist/dtos/update-scheduled-job.dto.d.ts.map +1 -1
  64. package/dist/dtos/update-scheduled-job.dto.js.map +1 -1
  65. package/dist/dtos/update-security-rule.dto.d.ts.map +1 -1
  66. package/dist/dtos/update-security-rule.dto.js.map +1 -1
  67. package/dist/dtos/update-sms-template.dto.d.ts.map +1 -1
  68. package/dist/dtos/update-sms-template.dto.js.map +1 -1
  69. package/dist/dtos/update-view-metadata.dto.d.ts.map +1 -1
  70. package/dist/dtos/update-view-metadata.dto.js.map +1 -1
  71. package/dist/entities/chatter-message-details.entity.d.ts.map +1 -1
  72. package/dist/entities/chatter-message-details.entity.js +1 -0
  73. package/dist/entities/chatter-message-details.entity.js.map +1 -1
  74. package/dist/entities/chatter-message.entity.d.ts.map +1 -1
  75. package/dist/entities/chatter-message.entity.js +1 -0
  76. package/dist/entities/chatter-message.entity.js.map +1 -1
  77. package/dist/entities/common.entity.js +4 -4
  78. package/dist/entities/common.entity.js.map +1 -1
  79. package/dist/entities/legacy-common.entity.js +4 -4
  80. package/dist/entities/legacy-common.entity.js.map +1 -1
  81. package/dist/entities/saved-filters.entity.d.ts +1 -0
  82. package/dist/entities/saved-filters.entity.d.ts.map +1 -1
  83. package/dist/entities/saved-filters.entity.js +6 -1
  84. package/dist/entities/saved-filters.entity.js.map +1 -1
  85. package/dist/entities/user.entity.d.ts +1 -0
  86. package/dist/entities/user.entity.d.ts.map +1 -1
  87. package/dist/entities/user.entity.js +6 -1
  88. package/dist/entities/user.entity.js.map +1 -1
  89. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts +2 -0
  90. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
  91. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js +33 -23
  92. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
  93. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts +3 -0
  94. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
  95. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js +36 -23
  96. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
  97. package/dist/helpers/security.helper.js +1 -0
  98. package/dist/helpers/security.helper.js.map +1 -1
  99. package/dist/index.d.ts +0 -4
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +0 -4
  102. package/dist/index.js.map +1 -1
  103. package/dist/repository/solid-base.repository.d.ts +10 -1
  104. package/dist/repository/solid-base.repository.d.ts.map +1 -1
  105. package/dist/repository/solid-base.repository.js +109 -0
  106. package/dist/repository/solid-base.repository.js.map +1 -1
  107. package/dist/seeders/module-metadata-seeder.service.d.ts +2 -0
  108. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  109. package/dist/seeders/module-metadata-seeder.service.js +142 -72
  110. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  111. package/dist/seeders/permission-metadata-seeder.service.d.ts +1 -1
  112. package/dist/seeders/permission-metadata-seeder.service.d.ts.map +1 -1
  113. package/dist/seeders/permission-metadata-seeder.service.js +1 -1
  114. package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
  115. package/dist/seeders/seed-data/solid-core-metadata.json +23 -25
  116. package/dist/services/authentication.service.d.ts +22 -8
  117. package/dist/services/authentication.service.d.ts.map +1 -1
  118. package/dist/services/authentication.service.js +230 -214
  119. package/dist/services/authentication.service.js.map +1 -1
  120. package/dist/services/crud-helper.service.d.ts +4 -0
  121. package/dist/services/crud-helper.service.d.ts.map +1 -1
  122. package/dist/services/crud-helper.service.js +66 -32
  123. package/dist/services/crud-helper.service.js.map +1 -1
  124. package/dist/services/crud.service.d.ts.map +1 -1
  125. package/dist/services/crud.service.js +7 -4
  126. package/dist/services/crud.service.js.map +1 -1
  127. package/dist/services/field-metadata.service.d.ts.map +1 -1
  128. package/dist/services/field-metadata.service.js.map +1 -1
  129. package/dist/services/file/disk-file.service.d.ts +0 -2
  130. package/dist/services/file/disk-file.service.d.ts.map +1 -1
  131. package/dist/services/file/disk-file.service.js +7 -16
  132. package/dist/services/file/disk-file.service.js.map +1 -1
  133. package/dist/services/file/index.d.ts +1 -0
  134. package/dist/services/file/index.d.ts.map +1 -1
  135. package/dist/services/file/index.js +1 -0
  136. package/dist/services/file/index.js.map +1 -1
  137. package/dist/services/file/storage-path-builder.d.ts +17 -0
  138. package/dist/services/file/storage-path-builder.d.ts.map +1 -0
  139. package/dist/{seeders/sms-template-seeder.service.js → services/file/storage-path-builder.js} +45 -35
  140. package/dist/services/file/storage-path-builder.js.map +1 -0
  141. package/dist/services/media.service.d.ts +1 -1
  142. package/dist/services/media.service.d.ts.map +1 -1
  143. package/dist/services/media.service.js +45 -6
  144. package/dist/services/media.service.js.map +1 -1
  145. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  146. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  147. package/dist/services/mediaStorageProviders/file-storage-provider.js +46 -7
  148. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  149. package/dist/services/module-metadata.service.d.ts +4 -6
  150. package/dist/services/module-metadata.service.d.ts.map +1 -1
  151. package/dist/services/module-metadata.service.js +16 -14
  152. package/dist/services/module-metadata.service.js.map +1 -1
  153. package/dist/services/setting.service.d.ts +3 -2
  154. package/dist/services/setting.service.d.ts.map +1 -1
  155. package/dist/services/setting.service.js +7 -4
  156. package/dist/services/setting.service.js.map +1 -1
  157. package/dist/services/settings/default-settings-provider.service.d.ts +24 -2
  158. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  159. package/dist/services/settings/default-settings-provider.service.js +8 -6
  160. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  161. package/dist/services/view-metadata.service.d.ts +1 -0
  162. package/dist/services/view-metadata.service.d.ts.map +1 -1
  163. package/dist/services/view-metadata.service.js +25 -1
  164. package/dist/services/view-metadata.service.js.map +1 -1
  165. package/dist/solid-core.module.d.ts +3 -1
  166. package/dist/solid-core.module.d.ts.map +1 -1
  167. package/dist/solid-core.module.js +45 -9
  168. package/dist/solid-core.module.js.map +1 -1
  169. package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
  170. package/dist/testing/adapters/ui/playwright-adapter.js +35 -2
  171. package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
  172. package/dist/transformers/typeorm/local-date-time-transformer.d.ts +4 -3
  173. package/dist/transformers/typeorm/local-date-time-transformer.d.ts.map +1 -1
  174. package/dist/transformers/typeorm/local-date-time-transformer.js +20 -2
  175. package/dist/transformers/typeorm/local-date-time-transformer.js.map +1 -1
  176. package/package.json +8 -2
  177. package/src/constants/error-messages.ts +1 -0
  178. package/src/constants.ts +3 -3
  179. package/src/controllers/role-metadata.controller.ts +26 -18
  180. package/src/dtos/create-email-template.dto.ts +7 -0
  181. package/src/dtos/create-list-of-values.dto.ts +7 -0
  182. package/src/dtos/create-menu-item-metadata.dto.ts +12 -1
  183. package/src/dtos/create-role-metadata.dto.ts +9 -0
  184. package/src/dtos/create-saved-filters.dto.ts +7 -0
  185. package/src/dtos/create-scheduled-job.dto.ts +14 -0
  186. package/src/dtos/create-security-rule.dto.ts +6 -0
  187. package/src/dtos/create-sms-template.dto.ts +6 -0
  188. package/src/dtos/create-view-metadata.dto.ts +11 -0
  189. package/src/dtos/otp-sign-in.dto.ts +3 -3
  190. package/src/dtos/otp-sign-up.dto.ts +3 -3
  191. package/src/dtos/resolve-s3-url.dto.ts +2 -12
  192. package/src/dtos/sign-up.dto.ts +0 -2
  193. package/src/dtos/update-email-template.dto.ts +6 -0
  194. package/src/dtos/update-list-of-values.dto.ts +8 -0
  195. package/src/dtos/update-menu-item-metadata.dto.ts +12 -0
  196. package/src/dtos/update-saved-filters.dto.ts +5 -1
  197. package/src/dtos/update-scheduled-job.dto.ts +15 -0
  198. package/src/dtos/update-security-rule.dto.ts +7 -0
  199. package/src/dtos/update-sms-template.dto.ts +32 -32
  200. package/src/dtos/update-view-metadata.dto.ts +12 -0
  201. package/src/entities/chatter-message-details.entity.ts +1 -0
  202. package/src/entities/chatter-message.entity.ts +1 -0
  203. package/src/entities/common.entity.ts +5 -5
  204. package/src/entities/legacy-common.entity.ts +5 -5
  205. package/src/entities/saved-filters.entity.ts +3 -0
  206. package/src/entities/user.entity.ts +4 -1
  207. package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +43 -32
  208. package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +45 -33
  209. package/src/helpers/security.helper.ts +1 -1
  210. package/src/index.ts +0 -4
  211. package/src/repository/solid-base.repository.ts +172 -1
  212. package/src/seeders/module-metadata-seeder.service.ts +189 -127
  213. package/src/seeders/permission-metadata-seeder.service.ts +1 -4
  214. package/src/seeders/seed-data/solid-core-metadata.json +30 -32
  215. package/src/services/authentication.service.ts +273 -269
  216. package/src/services/crud-helper.service.ts +79 -36
  217. package/src/services/crud.service.ts +9 -4
  218. package/src/services/field-metadata.service.ts +0 -71
  219. package/src/services/file/disk-file.service.ts +8 -18
  220. package/src/services/file/index.ts +1 -0
  221. package/src/services/file/storage-path-builder.ts +56 -0
  222. package/src/services/media.service.ts +13 -7
  223. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +1 -1
  224. package/src/services/mediaStorageProviders/file-storage-provider.ts +13 -8
  225. package/src/services/module-metadata.service.ts +18 -15
  226. package/src/services/setting.service.ts +5 -3
  227. package/src/services/settings/default-settings-provider.service.ts +5 -3
  228. package/src/services/view-metadata.service.ts +29 -1
  229. package/src/solid-core.module.ts +16 -12
  230. package/src/testing/adapters/ui/playwright-adapter.ts +1 -1
  231. package/src/transformers/typeorm/local-date-time-transformer.ts +21 -3
  232. package/dist/passport-strategies/local.strategy.d.ts +0 -15
  233. package/dist/passport-strategies/local.strategy.d.ts.map +0 -1
  234. package/dist/passport-strategies/local.strategy.js +0 -44
  235. package/dist/passport-strategies/local.strategy.js.map +0 -1
  236. package/dist/seeders/email-template-seeder.service.d.ts +0 -10
  237. package/dist/seeders/email-template-seeder.service.d.ts.map +0 -1
  238. package/dist/seeders/email-template-seeder.service.js +0 -84
  239. package/dist/seeders/email-template-seeder.service.js.map +0 -1
  240. package/dist/seeders/sms-template-seeder.service.d.ts +0 -10
  241. package/dist/seeders/sms-template-seeder.service.d.ts.map +0 -1
  242. package/dist/seeders/sms-template-seeder.service.js.map +0 -1
  243. package/dist/seeders/user-seeder.service.d.ts +0 -10
  244. package/dist/seeders/user-seeder.service.d.ts.map +0 -1
  245. package/dist/seeders/user-seeder.service.js +0 -44
  246. package/dist/seeders/user-seeder.service.js.map +0 -1
  247. package/src/passport-strategies/local.strategy.ts +0 -28
  248. package/src/seeders/email-template-seeder.service.ts +0 -49
  249. package/src/seeders/sms-template-seeder.service.ts +0 -50
  250. package/src/seeders/user-seeder.service.ts +0 -33
  251. package/src/workflow.readme.md +0 -25
@@ -3,14 +3,13 @@ import type { SolidCoreSetting } from "src/services/settings/default-settings-pr
3
3
  import {
4
4
  BadRequestException,
5
5
  ConflictException,
6
- Inject,
6
+ ForbiddenException,
7
7
  Injectable,
8
8
  InternalServerErrorException,
9
9
  Logger,
10
10
  NotFoundException,
11
11
  UnauthorizedException,
12
12
  } from '@nestjs/common';
13
- import { ConfigType } from '@nestjs/config';
14
13
  import { EventEmitter2 } from '@nestjs/event-emitter';
15
14
  import { JwtService } from '@nestjs/jwt';
16
15
  import { InjectDataSource } from '@nestjs/typeorm';
@@ -21,13 +20,12 @@ import { SUCCESS_MESSAGES } from 'src/constants/success-messages';
21
20
  import { CreateUserDto } from 'src/dtos/create-user.dto';
22
21
  import { MailFactory } from 'src/factories/mail.factory';
23
22
  import { UserRepository } from 'src/repository/user.repository';
24
- import { Msg91OTPService } from 'src/services/sms/Msg91OTPService';
25
23
  import { DataSource, Repository } from 'typeorm';
26
24
  import { v4 as uuidv4 } from 'uuid';
27
25
  import {
28
26
  ForgotPasswordSendVerificationTokenOn,
29
- RegistrationValidationSource,
30
- TransactionalRegistrationValidationSource
27
+ PasswordlessLoginValidateWhatSources,
28
+ PasswordlessRegistrationValidateWhatSources
31
29
  } from "../constants";
32
30
  import { ChangePasswordDto } from "../dtos/change-password.dto";
33
31
  import { ConfirmForgotPasswordDto } from '../dtos/confirm-forgot-password.dto';
@@ -124,33 +122,22 @@ export class AuthenticationService {
124
122
  });
125
123
  }
126
124
 
127
- async validateUserAndRehashPasswordIfRequired(signInDto: SignInDto) {
128
-
129
- const user = await this.resolveUser(signInDto.username, signInDto.email);
130
-
131
- if (!user) {
132
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
133
- }
125
+ private async validateUserForPasswordLogin(user: User, password: string): Promise<void> {
134
126
  if (!user.active) {
135
127
  throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_ACTIVE);
136
128
  }
137
- const isEqual = await this.hashingService.compare(
138
- signInDto.password,
139
- user.password,
140
- user.passwordSchemeVersion
141
- );
129
+ this.checkAccountBlocked(user);
130
+ const isEqual = await this.hashingService.compare(password, user.password, user.passwordSchemeVersion);
142
131
  if (!isEqual) {
132
+ await this.incrementFailedAttempts(user);
143
133
  throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
144
134
  }
135
+ }
145
136
 
146
- // If we reach here means that the user has been validated successfully.
147
- // Now we check if the password needs to be rehashed based on the current hashing scheme and version.
137
+ private async rehashPasswordIfRequired(user: User, password: string): Promise<void> {
148
138
  if (this.hashingService.needsRehash(user.password, user.passwordSchemeVersion)) {
149
- const rehashedUser = await this.updatePasswordDetails(user, signInDto.password);
150
- return rehashedUser;
139
+ await this.updatePasswordDetails(user, password);
151
140
  }
152
-
153
- return user;
154
141
  }
155
142
 
156
143
  async signUp(signUpDto: SignUpDto, activeUser: ActiveUserData = null): Promise<User> {
@@ -375,60 +362,67 @@ export class AuthenticationService {
375
362
  }
376
363
 
377
364
  async otpInitiateRegistration(signUpDto: OTPSignUpDto) {
378
- try {
379
- const isPasswordlessRegistrationEnabled = await this.isPasswordlessRegistrationEnabled();
380
- if (!isPasswordlessRegistrationEnabled) {
381
- throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
382
- }
383
- // Validate if either mobile or email is present.
384
- if (isEmpty(signUpDto.mobile) && isEmpty(signUpDto.email)) {
385
- throw new BadRequestException(ERROR_MESSAGES.REGISTRATION_REQUIRES_CONTACT);
386
- }
387
- if (signUpDto.validationSources.includes(TransactionalRegistrationValidationSource.EMAIL) && isEmpty(signUpDto.email)) {
388
- throw new BadRequestException(ERROR_MESSAGES.EMAIL_REQUIRED_FOR_VALIDATION);
389
- }
390
- if (signUpDto.validationSources.includes(TransactionalRegistrationValidationSource.MOBILE) && isEmpty(signUpDto.mobile)) {
391
- throw new BadRequestException(ERROR_MESSAGES.MOBILE_REQUIRED_FOR_VALIDATION);
392
- }
365
+ const isPasswordlessRegistrationEnabled = await this.isPasswordlessRegistrationEnabled();
366
+ if (!isPasswordlessRegistrationEnabled) {
367
+ throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
368
+ }
393
369
 
394
- // Validate if user already exists.
395
- const existingUser = await this.userRepository.findOne({ //TODO Perhaps we should use the user service instead of the repository directly.
396
- where: [
397
- { email: signUpDto.email, },
398
- { mobile: signUpDto.mobile, },
399
- { username: signUpDto.username, }
400
- ]
401
- });
402
- if (isNotEmpty(existingUser) && existingUser.active) {
403
- throw new ConflictException(ERROR_MESSAGES.USER_ALREADY_EXISTS);
404
- }
405
- let passwordlessRegistrationValidateWhat = this.settingService.getConfigValue<SolidCoreSetting>('passwordlessRegistrationValidateWhat');
406
- if (!Array.isArray(passwordlessRegistrationValidateWhat)) {
407
- passwordlessRegistrationValidateWhat = [passwordlessRegistrationValidateWhat];
408
- }
409
- const finalRegistrationVerificationSources = this.calculateVerificationSources(passwordlessRegistrationValidateWhat, signUpDto);
410
- let user = existingUser
411
- if (isEmpty(user)) {
412
- user = this.createUser(signUpDto);
413
- await this.populateVerificationTokens(finalRegistrationVerificationSources, user);
414
- await this.userRepository.save(user);
415
- await this.userService.addRoleToUser(user.username, this.settingService.getConfigValue<SolidCoreSetting>('defaultRole'));
416
- }
417
- else {
418
- await this.populateVerificationTokens(finalRegistrationVerificationSources, user);
419
- await this.userRepository.save(user);
420
- }
370
+ const validationSource = this.resolvePasswordlessValidationSource();
371
+ this.validateOtpRegistrationInput(signUpDto, validationSource);
421
372
 
422
- // Send OTP to the user through email or SMS, depending on the configuration.
423
- await this.notifyUserOnOtpInitiateRegistration(user, finalRegistrationVerificationSources);
424
- return { message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_REGISTRATION }
373
+ const existingUser = await this.findExistingRegistrationUser(signUpDto);
374
+ if (isNotEmpty(existingUser) && existingUser.active) {
375
+ throw new ConflictException(ERROR_MESSAGES.USER_ALREADY_EXISTS);
376
+ }
377
+
378
+ try {
379
+ const user = await this.upsertUserWithRegistrationVerificationTokens(existingUser, signUpDto, validationSource);
380
+ await this.notifyUserOnOtpInitiateRegistration(user, validationSource);
425
381
  } catch (err) {
426
- const pgUniqueViolationErrorCode = '23505';
427
- if (err.code === pgUniqueViolationErrorCode) {
382
+ if (err.code === '23505') {
428
383
  throw new ConflictException(ERROR_MESSAGES.USER_ALREADY_EXISTS);
429
384
  }
430
385
  throw err;
431
386
  }
387
+
388
+ return { message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_REGISTRATION };
389
+ }
390
+
391
+ private validateOtpRegistrationInput(signUpDto: OTPSignUpDto, validationSource: string): void {
392
+ if (validationSource === PasswordlessRegistrationValidateWhatSources.EMAIL && isEmpty(signUpDto.email)) {
393
+ throw new BadRequestException(ERROR_MESSAGES.EMAIL_REQUIRED_FOR_VALIDATION);
394
+ }
395
+ if (validationSource === PasswordlessRegistrationValidateWhatSources.MOBILE && isEmpty(signUpDto.mobile)) {
396
+ throw new BadRequestException(ERROR_MESSAGES.MOBILE_REQUIRED_FOR_VALIDATION);
397
+ }
398
+ }
399
+
400
+ private async findExistingRegistrationUser(signUpDto: OTPSignUpDto): Promise<User> {
401
+ return this.userRepository.findOne({ //TODO Perhaps we should use the user service instead of the repository directly.
402
+ where: [
403
+ { email: signUpDto.email },
404
+ { mobile: signUpDto.mobile },
405
+ { username: signUpDto.username },
406
+ ]
407
+ });
408
+ }
409
+
410
+ private resolvePasswordlessValidationSource(): string {
411
+ return this.settingService.getConfigValue<SolidCoreSetting>('passwordlessRegistrationValidateWhat');
412
+ }
413
+
414
+ private async upsertUserWithRegistrationVerificationTokens(existingUser: User, signUpDto: OTPSignUpDto, validationSource: string): Promise<User> {
415
+ let user = existingUser;
416
+ if (isEmpty(user)) {
417
+ user = this.createUser(signUpDto);
418
+ await this.assignRegistrationOtp(validationSource, user);
419
+ await this.userRepository.save(user);
420
+ await this.userService.addRoleToUser(user.username, this.settingService.getConfigValue<SolidCoreSetting>('defaultRole'));
421
+ } else {
422
+ await this.assignRegistrationOtp(validationSource, user);
423
+ await this.userRepository.save(user);
424
+ }
425
+ return user;
432
426
  }
433
427
 
434
428
  // Create a new user entity.
@@ -442,36 +436,25 @@ export class AuthenticationService {
442
436
  return user;
443
437
  }
444
438
 
445
- private calculateVerificationSources(configuredRegistrationValidationSources: string[], signUpDto: OTPSignUpDto): string[] {
446
- const finalRegistrationValidationSources = configuredRegistrationValidationSources.filter((source) => source !== RegistrationValidationSource.TRANSACTIONAL);
447
- if (configuredRegistrationValidationSources.includes(RegistrationValidationSource.TRANSACTIONAL)) {
448
- finalRegistrationValidationSources.push(...signUpDto.validationSources); // Add the validation sources provided by the user.
449
- }
450
- return finalRegistrationValidationSources;
451
- }
452
-
453
439
  // Generate the validation tokens for the user i.e (system configured + user provided)
454
- private async populateVerificationTokens(finalRegistrationValidationSources: string[], user: User) {
455
- if (finalRegistrationValidationSources.length === 0) {
440
+ private async assignRegistrationOtp(passwordlessRegistrationValidateWhat: string, user: User) {
441
+ if (!passwordlessRegistrationValidateWhat) {
456
442
  throw new BadRequestException(ERROR_MESSAGES.VALIDATION_SOURCE_REQUIRED);
457
443
  }
458
- if (finalRegistrationValidationSources.includes(TransactionalRegistrationValidationSource.EMAIL)) {
444
+ const autoLoginUserOnRegistration = this.settingService.getConfigValue<SolidCoreSetting>('autoLoginUserOnRegistration');
445
+ if (passwordlessRegistrationValidateWhat === PasswordlessRegistrationValidateWhatSources.EMAIL) {
459
446
  const { token, expiresAt } = await this.otp();
460
447
  user.emailVerificationTokenOnRegistration = token;
461
448
  user.emailVerificationTokenOnRegistrationExpiresAt = expiresAt;
462
- const autoLoginUserOnRegistration = this.settingService.getConfigValue<SolidCoreSetting>('autoLoginUserOnRegistration');
463
-
464
449
  if (autoLoginUserOnRegistration) {
465
450
  user.emailVerificationTokenOnLogin = token;
466
451
  user.emailVerificationTokenOnLoginExpiresAt = expiresAt;
467
452
  }
468
453
  }
469
- if (finalRegistrationValidationSources.includes(TransactionalRegistrationValidationSource.MOBILE)) {
454
+ if (passwordlessRegistrationValidateWhat === PasswordlessRegistrationValidateWhatSources.MOBILE) {
470
455
  const { token, expiresAt } = await this.otp();
471
456
  user.mobileVerificationTokenOnRegistration = token;
472
457
  user.mobileVerificationTokenOnRegistrationExpiresAt = expiresAt;
473
-
474
- const autoLoginUserOnRegistration = this.settingService.getConfigValue<SolidCoreSetting>('autoLoginUserOnRegistration');
475
458
  if (autoLoginUserOnRegistration) {
476
459
  user.mobileVerificationTokenOnLogin = token;
477
460
  user.mobileVerificationTokenOnLoginExpiresAt = expiresAt;
@@ -479,13 +462,13 @@ export class AuthenticationService {
479
462
  }
480
463
  }
481
464
 
482
- private async notifyUserOnOtpInitiateRegistration(user: User, registrationValidationSources: string[]) {
465
+ private async notifyUserOnOtpInitiateRegistration(user: User, registrationValidationSource: string) {
483
466
  const companyLogo = await this.getCompanyLogo();
484
467
  const dummyOtp = this.settingService.getConfigValue<SolidCoreSetting>('dummyOtp');
485
468
 
486
469
  if (dummyOtp)
487
470
  return; // Do nothing if dummy otp is configured.
488
- if (registrationValidationSources.includes(RegistrationValidationSource.EMAIL)) {
471
+ if (registrationValidationSource === PasswordlessLoginValidateWhatSources.EMAIL) {
489
472
  const mailService = this.mailServiceFactory.getMailService();
490
473
  mailService.sendEmailUsingTemplate(
491
474
  user.email,
@@ -505,7 +488,7 @@ export class AuthenticationService {
505
488
  user.id
506
489
  );
507
490
  }
508
- if (registrationValidationSources.includes(RegistrationValidationSource.MOBILE)) {
491
+ if (registrationValidationSource === PasswordlessLoginValidateWhatSources.MOBILE) {
509
492
  const smsService = this.smsFactory.getSmsService();
510
493
  smsService.sendSMSUsingTemplate(
511
494
  user.mobile,
@@ -526,58 +509,68 @@ export class AuthenticationService {
526
509
 
527
510
  async otpConfirmRegistration(confirmSignUpDto: OTPConfirmOTPDto) {
528
511
  const isPasswordlessRegistrationEnabled = await this.isPasswordlessRegistrationEnabled();
529
-
530
512
  if (!isPasswordlessRegistrationEnabled) {
531
513
  throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
532
514
  }
533
515
 
534
- // Based on the identifier, validate by query the user table.
535
- if (confirmSignUpDto.type === RegistrationValidationSource.EMAIL) {
536
- const user = await this.userRepository.findOne({
537
- where: {
538
- email: confirmSignUpDto.identifier,
539
- }
540
- });
541
- if (!user) {
542
- throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
543
- }
544
- if (user.emailVerificationTokenOnRegistration !== confirmSignUpDto.otp) {
545
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
546
- }
547
- if (user.emailVerificationTokenOnRegistrationExpiresAt < new Date()) {
548
- throw new UnauthorizedException(ERROR_MESSAGES.OTP_EXPIRED);
549
- }
516
+ const { type, identifier, otp } = confirmSignUpDto;
517
+ if (type !== PasswordlessRegistrationValidateWhatSources.EMAIL &&
518
+ type !== PasswordlessRegistrationValidateWhatSources.MOBILE) {
519
+ throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
520
+ }
521
+
522
+ const user = await this.findUserByRegistrationIdentifier(type, identifier);
523
+ this.validateRegistrationOtp(user, otp, type);
524
+ this.clearRegistrationOtp(user, type);
525
+ user.active = this.settingService.getConfigValue<SolidCoreSetting>('activateUserOnRegistration') &&
526
+ await this.areAllPasswordlessRegistrationValidationSourcesVerified(user);
527
+
528
+ const savedUser: User = await this.userRepository.save(user);
529
+ this.triggerRegistrationEvent(savedUser);
530
+ return { active: savedUser.active, message: `User registration verified for ${type}` };
531
+ }
532
+
533
+ private async findUserByRegistrationIdentifier(
534
+ type: PasswordlessRegistrationValidateWhatSources,
535
+ identifier: string,
536
+ ): Promise<User> {
537
+ const where = type === PasswordlessRegistrationValidateWhatSources.EMAIL
538
+ ? { email: identifier }
539
+ : { mobile: identifier };
540
+ const user = await this.userRepository.findOne({ where });
541
+ if (!user) {
542
+ throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
543
+ }
544
+ return user;
545
+ }
546
+
547
+ private validateRegistrationOtp(
548
+ user: User,
549
+ otp: string,
550
+ type: PasswordlessRegistrationValidateWhatSources,
551
+ ): void {
552
+ const isEmail = type === PasswordlessRegistrationValidateWhatSources.EMAIL;
553
+ const token = isEmail ? user.emailVerificationTokenOnRegistration : user.mobileVerificationTokenOnRegistration;
554
+ const expiresAt = isEmail ? user.emailVerificationTokenOnRegistrationExpiresAt : user.mobileVerificationTokenOnRegistrationExpiresAt;
555
+
556
+ if (token !== otp) {
557
+ throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
558
+ }
559
+ if (expiresAt < new Date()) {
560
+ throw new UnauthorizedException(ERROR_MESSAGES.OTP_EXPIRED);
561
+ }
562
+ }
563
+
564
+ private clearRegistrationOtp(user: User, type: PasswordlessRegistrationValidateWhatSources): void {
565
+ if (type === PasswordlessRegistrationValidateWhatSources.EMAIL) {
550
566
  user.emailVerifiedOnRegistrationAt = new Date();
551
567
  user.emailVerificationTokenOnRegistration = null;
552
568
  user.emailVerificationTokenOnRegistrationExpiresAt = null;
553
- user.active = this.settingService.getConfigValue<SolidCoreSetting>('activateUserOnRegistration') && await this.areRegistrationValidationSourcesVerified(user);
554
- const savedUser: User = await this.userRepository.save(user);
555
- this.triggerRegistrationEvent(savedUser);
556
- return { active: savedUser.active, message: `User registration verified for ${confirmSignUpDto.type}` }
557
- } else if (confirmSignUpDto.type === RegistrationValidationSource.MOBILE) {
558
- const user = await this.userRepository.findOne({
559
- where: {
560
- mobile: confirmSignUpDto.identifier,
561
- }
562
- });
563
- if (!user) {
564
- throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
565
- }
566
- if (user.mobileVerificationTokenOnRegistration !== confirmSignUpDto.otp) {
567
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
568
- }
569
- if (user.mobileVerificationTokenOnRegistrationExpiresAt < new Date()) {
570
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
571
- }
569
+ } else {
572
570
  user.mobileVerifiedOnRegistrationAt = new Date();
573
571
  user.mobileVerificationTokenOnRegistration = null;
574
572
  user.mobileVerificationTokenOnRegistrationExpiresAt = null;
575
- user.active = this.settingService.getConfigValue<SolidCoreSetting>('activateUserOnRegistration') && await this.areRegistrationValidationSourcesVerified(user);
576
- const savedUser: User = await this.userRepository.save(user);
577
- this.triggerRegistrationEvent(savedUser);
578
- return { active: savedUser.active, message: `User registration verified for ${confirmSignUpDto.type}` }
579
573
  }
580
- throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
581
574
  }
582
575
 
583
576
  private triggerRegistrationEvent(savedUser: User) {
@@ -586,16 +579,14 @@ export class AuthenticationService {
586
579
  this.eventEmitter.emit(EventType.USER_REGISTERED, event);
587
580
  }
588
581
 
589
- async areRegistrationValidationSourcesVerified(user: User): Promise<boolean> {
590
- const passwordlessRegistrationValidateWhat = this.settingService.getConfigValue<SolidCoreSetting>('passwordlessRegistrationValidateWhat');
591
-
592
- const registrationValidationSources = passwordlessRegistrationValidateWhat;
593
- if (registrationValidationSources.includes(RegistrationValidationSource.EMAIL)) {
582
+ private async areAllPasswordlessRegistrationValidationSourcesVerified(user: User): Promise<boolean> {
583
+ const registrationValidationSource = this.resolvePasswordlessValidationSource();
584
+ if (registrationValidationSource === PasswordlessLoginValidateWhatSources.EMAIL) {
594
585
  if (!user.emailVerifiedOnRegistrationAt) {
595
586
  return false;
596
587
  }
597
588
  }
598
- if (registrationValidationSources.includes(RegistrationValidationSource.MOBILE)) {
589
+ if (registrationValidationSource === PasswordlessLoginValidateWhatSources.MOBILE) {
599
590
  if (!user.mobileVerifiedOnRegistrationAt) {
600
591
  return false;
601
592
  }
@@ -615,9 +606,14 @@ export class AuthenticationService {
615
606
  }
616
607
 
617
608
  async signIn(signInDto: SignInDto) {
618
- const user = await this.validateUserAndRehashPasswordIfRequired(signInDto);
609
+ const user = await this.resolveUser(signInDto.username, signInDto.email);
610
+ if (!user) {
611
+ throw new UnauthorizedException(ERROR_MESSAGES.INVALID_CREDENTIALS);
612
+ }
613
+ await this.validateUserForPasswordLogin(user, signInDto.password);
614
+ await this.rehashPasswordIfRequired(user, signInDto.password);
615
+ await this.resetFailedAttempts(user);
619
616
 
620
- // TODO: Unset the password etc...
621
617
  const tokens = await this.generateTokens(user);
622
618
 
623
619
  await this.userActivityHistoryService.logEvent('login', user);
@@ -665,78 +661,79 @@ export class AuthenticationService {
665
661
  throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
666
662
  }
667
663
 
668
- // Validate & generate otp token for the user based on the identifier type.
669
- if (signInDto.type === RegistrationValidationSource.EMAIL) {
670
- // const user = await this.userRepository.findOne({
671
- // where: {
672
- // email: signInDto.identifier,
673
- // }
674
- // });
675
- const user = await this.userRepository.findOne({
676
- where: [
677
- { username: signInDto.identifier },
678
- { email: signInDto.identifier },
679
- ]
680
- });
664
+ const type = this.resolveLoginType(signInDto);
665
+ const user = await this.findUserForLogin(type, signInDto.identifier);
666
+ await this.assignLoginOtp(user, type);
667
+ this.notifyUserOnOtpInititateLogin(user, type);
668
+ return this.buildLoginOtpResponse(user, type);
669
+ }
681
670
 
682
- if (!user) {
683
- throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
684
- }
685
- if (!user.active) {
686
- throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
687
- }
688
- const { token, expiresAt } = await this.otp();
689
- user.emailVerificationTokenOnLogin = token;
690
- user.emailVerificationTokenOnLoginExpiresAt = expiresAt;
691
- await this.userRepository.save(user);
692
- this.notifyUserOnOtpInititateLogin(user, RegistrationValidationSource.EMAIL);
693
- return {
694
- message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_LOGIN,
695
- user: {
696
- email: this.maskEmail(user.email)
697
- }
698
- };
699
- } else if (signInDto.type === RegistrationValidationSource.MOBILE) {
700
- // const user = await this.userRepository.findOne({
701
- // where: {
702
- // mobile: signInDto.identifier,
703
- // }
704
- // });
705
- const user = await this.userRepository.findOne({
706
- where: [
707
- { username: signInDto.identifier },
708
- { mobile: signInDto.identifier },
709
- ]
710
- });
671
+ private resolveLoginType(signInDto: OTPSignInDto): PasswordlessLoginValidateWhatSources {
672
+ const setting = this.settingService.getConfigValue<SolidCoreSetting>('passwordlessLoginValidateWhat') as PasswordlessLoginValidateWhatSources;
711
673
 
712
- if (!user) {
713
- throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
674
+ if (setting === PasswordlessLoginValidateWhatSources.SELECTABLE) {
675
+ if (signInDto.type !== PasswordlessLoginValidateWhatSources.EMAIL &&
676
+ signInDto.type !== PasswordlessLoginValidateWhatSources.MOBILE) {
677
+ throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
714
678
  }
679
+ return signInDto.type as PasswordlessLoginValidateWhatSources;
680
+ }
715
681
 
716
- const { token, expiresAt } = await this.otp();
682
+ if (setting === PasswordlessLoginValidateWhatSources.EMAIL ||
683
+ setting === PasswordlessLoginValidateWhatSources.MOBILE) {
684
+ return setting;
685
+ }
686
+
687
+ throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
688
+ }
689
+
690
+ private async findUserForLogin(
691
+ type: PasswordlessLoginValidateWhatSources,
692
+ identifier: string,
693
+ options: { withRoles?: boolean } = {},
694
+ ): Promise<User> {
695
+ const typeWhere = type === PasswordlessLoginValidateWhatSources.EMAIL
696
+ ? { email: identifier }
697
+ : { mobile: identifier };
698
+ const user = await this.userRepository.findOne({
699
+ where: [{ username: identifier }, typeWhere],
700
+ ...(options.withRoles ? { relations: { roles: true } } : {}),
701
+ });
702
+ if (!user) {
703
+ throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
704
+ }
705
+ if (!user.active) {
706
+ throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
707
+ }
708
+ return user;
709
+ }
710
+
711
+ private async assignLoginOtp(user: User, type: PasswordlessLoginValidateWhatSources): Promise<void> {
712
+ const { token, expiresAt } = await this.otp();
713
+ if (type === PasswordlessLoginValidateWhatSources.EMAIL) {
714
+ user.emailVerificationTokenOnLogin = token;
715
+ user.emailVerificationTokenOnLoginExpiresAt = expiresAt;
716
+ } else {
717
717
  user.mobileVerificationTokenOnLogin = token;
718
718
  user.mobileVerificationTokenOnLoginExpiresAt = expiresAt;
719
- await this.userRepository.save(user);
720
- this.notifyUserOnOtpInititateLogin(user, RegistrationValidationSource.MOBILE);
721
- return {
722
- message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_LOGIN,
723
- user: {
724
- mobile: this.maskMobile(user.mobile)
725
- }
726
- };
727
- }
728
- else {
729
- throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
730
719
  }
720
+ await this.userRepository.save(user);
721
+ }
722
+
723
+ private buildLoginOtpResponse(user: User, type: PasswordlessLoginValidateWhatSources) {
724
+ const maskedIdentifier = type === PasswordlessLoginValidateWhatSources.EMAIL
725
+ ? { email: this.maskEmail(user.email) }
726
+ : { mobile: this.maskMobile(user.mobile) };
727
+ return { message: SUCCESS_MESSAGES.OTP_SENT_SUCCESS_LOGIN, user: maskedIdentifier };
731
728
  }
732
729
 
733
- private async notifyUserOnOtpInititateLogin(user: User, loginType: RegistrationValidationSource) {
730
+ private async notifyUserOnOtpInititateLogin(user: User, loginType: PasswordlessLoginValidateWhatSources) {
734
731
  const companyLogo = await this.getCompanyLogo();
735
732
  const dummyOtp = this.settingService.getConfigValue<SolidCoreSetting>('dummyOtp');
736
733
 
737
734
  if (dummyOtp)
738
735
  return; // Do nothing if dummy otp is configured.
739
- if (loginType === RegistrationValidationSource.EMAIL) {
736
+ if (loginType === PasswordlessLoginValidateWhatSources.EMAIL) {
740
737
  const mailService = this.mailServiceFactory.getMailService();
741
738
  mailService.sendEmailUsingTemplate(
742
739
  user.email,
@@ -756,7 +753,7 @@ export class AuthenticationService {
756
753
  user.id
757
754
  );
758
755
  }
759
- if (loginType === RegistrationValidationSource.MOBILE) {
756
+ if (loginType === PasswordlessLoginValidateWhatSources.MOBILE) {
760
757
  const smsService = this.smsFactory.getSmsService();
761
758
  smsService.sendSMSUsingTemplate(
762
759
  user.mobile,
@@ -780,81 +777,62 @@ export class AuthenticationService {
780
777
  if (!isPasswordlessRegistrationEnabled) {
781
778
  throw new BadRequestException(ERROR_MESSAGES.PASSWORDLESS_REGISTRATION_DISABLED);
782
779
  }
783
- if (confirmSignInDto.type === RegistrationValidationSource.EMAIL) {
784
- // const user = await this.userRepository.findOne({
785
- // where: {
786
- // email: confirmSignInDto.identifier,
787
- // },
788
- // relations: ['roles']
789
- // });
790
- const user = await this.userRepository.findOne({
791
- where: [
792
- { username: confirmSignInDto.identifier },
793
- { email: confirmSignInDto.identifier },
794
- ],
795
- relations: {
796
- roles: true
797
- }
798
- });
799
- if (!user) {
800
- throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
801
- }
802
- if (!user.active) {
803
- throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
804
- }
805
- if (user.emailVerificationTokenOnLogin !== confirmSignInDto.otp) {
806
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
807
- }
808
- if (user.emailVerificationTokenOnLoginExpiresAt < new Date()) {
809
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
810
- }
780
+
781
+ const { type, identifier, otp } = confirmSignInDto;
782
+ if (type !== PasswordlessLoginValidateWhatSources.EMAIL &&
783
+ type !== PasswordlessLoginValidateWhatSources.MOBILE) {
784
+ throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
785
+ }
786
+
787
+ const user = await this.findUserForLogin(type, identifier, { withRoles: true });
788
+ this.checkAccountBlocked(user);
789
+ try {
790
+ this.validateLoginOtp(user, otp, type);
791
+ } catch (e) {
792
+ await this.incrementFailedAttempts(user);
793
+ throw e;
794
+ }
795
+
796
+ // we do not need to clear the otp when dummy otp is configured...
797
+ const dummyOtp = this.settingService.getConfigValue<SolidCoreSetting>('dummyOtp');
798
+ if (!dummyOtp)
799
+ this.clearLoginOtp(user, type);
800
+
801
+ user.failedLoginAttempts = 0;
802
+ await this.userRepository.save(user);
803
+ return this.buildLoginTokenResponse(user);
804
+ }
805
+
806
+ private validateLoginOtp(user: User, otp: string, type: PasswordlessLoginValidateWhatSources): void {
807
+ const isEmail = type === PasswordlessLoginValidateWhatSources.EMAIL;
808
+ const token = isEmail ? user.emailVerificationTokenOnLogin : user.mobileVerificationTokenOnLogin;
809
+ const expiresAt = isEmail ? user.emailVerificationTokenOnLoginExpiresAt : user.mobileVerificationTokenOnLoginExpiresAt;
810
+
811
+ if (token !== otp) {
812
+ throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
813
+ }
814
+ if (expiresAt < new Date()) {
815
+ throw new UnauthorizedException(ERROR_MESSAGES.OTP_EXPIRED);
816
+ }
817
+ }
818
+
819
+ private clearLoginOtp(user: User, type: PasswordlessLoginValidateWhatSources): void {
820
+ if (type === PasswordlessLoginValidateWhatSources.EMAIL) {
811
821
  user.emailVerifiedOnLoginAt = new Date();
812
822
  user.emailVerificationTokenOnLogin = null;
813
823
  user.emailVerificationTokenOnLoginExpiresAt = null;
814
- await this.userRepository.save(user);
815
- const { accessToken, refreshToken } = await this.generateTokens(user);
816
- const { id, username, email, mobile, lastLoginProvider } = user;
817
- const roles = user.roles.map((role) => role.name);
818
- return { accessToken, refreshToken, user: { id, username, email, mobile, lastLoginProvider, roles } };
819
- } else if (confirmSignInDto.type === RegistrationValidationSource.MOBILE) {
820
- // const user = await this.userRepository.findOne({
821
- // where: {
822
- // mobile: confirmSignInDto.identifier,
823
- // },
824
- // relations: ['roles']
825
- // });
826
- const user = await this.userRepository.findOne({
827
- where: [
828
- { username: confirmSignInDto.identifier },
829
- { mobile: confirmSignInDto.identifier },
830
- ],
831
- relations: {
832
- roles: true
833
- }
834
- });
835
- if (!user) {
836
- throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_ACTIVE);
837
- }
838
- if (!user.active) {
839
- throw new UnauthorizedException(ERROR_MESSAGES.USER_INACTIVE);
840
- }
841
- if (user.mobileVerificationTokenOnLogin !== confirmSignInDto.otp) {
842
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
843
- }
844
- if (user.mobileVerificationTokenOnLoginExpiresAt < new Date()) {
845
- throw new UnauthorizedException(ERROR_MESSAGES.INVALID_OTP);
846
- }
824
+ } else {
847
825
  user.mobileVerifiedOnLoginAt = new Date();
848
826
  user.mobileVerificationTokenOnLogin = null;
849
827
  user.mobileVerificationTokenOnLoginExpiresAt = null;
850
- await this.userRepository.save(user);
851
- const { accessToken, refreshToken } = await this.generateTokens(user);
852
- const { id, username, email, mobile, lastLoginProvider } = user;
853
- const roles = user.roles.map((role) => role.name);
854
- return { accessToken, refreshToken, user: { id, username, email, mobile, lastLoginProvider, roles } };
855
-
856
828
  }
857
- throw new BadRequestException(ERROR_MESSAGES.INVALID_VERIFICATION_TYPE);
829
+ }
830
+
831
+ private async buildLoginTokenResponse(user: User) {
832
+ const { accessToken, refreshToken } = await this.generateTokens(user);
833
+ const { id, username, email, mobile, lastLoginProvider } = user;
834
+ const roles = user.roles.map((role) => role.name);
835
+ return { accessToken, refreshToken, user: { id, username, email, mobile, lastLoginProvider, roles } };
858
836
  }
859
837
 
860
838
  async changePassword(changePasswordDto: ChangePasswordDto, activeUser: ActiveUserData) {
@@ -1251,11 +1229,19 @@ export class AuthenticationService {
1251
1229
  }
1252
1230
  });
1253
1231
 
1254
- // Validate the user against the Google oauth provider.
1255
- // If the below call finishes without raising an exception then we have validated the user properly.
1256
- await this.validateUserUsingGoogle(user);
1232
+ if (!user) {
1233
+ throw new UnauthorizedException(ERROR_MESSAGES.USER_NOT_FOUND);
1234
+ }
1235
+ this.checkAccountBlocked(user);
1236
+
1237
+ try {
1238
+ await this.validateUserUsingGoogle(user);
1239
+ } catch (e) {
1240
+ await this.incrementFailedAttempts(user);
1241
+ throw e;
1242
+ }
1257
1243
 
1258
- // finally we simply generate the tokens.
1244
+ await this.resetFailedAttempts(user);
1259
1245
  const tokens = await this.generateTokens(user);
1260
1246
  return {
1261
1247
  user: {
@@ -1275,6 +1261,24 @@ export class AuthenticationService {
1275
1261
  return this.settingService.getConfigValue<SolidCoreSetting>('passwordLessAuth');
1276
1262
  }
1277
1263
 
1264
+ private checkAccountBlocked(user: User): void {
1265
+ const maxFailedAttempts = this.settingService.getConfigValue<SolidCoreSetting>('maxFailedLoginAttempts') as number;
1266
+ if (maxFailedAttempts > 0 && user.failedLoginAttempts >= maxFailedAttempts) {
1267
+ throw new ForbiddenException(ERROR_MESSAGES.ACCOUNT_BLOCKED);
1268
+ }
1269
+ }
1270
+
1271
+ private async incrementFailedAttempts(user: User): Promise<void> {
1272
+ user.failedLoginAttempts += 1;
1273
+ await this.userRepository.save(user);
1274
+ }
1275
+
1276
+ private async resetFailedAttempts(user: User): Promise<void> {
1277
+ if (user.failedLoginAttempts === 0) return;
1278
+ user.failedLoginAttempts = 0;
1279
+ await this.userRepository.save(user);
1280
+ }
1281
+
1278
1282
  //FIXME - Pending implementation
1279
1283
  // async logout() {
1280
1284
  // // const user = this.request.user; //TODO: // Access the user from the execution context