@flusys/nestjs-auth 3.0.0-rc → 3.0.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 (40) hide show
  1. package/README.md +610 -95
  2. package/cjs/controllers/authentication.controller.js +2 -173
  3. package/cjs/controllers/email-verification.controller.js +220 -0
  4. package/cjs/controllers/index.js +1 -0
  5. package/cjs/dtos/authentication.dto.js +12 -2
  6. package/cjs/entities/index.js +16 -17
  7. package/cjs/entities/pending-registration.entity.js +9 -0
  8. package/cjs/modules/auth.module.js +33 -24
  9. package/cjs/services/auth-config.service.js +11 -7
  10. package/cjs/services/auth-datasource.provider.js +15 -27
  11. package/cjs/services/auth-email.service.js +8 -33
  12. package/cjs/services/authentication.service.js +155 -110
  13. package/cjs/services/user.service.js +28 -83
  14. package/controllers/authentication.controller.d.ts +2 -10
  15. package/controllers/email-verification.controller.d.ts +20 -0
  16. package/controllers/index.d.ts +1 -0
  17. package/dtos/authentication.dto.d.ts +1 -0
  18. package/entities/index.d.ts +5 -10
  19. package/entities/pending-registration.entity.d.ts +1 -0
  20. package/fesm/controllers/authentication.controller.js +3 -174
  21. package/fesm/controllers/email-verification.controller.js +210 -0
  22. package/fesm/controllers/index.js +1 -0
  23. package/fesm/dtos/authentication.dto.js +32 -25
  24. package/fesm/entities/index.js +17 -9
  25. package/fesm/entities/pending-registration.entity.js +9 -0
  26. package/fesm/interfaces/user-enricher.interface.js +2 -24
  27. package/fesm/modules/auth.module.js +35 -26
  28. package/fesm/services/auth-config.service.js +11 -7
  29. package/fesm/services/auth-datasource.provider.js +15 -27
  30. package/fesm/services/auth-email.service.js +8 -33
  31. package/fesm/services/authentication.service.js +145 -100
  32. package/fesm/services/user.service.js +28 -83
  33. package/interfaces/user-enricher.interface.d.ts +3 -2
  34. package/modules/auth.module.d.ts +3 -1
  35. package/package.json +3 -3
  36. package/services/auth-config.service.d.ts +1 -0
  37. package/services/auth-datasource.provider.d.ts +1 -0
  38. package/services/auth-email.service.d.ts +1 -0
  39. package/services/authentication.service.d.ts +7 -1
  40. package/services/user.service.d.ts +5 -0
@@ -9,16 +9,19 @@ Object.defineProperty(exports, "AuthenticationService", {
9
9
  }
10
10
  });
11
11
  const _classes = require("@flusys/nestjs-shared/classes");
12
+ const _interfaces = require("@flusys/nestjs-shared/interfaces");
13
+ const _enums = require("@flusys/nestjs-shared/enums");
12
14
  const _utils = require("@flusys/nestjs-shared/utils");
13
15
  const _common = require("@nestjs/common");
14
16
  const _jwt = require("@nestjs/jwt");
15
17
  const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
16
18
  const _config = require("../config");
19
+ const _userenricherinterface = require("../interfaces/user-enricher.interface");
17
20
  const _companybranchentity = require("../entities/company-branch.entity");
18
21
  const _companyentity = require("../entities/company.entity");
19
22
  const _usercompanypermissionentity = require("../entities/user-company-permission.entity");
20
23
  const _userentity = require("../entities/user.entity");
21
- const _enums = require("../enums");
24
+ const _enums1 = require("../enums");
22
25
  const _authconfigservice = require("./auth-config.service");
23
26
  const _authdatasourceprovider = require("./auth-datasource.provider");
24
27
  const _authemailservice = require("./auth-email.service");
@@ -92,11 +95,14 @@ function _ts_param(paramIndex, decorator) {
92
95
  };
93
96
  }
