@flusys/nestjs-auth 7.1.0 → 8.0.0-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 (270) hide show
  1. package/README.md +1 -1
  2. package/config/index.d.ts +3 -3
  3. package/config/message-keys.d.ts +2 -0
  4. package/controllers/authentication.controller.d.ts +5 -5
  5. package/controllers/branch.controller.d.ts +2 -2
  6. package/controllers/company-selection.controller.d.ts +4 -4
  7. package/controllers/company.controller.d.ts +2 -2
  8. package/controllers/email-verification.controller.d.ts +6 -6
  9. package/controllers/index.d.ts +9 -9
  10. package/controllers/social-auth-config.controller.d.ts +2 -2
  11. package/controllers/social-auth.controller.d.ts +3 -3
  12. package/controllers/user-permission.controller.d.ts +5 -4
  13. package/controllers/user.controller.d.ts +7 -7
  14. package/docs/auth-swagger.config.d.ts +2 -2
  15. package/docs/index.d.ts +1 -1
  16. package/dtos/index.d.ts +6 -6
  17. package/dtos/social-auth.dto.d.ts +1 -1
  18. package/entities/company-branch.entity.d.ts +1 -1
  19. package/entities/company.entity.d.ts +1 -1
  20. package/entities/index.d.ts +14 -14
  21. package/entities/pending-registration.entity.d.ts +1 -1
  22. package/entities/user-company-permission.entity.d.ts +4 -4
  23. package/enums/index.d.ts +2 -2
  24. package/fesm/118.js +1230 -0
  25. package/fesm/120.js +2197 -0
  26. package/fesm/24.js +4389 -0
  27. package/fesm/370.js +1163 -0
  28. package/fesm/446.js +877 -0
  29. package/fesm/chunks/117.js +69 -0
  30. package/fesm/config/index.js +244 -3
  31. package/fesm/controllers/index.js +27 -9
  32. package/fesm/docs/index.js +479 -1
  33. package/fesm/dtos/index.js +123 -6
  34. package/fesm/entities/index.js +68 -22
  35. package/fesm/enums/index.js +53 -2
  36. package/fesm/helpers/index.js +115 -2
  37. package/fesm/index.js +1437 -13
  38. package/fesm/interceptors/index.js +508 -2
  39. package/fesm/interfaces/index.js +75 -8
  40. package/fesm/modules/index.js +546 -1
  41. package/fesm/runtime.js +134 -0
  42. package/fesm/services/index.js +67 -11
  43. package/helpers/auth-response.mapper.d.ts +3 -3
  44. package/helpers/index.d.ts +2 -2
  45. package/helpers/token.helper.d.ts +2 -2
  46. package/index.d.ts +13 -13
  47. package/interceptors/clear-token.interceptor.d.ts +2 -2
  48. package/interceptors/index.d.ts +2 -2
  49. package/interceptors/set-token.interceptor.d.ts +2 -2
  50. package/interfaces/auth-module-options.interface.d.ts +3 -3
  51. package/interfaces/authentication.interface.d.ts +3 -3
  52. package/interfaces/company-branch.interface.d.ts +2 -2
  53. package/interfaces/company.interface.d.ts +1 -1
  54. package/interfaces/index.d.ts +8 -8
  55. package/interfaces/social-auth.interface.d.ts +2 -2
  56. package/interfaces/user-enricher.interface.d.ts +3 -3
  57. package/modules/auth.module.d.ts +4 -4
  58. package/modules/index.d.ts +1 -1
  59. package/package.json +32 -39
  60. package/providers/facebook.social-provider.d.ts +2 -2
  61. package/providers/google.social-provider.d.ts +2 -2
  62. package/providers/index.d.ts +1 -1
  63. package/providers/linkedin.social-provider.d.ts +2 -2
  64. package/providers/microsoft.social-provider.d.ts +2 -2
  65. package/providers/social-provider.registry.d.ts +2 -2
  66. package/services/auth-config.service.d.ts +3 -3
  67. package/services/auth-datasource.provider.d.ts +3 -3
  68. package/services/auth-email.service.d.ts +3 -3
  69. package/services/authentication.service.d.ts +12 -12
  70. package/services/branch.service.d.ts +7 -7
  71. package/services/company.service.d.ts +6 -6
  72. package/services/index.d.ts +11 -11
  73. package/services/social-auth-config.service.d.ts +6 -6
  74. package/services/social-auth.service.d.ts +5 -5
  75. package/services/user-permission.service.d.ts +8 -4
  76. package/services/user.service.d.ts +7 -7
  77. package/strategies/jwt.strategy.d.ts +4 -4
  78. package/validators/password.validator.d.ts +1 -1
  79. package/cjs/config/auth.constants.js +0 -49
  80. package/cjs/config/event-actions.js +0 -47
  81. package/cjs/config/index.js +0 -20
  82. package/cjs/config/message-keys.js +0 -135
  83. package/cjs/controllers/authentication.controller.js +0 -427
  84. package/cjs/controllers/authentication.controller.spec.js +0 -219
  85. package/cjs/controllers/branch.controller.js +0 -102
  86. package/cjs/controllers/branch.controller.spec.js +0 -134
  87. package/cjs/controllers/company-selection.controller.js +0 -198
  88. package/cjs/controllers/company-selection.controller.spec.js +0 -84
  89. package/cjs/controllers/company.controller.js +0 -102
  90. package/cjs/controllers/company.controller.spec.js +0 -131
  91. package/cjs/controllers/email-verification.controller.js +0 -224
  92. package/cjs/controllers/email-verification.controller.spec.js +0 -142
  93. package/cjs/controllers/index.js +0 -26
  94. package/cjs/controllers/social-auth-config.controller.js +0 -107
  95. package/cjs/controllers/social-auth.controller.js +0 -175
  96. package/cjs/controllers/social-auth.controller.spec.js +0 -79
  97. package/cjs/controllers/user-permission.controller.js +0 -223
  98. package/cjs/controllers/user-permission.controller.spec.js +0 -138
  99. package/cjs/controllers/user.controller.js +0 -438
  100. package/cjs/controllers/user.controller.spec.js +0 -204
  101. package/cjs/docs/auth-swagger.config.js +0 -461
  102. package/cjs/docs/auth-swagger.config.spec.js +0 -130
  103. package/cjs/docs/index.js +0 -18
  104. package/cjs/dtos/authentication.dto.js +0 -755
  105. package/cjs/dtos/company-branch.dto.js +0 -294
  106. package/cjs/dtos/company.dto.js +0 -298
  107. package/cjs/dtos/index.js +0 -23
  108. package/cjs/dtos/social-auth.dto.js +0 -318
  109. package/cjs/dtos/user-permission.dto.js +0 -358
  110. package/cjs/dtos/user.dto.js +0 -413
  111. package/cjs/entities/app-user.entity.js +0 -25
  112. package/cjs/entities/company-branch.entity.js +0 -163
  113. package/cjs/entities/company.entity.js +0 -146
  114. package/cjs/entities/index.js +0 -67
  115. package/cjs/entities/index.spec.js +0 -84
  116. package/cjs/entities/pending-registration.entity.js +0 -135
  117. package/cjs/entities/social-auth-config.entity.js +0 -102
  118. package/cjs/entities/user-company-permission.entity.js +0 -153
  119. package/cjs/entities/user-token.entity.js +0 -120
  120. package/cjs/enums/index.js +0 -19
  121. package/cjs/enums/social-provider-type.enum.js +0 -17
  122. package/cjs/enums/user-org-permission-type.enum.js +0 -15
  123. package/cjs/helpers/auth-response.mapper.js +0 -46
  124. package/cjs/helpers/auth-response.mapper.spec.js +0 -139
  125. package/cjs/helpers/index.js +0 -19
  126. package/cjs/helpers/token.helper.js +0 -58
  127. package/cjs/helpers/token.helper.spec.js +0 -86
  128. package/cjs/index.js +0 -30
  129. package/cjs/interceptors/clear-token.interceptor.js +0 -73
  130. package/cjs/interceptors/clear-token.interceptor.spec.js +0 -107
  131. package/cjs/interceptors/index.js +0 -19
  132. package/cjs/interceptors/set-token.interceptor.js +0 -107
  133. package/cjs/interceptors/set-token.interceptor.spec.js +0 -165
  134. package/cjs/interfaces/auth-email-provider.interface.js +0 -45
  135. package/cjs/interfaces/auth-module-options.interface.js +0 -4
  136. package/cjs/interfaces/authentication.interface.js +0 -4
  137. package/cjs/interfaces/company-branch.interface.js +0 -4
  138. package/cjs/interfaces/company.interface.js +0 -4
  139. package/cjs/interfaces/index.js +0 -25
  140. package/cjs/interfaces/social-auth.interface.js +0 -4
  141. package/cjs/interfaces/user-enricher.interface.js +0 -4
  142. package/cjs/interfaces/user.interface.js +0 -4
  143. package/cjs/modules/auth.module.js +0 -208
  144. package/cjs/modules/auth.module.spec.js +0 -258
  145. package/cjs/modules/index.js +0 -18
  146. package/cjs/providers/facebook.social-provider.js +0 -73
  147. package/cjs/providers/google.social-provider.js +0 -68
  148. package/cjs/providers/index.js +0 -18
  149. package/cjs/providers/linkedin.social-provider.js +0 -66
  150. package/cjs/providers/microsoft.social-provider.js +0 -78
  151. package/cjs/providers/oauth-http.util.js +0 -107
  152. package/cjs/providers/social-provider.registry.js +0 -24
  153. package/cjs/providers/social-provider.registry.spec.js +0 -135
  154. package/cjs/services/auth-config.service.js +0 -113
  155. package/cjs/services/auth-config.service.spec.js +0 -100
  156. package/cjs/services/auth-datasource.provider.js +0 -138
  157. package/cjs/services/auth-datasource.provider.spec.js +0 -191
  158. package/cjs/services/auth-email.service.js +0 -539
  159. package/cjs/services/auth-email.service.spec.js +0 -456
  160. package/cjs/services/authentication.service.js +0 -1203
  161. package/cjs/services/authentication.service.spec.js +0 -1020
  162. package/cjs/services/branch.service.js +0 -153
  163. package/cjs/services/branch.service.spec.js +0 -169
  164. package/cjs/services/company-selection-session.service.js +0 -121
  165. package/cjs/services/company-selection-session.service.spec.js +0 -90
  166. package/cjs/services/company.service.js +0 -140
  167. package/cjs/services/company.service.spec.js +0 -127
  168. package/cjs/services/index.js +0 -28
  169. package/cjs/services/social-auth-config.service.js +0 -103
  170. package/cjs/services/social-auth-config.service.spec.js +0 -71
  171. package/cjs/services/social-auth.service.js +0 -310
  172. package/cjs/services/social-auth.service.spec.js +0 -310
  173. package/cjs/services/user-permission.service.js +0 -236
  174. package/cjs/services/user-permission.service.spec.js +0 -254
  175. package/cjs/services/user.service.js +0 -671
  176. package/cjs/services/user.service.spec.js +0 -718
  177. package/cjs/strategies/jwt.strategy.js +0 -140
  178. package/cjs/strategies/jwt.strategy.spec.js +0 -166
  179. package/cjs/validators/password.validator.js +0 -45
  180. package/cjs/validators/password.validator.spec.js +0 -161
  181. package/fesm/config/auth.constants.js +0 -14
  182. package/fesm/config/event-actions.js +0 -26
  183. package/fesm/config/message-keys.js +0 -99
  184. package/fesm/controllers/authentication.controller.js +0 -417
  185. package/fesm/controllers/authentication.controller.spec.js +0 -215
  186. package/fesm/controllers/branch.controller.js +0 -92
  187. package/fesm/controllers/branch.controller.spec.js +0 -130
  188. package/fesm/controllers/company-selection.controller.js +0 -188
  189. package/fesm/controllers/company-selection.controller.spec.js +0 -80
  190. package/fesm/controllers/company.controller.js +0 -92
  191. package/fesm/controllers/company.controller.spec.js +0 -127
  192. package/fesm/controllers/email-verification.controller.js +0 -214
  193. package/fesm/controllers/email-verification.controller.spec.js +0 -138
  194. package/fesm/controllers/social-auth-config.controller.js +0 -97
  195. package/fesm/controllers/social-auth.controller.js +0 -165
  196. package/fesm/controllers/social-auth.controller.spec.js +0 -75
  197. package/fesm/controllers/user-permission.controller.js +0 -213
  198. package/fesm/controllers/user-permission.controller.spec.js +0 -134
  199. package/fesm/controllers/user.controller.js +0 -428
  200. package/fesm/controllers/user.controller.spec.js +0 -200
  201. package/fesm/docs/auth-swagger.config.js +0 -454
  202. package/fesm/docs/auth-swagger.config.spec.js +0 -126
  203. package/fesm/dtos/authentication.dto.js +0 -683
  204. package/fesm/dtos/company-branch.dto.js +0 -279
  205. package/fesm/dtos/company.dto.js +0 -283
  206. package/fesm/dtos/social-auth.dto.js +0 -282
  207. package/fesm/dtos/user-permission.dto.js +0 -339
  208. package/fesm/dtos/user.dto.js +0 -391
  209. package/fesm/entities/app-user.entity.js +0 -15
  210. package/fesm/entities/company-branch.entity.js +0 -153
  211. package/fesm/entities/company.entity.js +0 -136
  212. package/fesm/entities/index.spec.js +0 -80
  213. package/fesm/entities/pending-registration.entity.js +0 -125
  214. package/fesm/entities/social-auth-config.entity.js +0 -92
  215. package/fesm/entities/user-company-permission.entity.js +0 -136
  216. package/fesm/entities/user-token.entity.js +0 -104
  217. package/fesm/enums/social-provider-type.enum.js +0 -7
  218. package/fesm/enums/user-org-permission-type.enum.js +0 -5
  219. package/fesm/helpers/auth-response.mapper.js +0 -25
  220. package/fesm/helpers/auth-response.mapper.spec.js +0 -135
  221. package/fesm/helpers/token.helper.js +0 -40
  222. package/fesm/helpers/token.helper.spec.js +0 -82
  223. package/fesm/interceptors/clear-token.interceptor.js +0 -63
  224. package/fesm/interceptors/clear-token.interceptor.spec.js +0 -103
  225. package/fesm/interceptors/set-token.interceptor.js +0 -98
  226. package/fesm/interceptors/set-token.interceptor.spec.js +0 -161
  227. package/fesm/interfaces/auth-email-provider.interface.js +0 -38
  228. package/fesm/interfaces/auth-module-options.interface.js +0 -3
  229. package/fesm/interfaces/authentication.interface.js +0 -1
  230. package/fesm/interfaces/company-branch.interface.js +0 -1
  231. package/fesm/interfaces/company.interface.js +0 -1
  232. package/fesm/interfaces/social-auth.interface.js +0 -1
  233. package/fesm/interfaces/user-enricher.interface.js +0 -4
  234. package/fesm/interfaces/user.interface.js +0 -1
  235. package/fesm/modules/auth.module.js +0 -198
  236. package/fesm/modules/auth.module.spec.js +0 -254
  237. package/fesm/providers/facebook.social-provider.js +0 -63
  238. package/fesm/providers/google.social-provider.js +0 -58
  239. package/fesm/providers/index.js +0 -1
  240. package/fesm/providers/linkedin.social-provider.js +0 -56
  241. package/fesm/providers/microsoft.social-provider.js +0 -68
  242. package/fesm/providers/oauth-http.util.js +0 -77
  243. package/fesm/providers/social-provider.registry.js +0 -14
  244. package/fesm/providers/social-provider.registry.spec.js +0 -131
  245. package/fesm/services/auth-config.service.js +0 -103
  246. package/fesm/services/auth-config.service.spec.js +0 -96
  247. package/fesm/services/auth-datasource.provider.js +0 -87
  248. package/fesm/services/auth-datasource.provider.spec.js +0 -187
  249. package/fesm/services/auth-email.service.js +0 -488
  250. package/fesm/services/auth-email.service.spec.js +0 -411
  251. package/fesm/services/authentication.service.js +0 -1152
  252. package/fesm/services/authentication.service.spec.js +0 -975
  253. package/fesm/services/branch.service.js +0 -143
  254. package/fesm/services/branch.service.spec.js +0 -165
  255. package/fesm/services/company-selection-session.service.js +0 -111
  256. package/fesm/services/company-selection-session.service.spec.js +0 -86
  257. package/fesm/services/company.service.js +0 -130
  258. package/fesm/services/company.service.spec.js +0 -123
  259. package/fesm/services/social-auth-config.service.js +0 -93
  260. package/fesm/services/social-auth-config.service.spec.js +0 -67
  261. package/fesm/services/social-auth.service.js +0 -300
  262. package/fesm/services/social-auth.service.spec.js +0 -265
  263. package/fesm/services/user-permission.service.js +0 -226
  264. package/fesm/services/user-permission.service.spec.js +0 -250
  265. package/fesm/services/user.service.js +0 -620
  266. package/fesm/services/user.service.spec.js +0 -673
  267. package/fesm/strategies/jwt.strategy.js +0 -130
  268. package/fesm/strategies/jwt.strategy.spec.js +0 -162
  269. package/fesm/validators/password.validator.js +0 -48
  270. package/fesm/validators/password.validator.spec.js +0 -157
@@ -1,1152 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- function _ts_param(paramIndex, decorator) {
24
- return function(target, key) {
25
- decorator(target, key, paramIndex);
26
- };
27
- }
28
- import { LogAction } from '@flusys/nestjs-shared';
29
- import { HybridCache, publishDomainEvent } from '@flusys/nestjs-shared/classes';
30
- import { SYSTEM_MESSAGES } from '@flusys/nestjs-shared/constants';
31
- import { NotificationType } from '@flusys/nestjs-shared/enums';
32
- import { INotificationAdapter, NOTIFICATION_ADAPTER } from '@flusys/nestjs-shared/interfaces';
33
- import { generateUniqueSlug } from '@flusys/nestjs-shared/utils';
34
- import { BadRequestException, ForbiddenException, Inject, Injectable, InternalServerErrorException, NotFoundException, Optional, Scope, UnauthorizedException } from '@nestjs/common';
35
- import { JwtService } from '@nestjs/jwt';
36
- import * as bcrypt from 'bcrypt';
37
- import { AUTH_EVENT_ACTIONS, AUTH_EVENT_ENTITIES, AUTH_EVENT_MODULE, AUTH_MESSAGES, BCRYPT_SALT_ROUNDS, EMAIL_VERIFICATION_MESSAGES, TOKEN_REVOKED_PREFIX, USER_ENRICHER, USER_MESSAGES } from '../config';
38
- import { RegistrationDto } from '../dtos/authentication.dto';
39
- import { AppUser } from '../entities/app-user.entity';
40
- import { CompanyBranch } from '../entities/company-branch.entity';
41
- import { Company } from '../entities/company.entity';
42
- import { PendingRegistration } from '../entities/pending-registration.entity';
43
- import { UserCompanyPermission } from '../entities/user-company-permission.entity';
44
- import { PermissionType } from '../enums';
45
- import { generateTokens, parseExpiryToMs, toBranchInfo, toCompanyInfo, toUserInfo } from '../helpers';
46
- import { IUserEnricher } from '../interfaces/user-enricher.interface';
47
- import { AuthConfigService } from './auth-config.service';
48
- import { AuthDataSourceProvider } from './auth-datasource.provider';
49
- import { AuthEmailService } from './auth-email.service';
50
- import { CompanySelectionSessionService } from './company-selection-session.service';
51
- export class AuthenticationService {
52
- isCompanyFeatureEnabled() {
53
- return this.dataSourceProvider?.isCompanyFeatureEnabled() ?? this.authConfig.isCompanyFeatureEnabled();
54
- }
55
- isEmailVerificationEnabled() {
56
- const featureEnabled = this.dataSourceProvider?.isEmailVerificationEnabled() ?? this.authConfig.isEmailVerificationEnabled();
57
- const providerAvailable = this.authEmailService?.isEmailEnabled() ?? false;
58
- return featureEnabled && providerAvailable;
59
- }
60
- isSignUpEnabled() {
61
- return this.dataSourceProvider?.isSignUpEnabled() ?? this.authConfig.isSignUpEnabled();
62
- }
63
- async getUserRepository() {
64
- if (!this.dataSourceProvider) {
65
- throw new InternalServerErrorException({
66
- message: 'User repository not available',
67
- messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
68
- messageVariables: {
69
- entity: 'AppUser'
70
- }
71
- });
72
- }
73
- return this.dataSourceProvider.getRepository(AppUser);
74
- }
75
- async getCompanyScopedRepository(entity) {
76
- if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
77
- return this.dataSourceProvider.getRepository(entity);
78
- }
79
- async login(email, password) {
80
- if (this.isEmailVerificationEnabled() && await this.authEmailService.hasPendingRegistration(email)) {
81
- return {
82
- requiresEmailVerification: true,
83
- email,
84
- message: 'Please verify your email address to complete registration.',
85
- messageKey: EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SENT
86
- };
87
- }
88
- const user = await this.validateUser(email, password);
89
- this.ensureUserActive(user);
90
- if (this.isEmailVerificationEnabled() && !user.emailVerified) {
91
- return {
92
- requiresEmailVerification: true,
93
- email: user.email,
94
- message: 'Please verify your email address before logging in.',
95
- messageKey: AUTH_MESSAGES.LOGIN_EMAIL_NOT_VERIFIED
96
- };
97
- }
98
- return this.issueLoginForUser(user);
99
- }
100
- async issueLoginForUser(user) {
101
- this.ensureUserActive(user);
102
- if (!this.isCompanyFeatureEnabled()) {
103
- return this.generateLoginResponse(user);
104
- }
105
- return await this.handleCompanyBasedLogin(user);
106
- }
107
- ensureUserActive(user) {
108
- if (!user.isActive) {
109
- throw new ForbiddenException({
110
- message: 'User account is deactivated',
111
- messageKey: AUTH_MESSAGES.LOGIN_ACCOUNT_DEACTIVATED
112
- });
113
- }
114
- }
115
- async handleCompanyBasedLogin(user) {
116
- const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
117
- const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
118
- if (!permissionRepository || !branchRepository) {
119
- return this.generateLoginResponse(user);
120
- }
121
- const companyPermissions = await permissionRepository.find({
122
- where: {
123
- userId: user.id,
124
- permissionType: PermissionType.COMPANY
125
- }
126
- });
127
- const companyRepository = await this.getCompanyScopedRepository(Company);
128
- if (!companyRepository) {
129
- return this.generateLoginResponse(user);
130
- }
131
- const companyIds = companyPermissions.map((p)=>p.targetId);
132
- const companies = companyIds.length > 0 ? await companyRepository.createQueryBuilder('c').where('c.id IN (:...ids)', {
133
- ids: companyIds
134
- }).andWhere('c.is_active = :active', {
135
- active: true
136
- }).getMany() : [];
137
- if (companies.length === 0) {
138
- return this.generateLoginResponse(user);
139
- }
140
- const branchPermissions = await permissionRepository.find({
141
- where: {
142
- userId: user.id,
143
- permissionType: PermissionType.BRANCH
144
- }
145
- });
146
- const branchIds = branchPermissions.map((p)=>p.targetId);
147
- const branches = branchIds.length > 0 ? await branchRepository.createQueryBuilder('b').leftJoinAndSelect('b.company', 'company').where('b.id IN (:...ids)', {
148
- ids: branchIds
149
- }).andWhere('b.is_active = :active', {
150
- active: true
151
- }).getMany() : [];
152
- if (companies.length === 1 && branches.length === 1) {
153
- return this.generateLoginResponseWithCompany(user, companies[0], branches[0]);
154
- }
155
- return await this.generateCompanySelectionResponse(user, companies, branches);
156
- }
157
- async register(dto, verifyUrlBase) {
158
- const userRepository = await this.getUserRepository();
159
- const existingUser = await userRepository.findOne({
160
- where: {
161
- email: dto.email
162
- },
163
- withDeleted: true
164
- });
165
- if (existingUser?.deletedAt) {
166
- throw new BadRequestException({
167
- message: 'This account has been permanently deleted. Please contact your system administrator.',
168
- messageKey: AUTH_MESSAGES.REGISTER_ACCOUNT_DELETED
169
- });
170
- }
171
- if (existingUser && this.isCompanyFeatureEnabled()) {
172
- return await this.handleExistingUserCompanyRegistration(existingUser, dto);
173
- }
174
- if (existingUser) {
175
- throw new BadRequestException({
176
- message: 'Registration failed due to email exists. Please try again or contact support.',
177
- messageKey: AUTH_MESSAGES.REGISTER_EMAIL_EXISTS
178
- });
179
- }
180
- if (!this.isSignUpEnabled()) {
181
- throw new BadRequestException({
182
- message: 'New account registration is currently disabled.',
183
- messageKey: AUTH_MESSAGES.REGISTER_SIGNUP_DISABLED
184
- });
185
- }
186
- if (this.isEmailVerificationEnabled() && verifyUrlBase) {
187
- const companyData = this.isCompanyFeatureEnabled() ? {
188
- companySlug: dto.companySlug,
189
- branchSlug: dto.branchSlug,
190
- newCompanyName: dto.newCompanyName,
191
- newCompanyPhone: dto.newCompanyPhone,
192
- newCompanyAddress: dto.newCompanyAddress
193
- } : undefined;
194
- const result = await this.authEmailService.createPendingRegistration({
195
- email: dto.email,
196
- name: dto.name,
197
- phone: dto.phone,
198
- password: dto.password,
199
- companyData,
200
- additionalFields: dto.additionalFields,
201
- verifyUrlBase
202
- });
203
- return {
204
- requiresEmailVerification: true,
205
- message: result.message,
206
- messageKey: EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SENT
207
- };
208
- }
209
- return await this.createUserDirectly(dto);
210
- }
211
- async handleExistingUserCompanyRegistration(existingUser, dto) {
212
- if (!dto.companySlug && !dto.newCompanyName) {
213
- throw new BadRequestException({
214
- message: 'Email already registered. Please login or provide a company to join/create.',
215
- messageKey: AUTH_MESSAGES.REGISTER_EMAIL_EXISTS
216
- });
217
- }
218
- const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
219
- const companyRepository = await this.getCompanyScopedRepository(Company);
220
- if (!permissionRepository || !companyRepository) {
221
- throw new BadRequestException({
222
- message: 'Company feature repositories not available',
223
- messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
224
- messageVariables: {
225
- entity: 'Company feature'
226
- }
227
- });
228
- }
229
- if (dto.companySlug) {
230
- const company = await companyRepository.findOne({
231
- where: {
232
- slug: dto.companySlug,
233
- isActive: true
234
- }
235
- });
236
- if (company) {
237
- const existingPermission = await permissionRepository.findOne({
238
- where: {
239
- userId: existingUser.id,
240
- permissionType: PermissionType.COMPANY,
241
- targetId: company.id
242
- }
243
- });
244
- if (existingPermission) {
245
- throw new BadRequestException({
246
- message: 'You are already assigned to this company. Please login.',
247
- messageKey: AUTH_MESSAGES.REGISTER_COMPANY_ALREADY_ASSIGNED
248
- });
249
- }
250
- }
251
- }
252
- return await this.processCompanyRegistration(existingUser, dto, true);
253
- }
254
- async createUserDirectly(dto) {
255
- if (!this.dataSourceProvider) {
256
- throw new BadRequestException({
257
- message: 'Data source not available',
258
- messageKey: SYSTEM_MESSAGES.DATASOURCE_NOT_AVAILABLE
259
- });
260
- }
261
- const dataSource = await this.dataSourceProvider.getDataSource();
262
- const queryRunner = dataSource.createQueryRunner();
263
- await queryRunner.connect();
264
- await queryRunner.startTransaction();
265
- try {
266
- const hashedPassword = await bcrypt.hash(dto.password, BCRYPT_SALT_ROUNDS);
267
- const user = queryRunner.manager.create(AppUser, {
268
- name: dto.name,
269
- email: dto.email,
270
- phone: dto.phone,
271
- password: hashedPassword,
272
- isActive: true,
273
- emailVerified: false,
274
- phoneVerified: false
275
- });
276
- await queryRunner.manager.save(AppUser, user);
277
- if (this.userEnricher?.onUserCreated) {
278
- await this.userEnricher.onUserCreated(user.id, dto.additionalFields ?? null, queryRunner);
279
- }
280
- await queryRunner.commitTransaction();
281
- if (!this.isCompanyFeatureEnabled()) {
282
- return this.generateRegistrationResponse(user);
283
- }
284
- return await this.processCompanyRegistration(user, dto);
285
- } catch (error) {
286
- await queryRunner.rollbackTransaction();
287
- if (error instanceof BadRequestException) {
288
- throw error;
289
- }
290
- throw new BadRequestException({
291
- message: 'Registration failed. Please try again.',
292
- messageKey: AUTH_MESSAGES.REGISTER_FAILED
293
- });
294
- } finally{
295
- await queryRunner.release();
296
- }
297
- }
298
- async createUserFromPendingRegistration(pending) {
299
- if (!this.dataSourceProvider) {
300
- throw new BadRequestException({
301
- message: 'Data source not available',
302
- messageKey: SYSTEM_MESSAGES.DATASOURCE_NOT_AVAILABLE
303
- });
304
- }
305
- const dataSource = await this.dataSourceProvider.getDataSource();
306
- const queryRunner = dataSource.createQueryRunner();
307
- await queryRunner.connect();
308
- await queryRunner.startTransaction();
309
- try {
310
- const existingUser = await queryRunner.manager.findOne(AppUser, {
311
- where: {
312
- email: pending.email
313
- },
314
- withDeleted: true
315
- });
316
- if (existingUser?.deletedAt) {
317
- throw new BadRequestException({
318
- message: 'This account has been permanently deleted. Please contact your system administrator.',
319
- messageKey: AUTH_MESSAGES.REGISTER_ACCOUNT_DELETED
320
- });
321
- }
322
- if (existingUser) {
323
- throw new BadRequestException({
324
- message: 'This email has already been registered.',
325
- messageKey: AUTH_MESSAGES.REGISTER_EMAIL_EXISTS
326
- });
327
- }
328
- const user = queryRunner.manager.create(AppUser, {
329
- name: pending.name,
330
- email: pending.email,
331
- phone: pending.phone,
332
- password: pending.passwordHash,
333
- isActive: true,
334
- emailVerified: true,
335
- phoneVerified: false
336
- });
337
- await queryRunner.manager.save(AppUser, user);
338
- if (this.userEnricher?.onUserCreated) {
339
- await this.userEnricher.onUserCreated(user.id, pending.additionalFields ?? null, queryRunner);
340
- }
341
- await queryRunner.commitTransaction();
342
- if (this.isCompanyFeatureEnabled() && pending.companyData) {
343
- return await this.processCompanyRegistration(user, pending.companyData);
344
- }
345
- return this.generateRegistrationResponse(user);
346
- } catch (error) {
347
- await queryRunner.rollbackTransaction();
348
- if (error instanceof BadRequestException) {
349
- throw error;
350
- }
351
- throw new BadRequestException({
352
- message: 'Registration failed. Please try again.',
353
- messageKey: AUTH_MESSAGES.REGISTER_FAILED
354
- });
355
- } finally{
356
- await queryRunner.release();
357
- }
358
- }
359
- async processCompanyRegistration(user, data, throwOnUnavailable = false) {
360
- const companyRepository = await this.getCompanyScopedRepository(Company);
361
- const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
362
- const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
363
- if (!companyRepository || !branchRepository || !permissionRepository) {
364
- if (throwOnUnavailable) throw new BadRequestException({
365
- message: 'Company feature repositories not available',
366
- messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
367
- messageVariables: {
368
- entity: 'Company feature'
369
- }
370
- });
371
- return this.generateRegistrationResponse(user);
372
- }
373
- const { company, branch } = await this.findOrCreateCompanyAndBranch(data, companyRepository, branchRepository);
374
- if (company) {
375
- await this.assignUserPermissions(user.id, company.id, branch?.id, permissionRepository);
376
- return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined);
377
- }
378
- return this.generateRegistrationResponse(user);
379
- }
380
- async findOrCreateCompanyAndBranch(data, companyRepo, branchRepo) {
381
- if (data.companySlug) {
382
- const company = await companyRepo.findOne({
383
- where: {
384
- slug: data.companySlug,
385
- isActive: true
386
- }
387
- });
388
- if (!company) throw new BadRequestException({
389
- message: 'Company not found or inactive',
390
- messageKey: AUTH_MESSAGES.COMPANY_NOT_FOUND
391
- });
392
- const branch = data.branchSlug ? await branchRepo.findOne({
393
- where: {
394
- slug: data.branchSlug,
395
- companyId: company.id,
396
- isActive: true
397
- }
398
- }) : await branchRepo.findOne({
399
- where: {
400
- companyId: company.id,
401
- isActive: true
402
- },
403
- order: {
404
- serial: 'ASC',
405
- createdAt: 'ASC'
406
- }
407
- });
408
- if (data.branchSlug && !branch) throw new BadRequestException({
409
- message: 'Branch not found or inactive',
410
- messageKey: AUTH_MESSAGES.BRANCH_NOT_FOUND
411
- });
412
- return {
413
- company,
414
- branch
415
- };
416
- }
417
- if (data.newCompanyName) {
418
- const slug = await generateUniqueSlug(data.newCompanyName, async (baseSlug)=>{
419
- const rows = await companyRepo.createQueryBuilder('c').select('c.slug').where('c.slug = :base OR c.slug LIKE :pattern', {
420
- base: baseSlug,
421
- pattern: `${baseSlug}-%`
422
- }).getMany();
423
- return rows.map((r)=>r.slug);
424
- });
425
- const company = companyRepo.create({
426
- name: data.newCompanyName,
427
- slug,
428
- phone: data.newCompanyPhone,
429
- address: data.newCompanyAddress,
430
- isActive: true
431
- });
432
- await companyRepo.save(company);
433
- const branch = branchRepo.create({
434
- name: 'Main Branch',
435
- slug: 'main-branch',
436
- companyId: company.id,
437
- isActive: true
438
- });
439
- await branchRepo.save(branch);
440
- return {
441
- company,
442
- branch
443
- };
444
- }
445
- return {
446
- company: null,
447
- branch: null
448
- };
449
- }
450
- async assignUserPermissions(userId, companyId, branchId, permissionRepo) {
451
- await permissionRepo.save(permissionRepo.create({
452
- userId,
453
- permissionType: PermissionType.COMPANY,
454
- targetId: companyId
455
- }));
456
- if (branchId) {
457
- await permissionRepo.save(permissionRepo.create({
458
- userId,
459
- permissionType: PermissionType.BRANCH,
460
- targetId: branchId
461
- }));
462
- }
463
- }
464
- async select(sessionId, companyId, branchId) {
465
- this.ensureCompanyFeatureEnabled();
466
- if (!this.sessionService) {
467
- throw new BadRequestException({
468
- message: 'Session service not available',
469
- messageKey: AUTH_MESSAGES.SESSION_NOT_AVAILABLE
470
- });
471
- }
472
- const session = await this.sessionService.consumeSession(sessionId);
473
- if (!session) {
474
- throw new UnauthorizedException({
475
- message: 'Invalid or expired session',
476
- messageKey: AUTH_MESSAGES.SESSION_INVALID
477
- });
478
- }
479
- return await this.validateAndLoginWithCompany(session.userId, companyId, branchId);
480
- }
481
- async switchCompany(userId, companyId, branchId) {
482
- this.ensureCompanyFeatureEnabled();
483
- return await this.validateAndLoginWithCompany(userId, companyId, branchId);
484
- }
485
- async validateAndLoginWithCompany(userId, companyId, branchId) {
486
- const userRepository = await this.getUserRepository();
487
- const user = await userRepository.findOne({
488
- where: {
489
- id: userId
490
- }
491
- });
492
- if (!user) {
493
- throw new NotFoundException({
494
- message: 'User not found',
495
- messageKey: USER_MESSAGES.NOT_FOUND
496
- });
497
- }
498
- const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
499
- const companyRepository = await this.getCompanyScopedRepository(Company);
500
- const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
501
- if (!permissionRepository || !companyRepository || !branchRepository) {
502
- throw new BadRequestException({
503
- message: 'Repositories not available',
504
- messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
505
- messageVariables: {
506
- entity: 'Some'
507
- }
508
- });
509
- }
510
- const hasCompanyAccess = await permissionRepository.findOne({
511
- where: {
512
- userId,
513
- permissionType: PermissionType.COMPANY,
514
- targetId: companyId
515
- }
516
- });
517
- if (!hasCompanyAccess) {
518
- throw new ForbiddenException({
519
- message: 'No access to this company',
520
- messageKey: AUTH_MESSAGES.COMPANY_NO_ACCESS
521
- });
522
- }
523
- const company = await companyRepository.findOne({
524
- where: {
525
- id: companyId,
526
- isActive: true
527
- }
528
- });
529
- if (!company) {
530
- throw new NotFoundException({
531
- message: 'Company not found or inactive',
532
- messageKey: AUTH_MESSAGES.COMPANY_NOT_FOUND
533
- });
534
- }
535
- const hasBranchAccess = await permissionRepository.findOne({
536
- where: {
537
- userId,
538
- permissionType: PermissionType.BRANCH,
539
- targetId: branchId
540
- }
541
- });
542
- if (!hasBranchAccess) {
543
- throw new ForbiddenException({
544
- message: 'No access to this branch',
545
- messageKey: AUTH_MESSAGES.BRANCH_NO_ACCESS
546
- });
547
- }
548
- const branch = await branchRepository.findOne({
549
- where: {
550
- id: branchId,
551
- companyId,
552
- isActive: true
553
- }
554
- });
555
- if (!branch) {
556
- throw new NotFoundException({
557
- message: 'Branch not found or inactive',
558
- messageKey: AUTH_MESSAGES.BRANCH_NOT_FOUND
559
- });
560
- }
561
- const response = this.generateLoginResponseWithCompany(user, company, branch);
562
- this.publishAuthEvent(AUTH_EVENT_ENTITIES.SESSION, AUTH_EVENT_ACTIONS.COMPANY_SWITCHED, user, {
563
- companyId: company.id,
564
- branchId: branch.id
565
- });
566
- return response;
567
- }
568
- async refreshToken(refreshToken) {
569
- try {
570
- const payload = this.jwtService.verify(refreshToken, {
571
- secret: this.authConfig.getRefreshTokenSecret(),
572
- algorithms: [
573
- 'HS256'
574
- ]
575
- });
576
- if (await this.isTokenRevoked(payload.id, payload.iat)) {
577
- throw new UnauthorizedException({
578
- message: 'Token has been revoked',
579
- messageKey: AUTH_MESSAGES.TOKEN_REVOKED
580
- });
581
- }
582
- const userRepository = await this.getUserRepository();
583
- const user = await userRepository.findOne({
584
- where: {
585
- id: payload.id
586
- }
587
- });
588
- if (!user || !user.isActive) {
589
- throw new UnauthorizedException({
590
- message: 'Invalid refresh token',
591
- messageKey: AUTH_MESSAGES.TOKEN_INVALID
592
- });
593
- }
594
- if (payload.companyId) {
595
- const companyRepository = await this.getCompanyScopedRepository(Company);
596
- const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
597
- if (companyRepository) {
598
- const company = await companyRepository.findOne({
599
- where: {
600
- id: payload.companyId,
601
- isActive: true
602
- }
603
- });
604
- if (!company) {
605
- throw new UnauthorizedException({
606
- message: 'Company not found or inactive',
607
- messageKey: AUTH_MESSAGES.COMPANY_NOT_FOUND
608
- });
609
- }
610
- }
611
- if (permissionRepository) {
612
- const hasCompanyAccess = await permissionRepository.findOne({
613
- where: {
614
- userId: user.id,
615
- permissionType: PermissionType.COMPANY,
616
- targetId: payload.companyId
617
- }
618
- });
619
- if (!hasCompanyAccess) {
620
- throw new UnauthorizedException({
621
- message: 'No access to this company',
622
- messageKey: AUTH_MESSAGES.COMPANY_NO_ACCESS
623
- });
624
- }
625
- }
626
- if (payload.branchId) {
627
- const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
628
- if (branchRepository) {
629
- const branch = await branchRepository.findOne({
630
- where: {
631
- id: payload.branchId,
632
- companyId: payload.companyId,
633
- isActive: true
634
- }
635
- });
636
- if (!branch) {
637
- throw new UnauthorizedException({
638
- message: 'Branch not found or inactive',
639
- messageKey: AUTH_MESSAGES.BRANCH_NOT_FOUND
640
- });
641
- }
642
- }
643
- if (permissionRepository) {
644
- const hasBranchAccess = await permissionRepository.findOne({
645
- where: {
646
- userId: user.id,
647
- permissionType: PermissionType.BRANCH,
648
- targetId: payload.branchId
649
- }
650
- });
651
- if (!hasBranchAccess) {
652
- throw new UnauthorizedException({
653
- message: 'No access to this branch',
654
- messageKey: AUTH_MESSAGES.BRANCH_NO_ACCESS
655
- });
656
- }
657
- }
658
- }
659
- }
660
- const tokens = generateTokens(this.jwtService, this.authConfig, {
661
- id: user.id,
662
- email: user.email,
663
- type: 'access',
664
- profilePictureId: user.profilePictureId ?? undefined,
665
- ...payload.companyId && {
666
- companyId: payload.companyId
667
- },
668
- ...payload.branchId && {
669
- branchId: payload.branchId
670
- }
671
- });
672
- this.publishAuthEvent(AUTH_EVENT_ENTITIES.SESSION, AUTH_EVENT_ACTIONS.TOKEN_REFRESHED, user, {
673
- companyId: payload.companyId ?? null,
674
- branchId: payload.branchId ?? null
675
- });
676
- return tokens;
677
- } catch {
678
- throw new UnauthorizedException({
679
- message: 'Invalid or expired refresh token',
680
- messageKey: AUTH_MESSAGES.TOKEN_EXPIRED
681
- });
682
- }
683
- }
684
- async changePassword(userId, currentPassword, newPassword) {
685
- const userRepository = await this.getUserRepository();
686
- const user = await userRepository.findOne({
687
- where: {
688
- id: userId
689
- }
690
- });
691
- if (!user) throw new UnauthorizedException({
692
- message: 'User not found',
693
- messageKey: USER_MESSAGES.NOT_FOUND
694
- });
695
- if (!await bcrypt.compare(currentPassword, user.password || '')) {
696
- throw new UnauthorizedException({
697
- message: 'Current password is incorrect',
698
- messageKey: AUTH_MESSAGES.PASSWORD_CHANGE_INVALID_CURRENT
699
- });
700
- }
701
- user.password = await bcrypt.hash(newPassword, BCRYPT_SALT_ROUNDS);
702
- await userRepository.save(user);
703
- this.publishAuthEvent(AUTH_EVENT_ENTITIES.USER, AUTH_EVENT_ACTIONS.PASSWORD_CHANGED, user);
704
- }
705
- async logout(userId) {
706
- if (!this.cacheManager) {
707
- throw new BadRequestException({
708
- message: 'Logout service temporarily unavailable. Please try again later.',
709
- messageKey: AUTH_MESSAGES.LOGOUT_SERVICE_UNAVAILABLE
710
- });
711
- }
712
- const revokedAt = Math.floor(Date.now() / 1000);
713
- const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
714
- const refreshTokenExpiry = this.authConfig.getRefreshTokenExpiration();
715
- const ttlMs = parseExpiryToMs(refreshTokenExpiry);
716
- await this.cacheManager.set(cacheKey, revokedAt, ttlMs);
717
- await publishDomainEvent({
718
- module: AUTH_EVENT_MODULE,
719
- entity: AUTH_EVENT_ENTITIES.SESSION,
720
- action: AUTH_EVENT_ACTIONS.LOGGED_OUT,
721
- ids: [
722
- userId
723
- ],
724
- user: {
725
- id: userId
726
- }
727
- });
728
- }
729
- async isTokenRevoked(userId, tokenIssuedAt) {
730
- if (!this.cacheManager || !tokenIssuedAt) {
731
- return false;
732
- }
733
- const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
734
- const revokedAt = await this.cacheManager.get(cacheKey);
735
- if (!revokedAt) {
736
- return false;
737
- }
738
- return tokenIssuedAt < revokedAt;
739
- }
740
- async getUserCompanies(userId) {
741
- this.ensureCompanyFeatureEnabled();
742
- const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
743
- const companyRepository = await this.getCompanyScopedRepository(Company);
744
- if (!permissionRepository || !companyRepository) {
745
- throw new BadRequestException({
746
- message: 'Repositories not available',
747
- messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
748
- messageVariables: {
749
- entity: 'Some'
750
- }
751
- });
752
- }
753
- const permissions = await permissionRepository.find({
754
- where: {
755
- userId,
756
- permissionType: PermissionType.COMPANY
757
- }
758
- });
759
- const companyIds = permissions.map((p)=>p.targetId);
760
- if (companyIds.length === 0) return [];
761
- return companyRepository.createQueryBuilder('c').where('c.id IN (:...ids)', {
762
- ids: companyIds
763
- }).andWhere('c.is_active = :active', {
764
- active: true
765
- }).orderBy('c.name', 'ASC').getMany();
766
- }
767
- async getUserBranches(userId, companyId) {
768
- this.ensureCompanyFeatureEnabled();
769
- const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
770
- const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
771
- if (!permissionRepository || !branchRepository) {
772
- throw new BadRequestException({
773
- message: 'Repositories not available',
774
- messageKey: SYSTEM_MESSAGES.REPOSITORY_NOT_AVAILABLE,
775
- messageVariables: {
776
- entity: 'Some'
777
- }
778
- });
779
- }
780
- const companyPermission = await permissionRepository.findOne({
781
- where: {
782
- userId,
783
- permissionType: PermissionType.COMPANY,
784
- targetId: companyId
785
- }
786
- });
787
- if (!companyPermission) {
788
- throw new UnauthorizedException({
789
- message: 'No access to this company',
790
- messageKey: AUTH_MESSAGES.COMPANY_NO_ACCESS
791
- });
792
- }
793
- const branchPermissions = await permissionRepository.find({
794
- where: {
795
- userId,
796
- permissionType: PermissionType.BRANCH
797
- }
798
- });
799
- const branchIds = branchPermissions.map((p)=>p.targetId);
800
- if (branchIds.length === 0) return [];
801
- return branchRepository.createQueryBuilder('b').where('b.id IN (:...ids)', {
802
- ids: branchIds
803
- }).andWhere('b.company_id = :companyId', {
804
- companyId
805
- }).andWhere('b.is_active = :active', {
806
- active: true
807
- }).orderBy('b.serial', 'ASC').addOrderBy('b.name', 'ASC').getMany();
808
- }
809
- async getCompanyById(companyId) {
810
- const companyRepository = await this.getCompanyScopedRepository(Company);
811
- if (!companyRepository) {
812
- return null;
813
- }
814
- return companyRepository.findOne({
815
- where: {
816
- id: companyId
817
- }
818
- });
819
- }
820
- async getBranchById(branchId) {
821
- const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
822
- if (!branchRepository) {
823
- return null;
824
- }
825
- return branchRepository.findOne({
826
- where: {
827
- id: branchId
828
- }
829
- });
830
- }
831
- async getUserInfo(userId, companyId, branchId) {
832
- const userRepository = await this.getUserRepository();
833
- const user = await userRepository.findOne({
834
- where: {
835
- id: userId
836
- }
837
- });
838
- if (!user) throw new UnauthorizedException({
839
- message: 'User not found',
840
- messageKey: USER_MESSAGES.NOT_FOUND
841
- });
842
- const response = {
843
- user: toUserInfo(user)
844
- };
845
- if (this.isCompanyFeatureEnabled()) {
846
- if (!companyId) {
847
- throw new UnauthorizedException({
848
- message: 'Company feature is enabled but user has no company assigned.',
849
- messageKey: AUTH_MESSAGES.COMPANY_REQUIRED
850
- });
851
- }
852
- const company = await this.getCompanyById(companyId);
853
- if (!company) throw new UnauthorizedException({
854
- message: 'Company not found.',
855
- messageKey: AUTH_MESSAGES.COMPANY_NOT_FOUND
856
- });
857
- response.company = toCompanyInfo(company);
858
- if (branchId) {
859
- const branch = await this.getBranchById(branchId);
860
- if (!branch) throw new UnauthorizedException({
861
- message: 'Branch not found.',
862
- messageKey: AUTH_MESSAGES.BRANCH_NOT_FOUND
863
- });
864
- response.branch = toBranchInfo(branch);
865
- }
866
- }
867
- return response;
868
- }
869
- ensureCompanyFeatureEnabled() {
870
- if (!this.isCompanyFeatureEnabled()) {
871
- throw new BadRequestException({
872
- message: 'Company feature is not enabled',
873
- messageKey: AUTH_MESSAGES.COMPANY_FEATURE_NOT_ENABLED
874
- });
875
- }
876
- }
877
- async validateUser(email, password) {
878
- const userRepository = await this.getUserRepository();
879
- const user = await userRepository.findOne({
880
- where: {
881
- email
882
- }
883
- });
884
- if (!user || !await bcrypt.compare(password, user.password || '')) {
885
- throw new UnauthorizedException({
886
- message: 'Invalid credentials',
887
- messageKey: AUTH_MESSAGES.LOGIN_INVALID_CREDENTIALS
888
- });
889
- }
890
- user.lastLoginAt = new Date();
891
- await userRepository.save(user);
892
- return user;
893
- }
894
- generateLoginResponse(user) {
895
- const { accessToken, refreshToken } = generateTokens(this.jwtService, this.authConfig, {
896
- id: user.id,
897
- email: user.email,
898
- type: 'access'
899
- });
900
- this.sendLoginNotification(user.id);
901
- this.publishAuthEvent(AUTH_EVENT_ENTITIES.SESSION, AUTH_EVENT_ACTIONS.LOGGED_IN, user);
902
- return {
903
- accessToken,
904
- refreshToken,
905
- user: toUserInfo(user)
906
- };
907
- }
908
- generateLoginResponseWithCompany(user, company, branch) {
909
- const { accessToken, refreshToken } = generateTokens(this.jwtService, this.authConfig, {
910
- id: user.id,
911
- email: user.email,
912
- type: 'access',
913
- companyId: company.id,
914
- branchId: branch?.id
915
- });
916
- this.sendLoginNotification(user.id, company.id);
917
- this.publishAuthEvent(AUTH_EVENT_ENTITIES.SESSION, AUTH_EVENT_ACTIONS.LOGGED_IN, user, {
918
- companyId: company.id,
919
- branchId: branch?.id ?? null
920
- });
921
- return {
922
- accessToken,
923
- refreshToken,
924
- user: toUserInfo(user),
925
- company: toCompanyInfo(company),
926
- branch: branch ? toBranchInfo(branch) : undefined
927
- };
928
- }
929
- sendLoginNotification(userId, companyId) {
930
- if (!this.notificationAdapter) return;
931
- this.notificationAdapter.send({
932
- userId,
933
- title: 'Login successful',
934
- message: 'You have successfully logged in to your account.',
935
- type: NotificationType.INFO,
936
- companyId,
937
- realtime: false
938
- }).catch(()=>{
939
- // Silent failure - notification is non-critical
940
- });
941
- }
942
- async generateCompanySelectionResponse(user, companies, branches) {
943
- if (!this.sessionService) {
944
- throw new BadRequestException({
945
- message: 'Session service not available',
946
- messageKey: AUTH_MESSAGES.SESSION_NOT_AVAILABLE
947
- });
948
- }
949
- const sessionId = await this.sessionService.createSession(user.id);
950
- const companiesWithBranches = companies.map((company)=>({
951
- ...toCompanyInfo(company),
952
- isActive: company.isActive,
953
- branches: branches.filter((b)=>b.companyId === company.id).map((b)=>({
954
- ...toBranchInfo(b),
955
- isActive: b.isActive
956
- }))
957
- }));
958
- return {
959
- requiresSelection: true,
960
- sessionId,
961
- expiresAt: new Date(Date.now() + 5 * 60 * 1000),
962
- user: toUserInfo(user),
963
- companies: companiesWithBranches
964
- };
965
- }
966
- generateRegistrationResponse(user) {
967
- const response = {
968
- ...this.generateLoginResponse(user),
969
- requiresEmailVerification: false
970
- };
971
- this.publishAuthEvent(AUTH_EVENT_ENTITIES.USER, AUTH_EVENT_ACTIONS.REGISTERED, user);
972
- return response;
973
- }
974
- generateRegistrationResponseWithCompany(user, company, branch) {
975
- const response = {
976
- ...this.generateLoginResponseWithCompany(user, company, branch),
977
- requiresEmailVerification: false
978
- };
979
- this.publishAuthEvent(AUTH_EVENT_ENTITIES.USER, AUTH_EVENT_ACTIONS.REGISTERED, user, {
980
- companyId: company.id,
981
- branchId: branch?.id ?? null
982
- });
983
- return response;
984
- }
985
- /** Never awaited - a login must not wait on, or fail because of, the event bus */ publishAuthEvent(entity, action, user, metadata = {}) {
986
- void publishDomainEvent({
987
- module: AUTH_EVENT_MODULE,
988
- entity,
989
- action,
990
- ids: [
991
- user.id
992
- ],
993
- metadata: {
994
- email: user.email,
995
- ...metadata
996
- },
997
- user: {
998
- id: user.id,
999
- email: user.email
1000
- }
1001
- });
1002
- }
1003
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
1004
- constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, authEmailService, userEnricher, notificationAdapter){
1005
- _define_property(this, "jwtService", void 0);
1006
- _define_property(this, "authConfig", void 0);
1007
- _define_property(this, "cacheManager", void 0);
1008
- _define_property(this, "dataSourceProvider", void 0);
1009
- _define_property(this, "sessionService", void 0);
1010
- _define_property(this, "authEmailService", void 0);
1011
- _define_property(this, "userEnricher", void 0);
1012
- _define_property(this, "notificationAdapter", void 0);
1013
- this.jwtService = jwtService;
1014
- this.authConfig = authConfig;
1015
- this.cacheManager = cacheManager;
1016
- this.dataSourceProvider = dataSourceProvider;
1017
- this.sessionService = sessionService;
1018
- this.authEmailService = authEmailService;
1019
- this.userEnricher = userEnricher;
1020
- this.notificationAdapter = notificationAdapter;
1021
- }
1022
- }
1023
- _ts_decorate([
1024
- LogAction({
1025
- action: 'auth.login',
1026
- module: 'auth',
1027
- includeParams: false
1028
- }),
1029
- _ts_metadata("design:type", Function),
1030
- _ts_metadata("design:paramtypes", [
1031
- String,
1032
- String
1033
- ]),
1034
- _ts_metadata("design:returntype", Promise)
1035
- ], AuthenticationService.prototype, "login", null);
1036
- _ts_decorate([
1037
- LogAction({
1038
- action: 'auth.register',
1039
- module: 'auth',
1040
- includeParams: false
1041
- }),
1042
- _ts_metadata("design:type", Function),
1043
- _ts_metadata("design:paramtypes", [
1044
- typeof RegistrationDto === "undefined" ? Object : RegistrationDto,
1045
- String
1046
- ]),
1047
- _ts_metadata("design:returntype", Promise)
1048
- ], AuthenticationService.prototype, "register", null);
1049
- _ts_decorate([
1050
- LogAction({
1051
- action: 'auth.createUser',
1052
- module: 'auth',
1053
- includeParams: false
1054
- }),
1055
- _ts_metadata("design:type", Function),
1056
- _ts_metadata("design:paramtypes", [
1057
- typeof RegistrationDto === "undefined" ? Object : RegistrationDto
1058
- ]),
1059
- _ts_metadata("design:returntype", Promise)
1060
- ], AuthenticationService.prototype, "createUserDirectly", null);
1061
- _ts_decorate([
1062
- LogAction({
1063
- action: 'auth.createUserFromPending',
1064
- module: 'auth',
1065
- includeParams: false
1066
- }),
1067
- _ts_metadata("design:type", Function),
1068
- _ts_metadata("design:paramtypes", [
1069
- typeof PendingRegistration === "undefined" ? Object : PendingRegistration
1070
- ]),
1071
- _ts_metadata("design:returntype", Promise)
1072
- ], AuthenticationService.prototype, "createUserFromPendingRegistration", null);
1073
- _ts_decorate([
1074
- LogAction({
1075
- action: 'auth.companySwitch',
1076
- module: 'auth'
1077
- }),
1078
- _ts_metadata("design:type", Function),
1079
- _ts_metadata("design:paramtypes", [
1080
- String,
1081
- String,
1082
- String
1083
- ]),
1084
- _ts_metadata("design:returntype", Promise)
1085
- ], AuthenticationService.prototype, "validateAndLoginWithCompany", null);
1086
- _ts_decorate([
1087
- LogAction({
1088
- action: 'auth.refreshToken',
1089
- module: 'auth',
1090
- includeParams: false
1091
- }),
1092
- _ts_metadata("design:type", Function),
1093
- _ts_metadata("design:paramtypes", [
1094
- String
1095
- ]),
1096
- _ts_metadata("design:returntype", Promise)
1097
- ], AuthenticationService.prototype, "refreshToken", null);
1098
- _ts_decorate([
1099
- LogAction({
1100
- action: 'auth.changePassword',
1101
- module: 'auth',
1102
- includeParams: false
1103
- }),
1104
- _ts_metadata("design:type", Function),
1105
- _ts_metadata("design:paramtypes", [
1106
- String,
1107
- String,
1108
- String
1109
- ]),
1110
- _ts_metadata("design:returntype", Promise)
1111
- ], AuthenticationService.prototype, "changePassword", null);
1112
- _ts_decorate([
1113
- LogAction({
1114
- action: 'auth.logout',
1115
- module: 'auth'
1116
- }),
1117
- _ts_metadata("design:type", Function),
1118
- _ts_metadata("design:paramtypes", [
1119
- String
1120
- ]),
1121
- _ts_metadata("design:returntype", Promise)
1122
- ], AuthenticationService.prototype, "logout", null);
1123
- AuthenticationService = _ts_decorate([
1124
- Injectable({
1125
- scope: Scope.REQUEST
1126
- }),
1127
- _ts_param(0, Inject(JwtService)),
1128
- _ts_param(1, Inject(AuthConfigService)),
1129
- _ts_param(2, Optional()),
1130
- _ts_param(2, Inject('CACHE_INSTANCE')),
1131
- _ts_param(3, Optional()),
1132
- _ts_param(3, Inject(AuthDataSourceProvider)),
1133
- _ts_param(4, Optional()),
1134
- _ts_param(4, Inject(CompanySelectionSessionService)),
1135
- _ts_param(5, Optional()),
1136
- _ts_param(5, Inject(AuthEmailService)),
1137
- _ts_param(6, Optional()),
1138
- _ts_param(6, Inject(USER_ENRICHER)),
1139
- _ts_param(7, Optional()),
1140
- _ts_param(7, Inject(NOTIFICATION_ADAPTER)),
1141
- _ts_metadata("design:type", Function),
1142
- _ts_metadata("design:paramtypes", [
1143
- typeof JwtService === "undefined" ? Object : JwtService,
1144
- typeof AuthConfigService === "undefined" ? Object : AuthConfigService,
1145
- typeof HybridCache === "undefined" ? Object : HybridCache,
1146
- typeof AuthDataSourceProvider === "undefined" ? Object : AuthDataSourceProvider,
1147
- typeof CompanySelectionSessionService === "undefined" ? Object : CompanySelectionSessionService,
1148
- typeof AuthEmailService === "undefined" ? Object : AuthEmailService,
1149
- typeof IUserEnricher === "undefined" ? Object : IUserEnricher,
1150
- typeof INotificationAdapter === "undefined" ? Object : INotificationAdapter
1151
- ])
1152
- ], AuthenticationService);