94
97
  let AuthenticationService = class AuthenticationService {
95
- // Feature Flags
96
- /** Check if company feature is enabled (tenant-aware with global fallback) */ isCompanyFeatureEnabled() {
98
+ isCompanyFeatureEnabled() {
97
99
  return this.dataSourceProvider?.isCompanyFeatureEnabled() ?? this.authConfig.isCompanyFeatureEnabled();
98
100
  }
99
- // Repository Getters
101
+ isEmailVerificationEnabled() {
102
+ const featureEnabled = this.dataSourceProvider?.isEmailVerificationEnabled() ?? this.authConfig.isEmailVerificationEnabled();
103
+ const providerAvailable = this.authEmailService?.isEmailEnabled() ?? false;
104
+ return featureEnabled && providerAvailable;
105
+ }
100
106
  async getUserRepository() {
101
107
  if (!this.dataSourceProvider) {
102
108
  throw new Error('User repository not available');
@@ -115,10 +121,8 @@ let AuthenticationService = class AuthenticationService {
115
121
  if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
116
122
  return this.dataSourceProvider.getRepository(_companybranchentity.CompanyBranch);
117
123
  }
118
- // Login
119
124
  async login(email, password) {
120
- // Check for pending registration first (user registered but hasn't verified email yet)
121
- if (await this.authEmailService?.hasPendingRegistration(email)) {
125
+ if (this.isEmailVerificationEnabled() && await this.authEmailService.hasPendingRegistration(email)) {
122
126
  return {
123
127
  requiresEmailVerification: true,
124
128
  email,
@@ -129,7 +133,7 @@ let AuthenticationService = class AuthenticationService {
129
133
  if (!user.isActive) {
130
134
  throw new _common.UnauthorizedException('User account is deactivated');
131
135
  }
132
- if (this.authEmailService?.isEmailEnabled() && !user.emailVerified) {
136
+ if (this.isEmailVerificationEnabled() && !user.emailVerified) {
133
137
  return {
134
138
  requiresEmailVerification: true,
135
139
  email: user.email,
@@ -141,20 +145,18 @@ let AuthenticationService = class AuthenticationService {
141
145
  }
142
146
  return this.handleCompanyBasedLogin(user);
143
147
  }
144
- /** Handle login when company feature is enabled */ async handleCompanyBasedLogin(user) {
148
+ async handleCompanyBasedLogin(user) {
145
149
  const permissionRepository = await this.getPermissionRepository();
146
150
  const branchRepository = await this.getBranchRepository();
147
151
  if (!permissionRepository || !branchRepository) {
148
152
  return this.generateLoginResponse(user);
149
153
  }
150
- // Get company permissions
151
154
  const companyPermissions = await permissionRepository.find({
152
155
  where: {
153
156
  userId: user.id,
154
- permissionType: _enums.PermissionType.COMPANY
157
+ permissionType: _enums1.PermissionType.COMPANY
155
158
  }
156
159
  });
157
- // Load companies
158
160
  const companyRepository = await this.getCompanyRepository();
159
161
  if (!companyRepository) {
160
162
  return this.generateLoginResponse(user);
@@ -165,16 +167,13 @@ let AuthenticationService = class AuthenticationService {
165
167
  }).andWhere('c.is_active = :active', {
166
168
  active: true
167
169
  }).getMany() : [];
168
- // If user has no company assignments yet, return simple login response
169
- // This allows users to login and be assigned to companies later
170
170
  if (companies.length === 0) {
171
171
  return this.generateLoginResponse(user);
172
172
  }
173
- // Get branch permissions
174
173
  const branchPermissions = await permissionRepository.find({
175
174
  where: {
176
175
  userId: user.id,
177
- permissionType: _enums.PermissionType.BRANCH
176
+ permissionType: _enums1.PermissionType.BRANCH
178
177
  }
179
178
  });
180
179
  const branchIds = branchPermissions.map((p)=>p.targetId);
@@ -183,32 +182,25 @@ let AuthenticationService = class AuthenticationService {
183
182
  }).andWhere('b.is_active = :active', {
184
183
  active: true
185
184
  }).getMany() : [];
186
- // Case 1: Single company, single branch → Direct login
187
185
  if (companies.length === 1 && branches.length === 1) {
188
186
  return this.generateLoginResponseWithCompany(user, companies[0], branches[0]);
189
187
  }
190
- // Case 2: Multiple options → Selection required (frontend handles UI)
191
- // Returns all companies with their branches for frontend to display
192
188
  return await this.generateCompanySelectionResponse(user, companies, branches);
193
189
  }
194
- // Registration
195
- /** Register a new user. If email enabled, creates pending registration. Otherwise creates user directly. */ async register(dto, verifyUrlBase) {
190
+ async register(dto, verifyUrlBase) {
196
191
  const userRepository = await this.getUserRepository();
197
192
  const existingUser = await userRepository.findOne({
198
193
  where: {
199
194
  email: dto.email
200
195
  }
201
196
  });
202
- // When company feature is enabled and user exists, allow joining/creating company
203
197
  if (existingUser && this.isCompanyFeatureEnabled()) {
204
198
  return this.handleExistingUserCompanyRegistration(existingUser, dto);
205
199
  }
206
- // User exists but company feature is off - email must be unique
207
200
  if (existingUser) {
208
201
  throw new _common.BadRequestException('Registration failed due to email exists. Please try again or contact support.');
209
202
  }
210
- // If email verification is enabled, create pending registration instead of user
211
- if (this.authEmailService?.isEmailEnabled() && verifyUrlBase) {
203
+ if (this.isEmailVerificationEnabled() && verifyUrlBase) {
212
204
  const companyData = this.isCompanyFeatureEnabled() ? {
213
205
  companySlug: dto.companySlug,
214
206
  branchSlug: dto.branchSlug,
@@ -222,6 +214,7 @@ let AuthenticationService = class AuthenticationService {
222
214
  phone: dto.phone,
223
215
  password: dto.password,
224
216
  companyData,
217
+ additionalFields: dto.additionalFields,
225
218
  verifyUrlBase
226
219
  });
227
220
  return {
@@ -229,10 +222,9 @@ let AuthenticationService = class AuthenticationService {
229
222
  message: result.message
230
223
  };
231
224
  }
232
- // No email verification - create user directly
233
225
  return this.createUserDirectly(dto);
234
226
  }
235
- /** Handle registration for existing user when company feature is enabled */ async handleExistingUserCompanyRegistration(existingUser, dto) {
227
+ async handleExistingUserCompanyRegistration(existingUser, dto) {
236
228
  if (!dto.companySlug && !dto.newCompanyName) {
237
229
  throw new _common.BadRequestException('Email already registered. Please login or provide a company to join/create.');
238
230
  }
@@ -241,7 +233,6 @@ let AuthenticationService = class AuthenticationService {
241
233
  if (!permissionRepository || !companyRepository) {
242
234
  throw new _common.BadRequestException('Company feature repositories not available');
243
235
  }
244
- // For existing users joining a company, check if already assigned
245
236
  if (dto.companySlug) {
246
237
  const company = await companyRepository.findOne({
247
238
  where: {
@@ -253,7 +244,7 @@ let AuthenticationService = class AuthenticationService {
253
244
  const existingPermission = await permissionRepository.findOne({
254
245
  where: {
255
246
  userId: existingUser.id,
256
- permissionType: _enums.PermissionType.COMPANY,
247
+ permissionType: _enums1.PermissionType.COMPANY,
257
248
  targetId: company.id
258
249
  }
259
250
  });
@@ -264,50 +255,92 @@ let AuthenticationService = class AuthenticationService {
264
255
  }
265
256
  return this.processCompanyRegistration(existingUser, dto, true);
266
257
  }
267
- /** Create user directly (no email verification) */ async createUserDirectly(dto) {
268
- const userRepository = await this.getUserRepository();
269
- const hashedPassword = await _bcrypt.hash(dto.password, _config.BCRYPT_SALT_ROUNDS);
270
- const user = userRepository.create({
271
- name: dto.name,
272
- email: dto.email,
273
- phone: dto.phone,
274
- password: hashedPassword,
275
- isActive: true,
276
- emailVerified: false,
277
- phoneVerified: false
278
- });
279
- await userRepository.save(user);
280
- if (!this.isCompanyFeatureEnabled()) {
281
- return this.generateRegistrationResponse(user);
258
+ async createUserDirectly(dto) {
259
+ if (!this.dataSourceProvider) {
260
+ throw new _common.BadRequestException('Data source not available');
261
+ }
262
+ const dataSource = await this.dataSourceProvider.getDataSource();
263
+ const queryRunner = dataSource.createQueryRunner();
264
+ await queryRunner.connect();
265
+ await queryRunner.startTransaction();
266
+ try {
267
+ const hashedPassword = await _bcrypt.hash(dto.password, _config.BCRYPT_SALT_ROUNDS);
268
+ const user = queryRunner.manager.create(_userentity.User, {
269
+ name: dto.name,
270
+ email: dto.email,
271
+ phone: dto.phone,
272
+ password: hashedPassword,
273
+ isActive: true,
274
+ emailVerified: false,
275
+ phoneVerified: false
276
+ });
277
+ await queryRunner.manager.save(_userentity.User, user);
278
+ if (this.userEnricher?.onUserCreated) {
279
+ await this.userEnricher.onUserCreated(user.id, dto.additionalFields ?? null, queryRunner);
280
+ }
281
+ await queryRunner.commitTransaction();
282
+ if (!this.isCompanyFeatureEnabled()) {
283
+ return this.generateRegistrationResponse(user);
284
+ }
285
+ return this.processCompanyRegistration(user, dto);
286
+ } catch (error) {
287
+ await queryRunner.rollbackTransaction();
288
+ if (error instanceof _common.BadRequestException) {
289
+ throw error;
290
+ }
291
+ _utils.ErrorHandler.logError(this.logger, error, 'createUserDirectly');
292
+ throw new _common.BadRequestException('Registration failed. Please try again.');
293
+ } finally{
294
+ await queryRunner.release();
282
295
  }
283
- return this.processCompanyRegistration(user, dto);
284
296
  }
285
- /** Create user from pending registration (after email verification) */ async createUserFromPendingRegistration(pending) {
286
- const userRepository = await this.getUserRepository();
287
- const existingUser = await userRepository.findOne({
288
- where: {
289
- email: pending.email
297
+ async createUserFromPendingRegistration(pending) {
298
+ if (!this.dataSourceProvider) {
299
+ throw new _common.BadRequestException('Data source not available');
300
+ }
301
+ const dataSource = await this.dataSourceProvider.getDataSource();
302
+ const queryRunner = dataSource.createQueryRunner();
303
+ await queryRunner.connect();
304
+ await queryRunner.startTransaction();
305
+ try {
306
+ const existingUser = await queryRunner.manager.findOne(_userentity.User, {
307
+ where: {
308
+ email: pending.email
309
+ }
310
+ });
311
+ if (existingUser) {
312
+ throw new _common.BadRequestException('This email has already been registered.');
290
313
  }
291
- });
292
- if (existingUser) {
293
- throw new _common.BadRequestException('This email has already been registered.');
294
- }
295
- const user = userRepository.create({
296
- name: pending.name,
297
- email: pending.email,
298
- phone: pending.phone,
299
- password: pending.passwordHash,
300
- isActive: true,
301
- emailVerified: true,
302
- phoneVerified: false
303
- });
304
- await userRepository.save(user);
305
- if (this.isCompanyFeatureEnabled() && pending.companyData) {
306
- return this.processCompanyRegistration(user, pending.companyData);
314
+ const user = queryRunner.manager.create(_userentity.User, {
315
+ name: pending.name,
316
+ email: pending.email,
317
+ phone: pending.phone,
318
+ password: pending.passwordHash,
319
+ isActive: true,
320
+ emailVerified: true,
321
+ phoneVerified: false
322
+ });
323
+ await queryRunner.manager.save(_userentity.User, user);
324
+ if (this.userEnricher?.onUserCreated) {
325
+ await this.userEnricher.onUserCreated(user.id, pending.additionalFields ?? null, queryRunner);
326
+ }
327
+ await queryRunner.commitTransaction();
328
+ if (this.isCompanyFeatureEnabled() && pending.companyData) {
329
+ return this.processCompanyRegistration(user, pending.companyData);
330
+ }
331
+ return this.generateRegistrationResponse(user);
332
+ } catch (error) {
333
+ await queryRunner.rollbackTransaction();
334
+ if (error instanceof _common.BadRequestException) {
335
+ throw error;
336
+ }
337
+ _utils.ErrorHandler.logError(this.logger, error, 'createUserFromPendingRegistration');
338
+ throw new _common.BadRequestException('Registration failed. Please try again.');
339
+ } finally{
340
+ await queryRunner.release();
307
341
  }
308
- return this.generateRegistrationResponse(user);
309
342
  }
310
- /** Process company registration (join existing or create new) */ async processCompanyRegistration(user, data, throwOnUnavailable = false) {
343
+ async processCompanyRegistration(user, data, throwOnUnavailable = false) {
311
344
  const companyRepository = await this.getCompanyRepository();
312
345
  const branchRepository = await this.getBranchRepository();
313
346
  const permissionRepository = await this.getPermissionRepository();
@@ -322,7 +355,7 @@ let AuthenticationService = class AuthenticationService {
322
355
  }
323
356
  return this.generateRegistrationResponse(user);
324
357
  }
325
- /** Find existing company/branch or create new ones */ async findOrCreateCompanyAndBranch(data, companyRepo, branchRepo) {
358
+ async findOrCreateCompanyAndBranch(data, companyRepo, branchRepo) {
326
359
  if (data.companySlug) {
327
360
  const company = await companyRepo.findOne({
328
361
  where: {
@@ -386,22 +419,21 @@ let AuthenticationService = class AuthenticationService {
386
419
  branch: null
387
420
  };
388
421
  }
389
- /** Assign user to company and optionally branch */ async assignUserPermissions(userId, companyId, branchId, permissionRepo) {
422
+ async assignUserPermissions(userId, companyId, branchId, permissionRepo) {
390
423
  await permissionRepo.save(permissionRepo.create({
391
424
  userId,
392
- permissionType: _enums.PermissionType.COMPANY,
425
+ permissionType: _enums1.PermissionType.COMPANY,
393
426
  targetId: companyId
394
427
  }));
395
428
  if (branchId) {
396
429
  await permissionRepo.save(permissionRepo.create({
397
430
  userId,
398
- permissionType: _enums.PermissionType.BRANCH,
431
+ permissionType: _enums1.PermissionType.BRANCH,
399
432
  targetId: branchId
400
433
  }));
401
434
  }
402
435
  }
403
- // Company/Branch Selection
404
- /** Select company and branch (step 2 of login). Frontend receives all options and user selects both. */ async select(sessionId, companyId, branchId) {
436
+ async select(sessionId, companyId, branchId) {
405
437
  this.ensureCompanyFeatureEnabled();
406
438
  if (!this.sessionService) {
407
439
  throw new _common.BadRequestException('Session service not available');
@@ -412,11 +444,11 @@ let AuthenticationService = class AuthenticationService {
412
444
  }
413
445
  return this.validateAndLoginWithCompany(session.userId, companyId, branchId);
414
446
  }
415
- /** Switch company/branch after login */ async switchCompany(userId, companyId, branchId) {
447
+ async switchCompany(userId, companyId, branchId) {
416
448
  this.ensureCompanyFeatureEnabled();
417
449
  return this.validateAndLoginWithCompany(userId, companyId, branchId);
418
450
  }
419
- /** Shared: Validate user access to company/branch and generate login response */ async validateAndLoginWithCompany(userId, companyId, branchId) {
451
+ async validateAndLoginWithCompany(userId, companyId, branchId) {
420
452
  const userRepository = await this.getUserRepository();
421
453
  const user = await userRepository.findOne({
422
454
  where: {
@@ -432,11 +464,10 @@ let AuthenticationService = class AuthenticationService {
432
464
  if (!permissionRepository || !companyRepository || !branchRepository) {
433
465
  throw new _common.BadRequestException('Repositories not available');
434
466
  }
435
- // Validate company access
436
467
  const hasCompanyAccess = await permissionRepository.findOne({
437
468
  where: {
438
469
  userId,
439
- permissionType: _enums.PermissionType.COMPANY,
470
+ permissionType: _enums1.PermissionType.COMPANY,
440
471
  targetId: companyId
441
472
  }
442
473
  });
@@ -452,11 +483,10 @@ let AuthenticationService = class AuthenticationService {
452
483
  if (!company) {
453
484
  throw new _common.UnauthorizedException('Company not found or inactive');
454
485
  }
455
- // Validate branch access
456
486
  const hasBranchAccess = await permissionRepository.findOne({
457
487
  where: {
458
488
  userId,
459
- permissionType: _enums.PermissionType.BRANCH,
489
+ permissionType: _enums1.PermissionType.BRANCH,
460
490
  targetId: branchId
461
491
  }
462
492
  });
@@ -475,8 +505,7 @@ let AuthenticationService = class AuthenticationService {
475
505
  }
476
506
  return this.generateLoginResponseWithCompany(user, company, branch);
477
507
  }
478
- // Token Management
479
- /** Refresh access token (with token rotation). Returns new access token and new refresh token. */ async refreshToken(refreshToken) {
508
+ async refreshToken(refreshToken) {
480
509
  try {
481
510
  const payload = this.jwtService.verify(refreshToken, {
482
511
  secret: this.authConfig.getRefreshTokenSecret()
@@ -510,7 +539,7 @@ let AuthenticationService = class AuthenticationService {
510
539
  throw new _common.UnauthorizedException('Invalid or expired refresh token');
511
540
  }
512
541
  }
513
- /** Change password */ async changePassword(userId, currentPassword, newPassword) {
542
+ async changePassword(userId, currentPassword, newPassword) {
514
543
  const userRepository = await this.getUserRepository();
515
544
  const user = await userRepository.findOne({
516
545
  where: {
@@ -524,21 +553,19 @@ let AuthenticationService = class AuthenticationService {
524
553
  user.password = await _bcrypt.hash(newPassword, _config.BCRYPT_SALT_ROUNDS);
525
554
  await userRepository.save(user);
526
555
  }
527
- /** Logout - Revokes all tokens. Stores revocation timestamp in cache. */ async logout(userId) {
556
+ async logout(userId) {
528
557
  if (!this.cacheManager) {
529
- // Cache is required for secure logout - tokens cannot be revoked without it
530
558
  this.logger.error(`SECURITY: Cache not available for logout (userId: ${userId}). ` + 'Token revocation failed - tokens will remain valid until expiry. ' + 'Configure cache (Redis) to enable proper token revocation.');
531
559
  throw new _common.BadRequestException('Logout service temporarily unavailable. Please try again later.');
532
560
  }
533
- const revokedAt = Math.floor(Date.now() / 1000); // Unix timestamp in seconds
561
+ const revokedAt = Math.floor(Date.now() / 1000);
534
562
  const cacheKey = `${_config.TOKEN_REVOKED_PREFIX}:${userId}`;
535
- // TTL matches refresh token expiry (tokens older than this are already expired)
536
563
  const refreshTokenExpiry = this.authConfig.getRefreshTokenExpiration();
537
564
  const ttlMs = this.parseExpiryToMs(refreshTokenExpiry);
538
565
  await this.cacheManager.set(cacheKey, revokedAt, ttlMs);
539
566
  this.logger.log(`User ${userId} logged out - all tokens revoked`);
540
567
  }
541
- /** Check if a token is revoked (issued before user's revocation timestamp) */ async isTokenRevoked(userId, tokenIssuedAt) {
568
+ async isTokenRevoked(userId, tokenIssuedAt) {
542
569
  if (!this.cacheManager || !tokenIssuedAt) {
543
570
  return false;
544
571
  }
@@ -547,12 +574,11 @@ let AuthenticationService = class AuthenticationService {
547
574
  if (!revokedAt) {
548
575
  return false;
549
576
  }
550
- // Token is revoked if it was issued before the revocation timestamp
551
577
  return tokenIssuedAt < revokedAt;
552
578
  }
553
- /** Parse JWT expiry string to milliseconds */ parseExpiryToMs(expiry) {
579
+ parseExpiryToMs(expiry) {
554
580
  const match = expiry.match(/^(\d+)([smhd])$/);
555
- if (!match) return 7 * 24 * 60 * 60 * 1000; // Default 7 days
581
+ if (!match) return 7 * 24 * 60 * 60 * 1000;
556
582
  const value = parseInt(match[1], 10);
557
583
  const unit = match[2];
558
584
  switch(unit){
@@ -568,8 +594,7 @@ let AuthenticationService = class AuthenticationService {
568
594
  return 7 * 24 * 60 * 60 * 1000;
569
595
  }
570
596
  }
571
- // Query Methods
572
- /** Get user's companies */ async getUserCompanies(userId) {
597
+ async getUserCompanies(userId) {
573
598
  this.ensureCompanyFeatureEnabled();
574
599
  const permissionRepository = await this.getPermissionRepository();
575
600
  const companyRepository = await this.getCompanyRepository();
@@ -579,7 +604,7 @@ let AuthenticationService = class AuthenticationService {
579
604
  const permissions = await permissionRepository.find({
580
605
  where: {
581
606
  userId,
582
- permissionType: _enums.PermissionType.COMPANY
607
+ permissionType: _enums1.PermissionType.COMPANY
583
608
  }
584
609
  });
585
610
  const companyIds = permissions.map((p)=>p.targetId);
@@ -590,29 +615,27 @@ let AuthenticationService = class AuthenticationService {
590
615
  active: true
591
616
  }).orderBy('c.name', 'ASC').getMany();
592
617
  }
593
- /** Get branches for a company that user has access to */ async getUserBranches(userId, companyId) {
618
+ async getUserBranches(userId, companyId) {
594
619
  this.ensureCompanyFeatureEnabled();
595
620
  const permissionRepository = await this.getPermissionRepository();
596
621
  const branchRepository = await this.getBranchRepository();
597
622
  if (!permissionRepository || !branchRepository) {
598
623
  throw new _common.BadRequestException('Repositories not available');
599
624
  }
600
- // Verify company access
601
625
  const companyPermission = await permissionRepository.findOne({
602
626
  where: {
603
627
  userId,
604
- permissionType: _enums.PermissionType.COMPANY,
628
+ permissionType: _enums1.PermissionType.COMPANY,
605
629
  targetId: companyId
606
630
  }
607
631
  });
608
632
  if (!companyPermission) {
609
633
  throw new _common.UnauthorizedException('No access to this company');
610
634
  }
611
- // Get branch permissions
612
635
  const branchPermissions = await permissionRepository.find({
613
636
  where: {
614
637
  userId,
615
- permissionType: _enums.PermissionType.BRANCH
638
+ permissionType: _enums1.PermissionType.BRANCH
616
639
  }
617
640
  });
618
641
  const branchIds = branchPermissions.map((p)=>p.targetId);
@@ -625,7 +648,7 @@ let AuthenticationService = class AuthenticationService {
625
648
  active: true
626
649
  }).orderBy('b.serial', 'ASC').addOrderBy('b.name', 'ASC').getMany();
627
650
  }
628
- /** Get company by ID */ async getCompanyById(companyId) {
651
+ async getCompanyById(companyId) {
629
652
  const companyRepository = await this.getCompanyRepository();
630
653
  if (!companyRepository) {
631
654
  return null;
@@ -636,7 +659,7 @@ let AuthenticationService = class AuthenticationService {
636
659
  }
637
660
  });
638
661
  }
639
- /** Get branch by ID */ async getBranchById(branchId) {
662
+ async getBranchById(branchId) {
640
663
  const branchRepository = await this.getBranchRepository();
641
664
  if (!branchRepository) {
642
665
  return null;
@@ -647,7 +670,7 @@ let AuthenticationService = class AuthenticationService {
647
670
  }
648
671
  });
649
672
  }
650
- /** Get user info for /auth/me endpoint. Returns user, company, and branch info (no tokens). */ async getUserInfo(userId, companyId, branchId) {
673
+ async getUserInfo(userId, companyId, branchId) {
651
674
  const userRepository = await this.getUserRepository();
652
675
  const user = await userRepository.findOne({
653
676
  where: {
@@ -673,7 +696,6 @@ let AuthenticationService = class AuthenticationService {
673
696
  }
674
697
  return response;
675
698
  }
676
- // Helper Methods
677
699
  ensureCompanyFeatureEnabled() {
678
700
  if (!this.isCompanyFeatureEnabled()) {
679
701
  throw new _common.BadRequestException('Company feature is not enabled');
@@ -693,8 +715,7 @@ let AuthenticationService = class AuthenticationService {
693
715
  await userRepository.save(user);
694
716
  return user;
695
717
  }
696
- // Response Generators
697
- /** Create user info object */ toUserInfo(user) {
718
+ toUserInfo(user) {
698
719
  return {
699
720
  id: user.id,
700
721
  email: user.email,
@@ -703,7 +724,7 @@ let AuthenticationService = class AuthenticationService {
703
724
  profilePictureId: user.profilePictureId ?? null
704
725
  };
705
726
  }
706
- /** Create company info object */ toCompanyInfo(company) {
727
+ toCompanyInfo(company) {
707
728
  return {
708
729
  id: company.id,
709
730
  name: company.name,
@@ -711,7 +732,7 @@ let AuthenticationService = class AuthenticationService {
711
732
  logoId: company.logoId ?? null
712
733
  };
713
734
  }
714
- /** Create branch info object */ toBranchInfo(branch) {
735
+ toBranchInfo(branch) {
715
736
  return {
716
737
  id: branch.id,
717
738
  name: branch.name,
@@ -719,7 +740,7 @@ let AuthenticationService = class AuthenticationService {
719
740
  logoId: branch.logoId ?? null
720
741
  };
721
742
  }
722
- /** Generate JWT tokens */ generateTokens(payload) {
743
+ generateTokens(payload) {
723
744
  const accessToken = this.jwtService.sign({
724
745
  ...payload,
725
746
  type: 'access'
@@ -745,6 +766,7 @@ let AuthenticationService = class AuthenticationService {
745
766
  email: user.email,
746
767
  type: 'access'
747
768
  });
769
+ this.sendLoginNotification(user.id);
748
770
  return {
749
771
  accessToken,
750
772
  refreshToken,
@@ -759,6 +781,7 @@ let AuthenticationService = class AuthenticationService {
759
781
  companyId: company.id,
760
782
  branchId: branch?.id
761
783
  });
784
+ this.sendLoginNotification(user.id, company.id);
762
785
  return {
763
786
  accessToken,
764
787
  refreshToken,
@@ -767,6 +790,18 @@ let AuthenticationService = class AuthenticationService {
767
790
  branch: branch ? this.toBranchInfo(branch) : undefined
768
791
  };
769
792
  }
793
+ sendLoginNotification(userId, companyId) {
794
+ if (!this.notificationAdapter) return;
795
+ this.notificationAdapter.send({
796
+ userId,
797
+ title: 'Login successful',
798
+ message: 'You have successfully logged in to your account.',
799
+ type: _enums.NotificationType.INFO,
800
+ companyId
801
+ }).catch((error)=>{
802
+ this.logger.warn(`Failed to send login notification: ${error.message}`);
803
+ });
804
+ }
770
805
  async generateCompanySelectionResponse(user, companies, branches) {
771
806
  if (!this.sessionService) {
772
807
  throw new _common.BadRequestException('Session service not available');
@@ -801,13 +836,15 @@ let AuthenticationService = class AuthenticationService {
801
836
  };
802
837
  }
803
838
  // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
804
- constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, authEmailService){
839
+ constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, authEmailService, userEnricher, notificationAdapter){
805
840
  _define_property(this, "jwtService", void 0);
806
841
  _define_property(this, "authConfig", void 0);
807
842
  _define_property(this, "cacheManager", void 0);
808
843
  _define_property(this, "dataSourceProvider", void 0);
809
844
  _define_property(this, "sessionService", void 0);
810
845
  _define_property(this, "authEmailService", void 0);
846
+ _define_property(this, "userEnricher", void 0);
847
+ _define_property(this, "notificationAdapter", void 0);
811
848
  _define_property(this, "logger", void 0);
812
849
  this.jwtService = jwtService;
813
850
  this.authConfig = authConfig;
@@ -815,6 +852,8 @@ let AuthenticationService = class AuthenticationService {
815
852
  this.dataSourceProvider = dataSourceProvider;
816
853
  this.sessionService = sessionService;
817
854
  this.authEmailService = authEmailService;
855
+ this.userEnricher = userEnricher;
856
+ this.notificationAdapter = notificationAdapter;
818
857
  this.logger = new _common.Logger(AuthenticationService.name);
819
858
  }
820
859
  };
@@ -832,6 +871,10 @@ AuthenticationService = _ts_decorate([
832
871
  _ts_param(4, (0, _common.Inject)(_companyselectionsessionservice.CompanySelectionSessionService)),
833
872
  _ts_param(5, (0, _common.Optional)()),
834
873
  _ts_param(5, (0, _common.Inject)(_authemailservice.AuthEmailService)),
874
+ _ts_param(6, (0, _common.Optional)()),
875
+ _ts_param(6, (0, _common.Inject)(_config.USER_ENRICHER)),
876
+ _ts_param(7, (0, _common.Optional)()),
877
+ _ts_param(7, (0, _common.Inject)(_interfaces.NOTIFICATION_ADAPTER)),
835
878
  _ts_metadata("design:type", Function),
836
879
  _ts_metadata("design:paramtypes", [
837
880
  typeof _jwt.JwtService === "undefined" ? Object : _jwt.JwtService,
@@ -839,6 +882,8 @@ AuthenticationService = _ts_decorate([
839
882
  typeof _classes.HybridCache === "undefined" ? Object : _classes.HybridCache,
840
883
  typeof _authdatasourceprovider.AuthDataSourceProvider === "undefined" ? Object : _authdatasourceprovider.AuthDataSourceProvider,
841
884
  typeof _companyselectionsessionservice.CompanySelectionSessionService === "undefined" ? Object : _companyselectionsessionservice.CompanySelectionSessionService,
842
- typeof _authemailservice.AuthEmailService === "undefined" ? Object : _authemailservice.AuthEmailService
885
+ typeof _authemailservice.AuthEmailService === "undefined" ? Object : _authemailservice.AuthEmailService,
886
+ typeof _userenricherinterface.IUserEnricher === "undefined" ? Object : _userenricherinterface.IUserEnricher,
887
+ typeof _interfaces.INotificationAdapter === "undefined" ? Object : _interfaces.INotificationAdapter
843
888
  ])
844
889
  ], AuthenticationService);