@flusys/nestjs-auth 0.1.0-beta.3 → 1.1.0-beta

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 (73) hide show
  1. package/README.md +1 -11
  2. package/cjs/config/auth.constants.js +14 -4
  3. package/cjs/controllers/authentication.controller.js +173 -36
  4. package/cjs/controllers/branch.controller.js +3 -4
  5. package/cjs/controllers/company.controller.js +3 -4
  6. package/cjs/controllers/user-permission.controller.js +6 -18
  7. package/cjs/controllers/user.controller.js +168 -22
  8. package/cjs/dtos/authentication.dto.js +106 -3
  9. package/cjs/entities/index.js +5 -1
  10. package/cjs/entities/pending-registration.entity.js +125 -0
  11. package/cjs/entities/user-token.entity.js +129 -0
  12. package/cjs/interfaces/auth-email-provider.interface.js +45 -0
  13. package/cjs/interfaces/authentication.interface.js +0 -3
  14. package/cjs/interfaces/index.js +2 -0
  15. package/cjs/interfaces/user-enricher.interface.js +4 -0
  16. package/cjs/modules/auth.module.js +8 -2
  17. package/cjs/services/auth-config.service.js +22 -44
  18. package/cjs/services/auth-datasource.provider.js +17 -33
  19. package/cjs/services/auth-email.service.js +452 -0
  20. package/cjs/services/authentication.service.js +229 -115
  21. package/cjs/services/branch.service.js +10 -44
  22. package/cjs/services/company.service.js +7 -36
  23. package/cjs/services/index.js +1 -0
  24. package/cjs/services/user-permission.service.js +9 -27
  25. package/cjs/services/user.service.js +177 -99
  26. package/cjs/strategies/jwt.strategy.js +14 -1
  27. package/config/auth.constants.d.ts +3 -0
  28. package/controllers/authentication.controller.d.ts +13 -9
  29. package/controllers/user.controller.d.ts +27 -0
  30. package/dtos/authentication.dto.d.ts +21 -3
  31. package/dtos/user.dto.d.ts +1 -0
  32. package/entities/index.d.ts +7 -3
  33. package/entities/pending-registration.entity.d.ts +17 -0
  34. package/entities/user-token.entity.d.ts +15 -0
  35. package/fesm/config/auth.constants.js +18 -8
  36. package/fesm/controllers/authentication.controller.js +174 -37
  37. package/fesm/controllers/branch.controller.js +5 -6
  38. package/fesm/controllers/company.controller.js +5 -6
  39. package/fesm/controllers/user-permission.controller.js +6 -18
  40. package/fesm/controllers/user.controller.js +168 -22
  41. package/fesm/dtos/authentication.dto.js +109 -100
  42. package/fesm/entities/index.js +7 -1
  43. package/fesm/entities/pending-registration.entity.js +115 -0
  44. package/fesm/entities/user-token.entity.js +113 -0
  45. package/fesm/interfaces/auth-email-provider.interface.js +38 -0
  46. package/fesm/interfaces/authentication.interface.js +1 -7
  47. package/fesm/interfaces/index.js +2 -0
  48. package/fesm/interfaces/user-enricher.interface.js +26 -0
  49. package/fesm/modules/auth.module.js +9 -3
  50. package/fesm/services/auth-config.service.js +22 -44
  51. package/fesm/services/auth-datasource.provider.js +17 -33
  52. package/fesm/services/auth-email.service.js +401 -0
  53. package/fesm/services/authentication.service.js +227 -113
  54. package/fesm/services/branch.service.js +11 -45
  55. package/fesm/services/company.service.js +8 -37
  56. package/fesm/services/index.js +1 -0
  57. package/fesm/services/user-permission.service.js +9 -27
  58. package/fesm/services/user.service.js +178 -100
  59. package/fesm/strategies/jwt.strategy.js +14 -1
  60. package/interfaces/auth-email-provider.interface.d.ts +6 -0
  61. package/interfaces/auth-module-options.interface.d.ts +4 -1
  62. package/interfaces/authentication.interface.d.ts +14 -0
  63. package/interfaces/index.d.ts +2 -0
  64. package/interfaces/user-enricher.interface.d.ts +67 -0
  65. package/interfaces/user.interface.d.ts +1 -0
  66. package/package.json +3 -3
  67. package/services/auth-config.service.d.ts +1 -0
  68. package/services/auth-email.service.d.ts +50 -0
  69. package/services/authentication.service.d.ts +21 -5
  70. package/services/branch.service.d.ts +2 -5
  71. package/services/company.service.d.ts +1 -4
  72. package/services/index.d.ts +1 -0
  73. package/services/user.service.d.ts +17 -6
@@ -34,17 +34,16 @@ import { ErrorHandler } from '@flusys/nestjs-shared/utils';
34
34
  import { AuthConfigService } from './auth-config.service';
35
35
  import { AuthDataSourceProvider } from './auth-datasource.provider';
36
36
  import { PermissionType } from '../enums';
37
- // Password hashing configuration
38
- const BCRYPT_SALT_ROUNDS = 12; // Increased from 10 for better security
37
+ import { BCRYPT_SALT_ROUNDS, TOKEN_REVOKED_PREFIX } from '../config';
38
+ import { HybridCache } from '@flusys/nestjs-shared/classes';
39
+ import { AuthEmailService } from './auth-email.service';
39
40
  import { CompanyBranch } from '../entities/company-branch.entity';
40
41
  import { Company } from '../entities/company.entity';
41
42
  import { UserCompanyPermission } from '../entities/user-company-permission.entity';
42
43
  import { User } from '../entities/user.entity';
43
44
  import { CompanySelectionSessionService } from './company-selection-session.service';
44
45
  export class AuthenticationService {
45
- // ==========================================================================
46
- // REPOSITORY GETTERS
47
- // ==========================================================================
46
+ // Repository Getters
48
47
  async getUserRepository() {
49
48
  if (this.dataSourceProvider) {
50
49
  return this.dataSourceProvider.getRepository(User);
@@ -75,26 +74,26 @@ export class AuthenticationService {
75
74
  }
76
75
  return this.injectedBranchRepository ?? null;
77
76
  }
78
- // ==========================================================================
79
- // LOGIN
80
- // ==========================================================================
81
- /**
82
- * Login with email and password
83
- * Returns either tokens directly or requires company/branch selection
84
- */ async login(email, password) {
77
+ // Login
78
+ /** Login with email and password. Returns tokens or requires company/branch selection. */ async login(email, password) {
85
79
  const user = await this.validateUser(email, password);
86
80
  if (!user.isActive) {
87
81
  throw new UnauthorizedException('User account is deactivated');
88
82
  }
89
- // If company feature not enabled, return simple token
83
+ // Check email verification if email service is enabled
84
+ if (this.authEmailService?.isEmailEnabled() && !user.emailVerified) {
85
+ return {
86
+ requiresEmailVerification: true,
87
+ email: user.email,
88
+ message: 'Please verify your email address before logging in.'
89
+ };
90
+ }
90
91
  if (!this.authConfig.isCompanyFeatureEnabled()) {
91
92
  return this.generateLoginResponse(user);
92
93
  }
93
94
  return this.handleCompanyBasedLogin(user);
94
95
  }
95
- /**
96
- * Handle login when company feature is enabled
97
- */ async handleCompanyBasedLogin(user) {
96
+ /** Handle login when company feature is enabled */ async handleCompanyBasedLogin(user) {
98
97
  const permissionRepository = await this.getPermissionRepository();
99
98
  const branchRepository = await this.getBranchRepository();
100
99
  if (!permissionRepository || !branchRepository) {
@@ -144,16 +143,9 @@ export class AuthenticationService {
144
143
  // Returns all companies with their branches for frontend to display
145
144
  return await this.generateCompanySelectionResponse(user, companies, branches);
146
145
  }
147
- // ==========================================================================
148
- // REGISTRATION
149
- // ==========================================================================
150
- /**
151
- * Register a new user
152
- * If company feature enabled, optionally join/create company
153
- */ async register(dto) {
146
+ // Registration
147
+ /** Register a new user. If email enabled, creates pending registration. Otherwise creates user directly. */ async register(dto, verifyUrlBase) {
154
148
  const userRepository = await this.getUserRepository();
155
- // Check if user exists
156
- // Note: Generic error message to prevent email enumeration attacks
157
149
  const existingUser = await userRepository.findOne({
158
150
  where: {
159
151
  email: dto.email
@@ -162,7 +154,33 @@ export class AuthenticationService {
162
154
  if (existingUser) {
163
155
  throw new BadRequestException('Registration failed. Please try again or contact support.');
164
156
  }
165
- // Create user
157
+ // If email verification is enabled, create pending registration instead of user
158
+ if (this.authEmailService?.isEmailEnabled() && verifyUrlBase) {
159
+ const companyData = this.authConfig.isCompanyFeatureEnabled() ? {
160
+ companySlug: dto.companySlug,
161
+ branchSlug: dto.branchSlug,
162
+ newCompanyName: dto.newCompanyName,
163
+ newCompanyPhone: dto.newCompanyPhone,
164
+ newCompanyAddress: dto.newCompanyAddress
165
+ } : undefined;
166
+ const result = await this.authEmailService.createPendingRegistration({
167
+ email: dto.email,
168
+ name: dto.name,
169
+ phone: dto.phone,
170
+ password: dto.password,
171
+ companyData,
172
+ verifyUrlBase
173
+ });
174
+ return {
175
+ requiresEmailVerification: true,
176
+ message: result.message
177
+ };
178
+ }
179
+ // No email verification - create user directly
180
+ return this.createUserDirectly(dto);
181
+ }
182
+ /** Create user directly (no email verification) */ async createUserDirectly(dto) {
183
+ const userRepository = await this.getUserRepository();
166
184
  const hashedPassword = await bcrypt.hash(dto.password, BCRYPT_SALT_ROUNDS);
167
185
  const user = userRepository.create({
168
186
  name: dto.name,
@@ -174,40 +192,68 @@ export class AuthenticationService {
174
192
  phoneVerified: false
175
193
  });
176
194
  await userRepository.save(user);
177
- // If company feature not enabled, return simple response
178
195
  if (!this.authConfig.isCompanyFeatureEnabled()) {
179
- return this.generateLoginResponse(user, false);
196
+ return this.generateRegistrationResponse(user, false, false);
197
+ }
198
+ return this.handleCompanyRegistration(user, dto, false);
199
+ }
200
+ /** Create user from pending registration (after email verification) */ async createUserFromPendingRegistration(pending) {
201
+ const userRepository = await this.getUserRepository();
202
+ // Check if user already exists (race condition protection)
203
+ const existingUser = await userRepository.findOne({
204
+ where: {
205
+ email: pending.email
206
+ }
207
+ });
208
+ if (existingUser) {
209
+ throw new BadRequestException('This email has already been registered.');
180
210
  }
181
- // Handle company feature
182
- return this.handleCompanyRegistration(user, dto);
211
+ // Create user with verified email
212
+ const user = userRepository.create({
213
+ name: pending.name,
214
+ email: pending.email,
215
+ phone: pending.phone,
216
+ password: pending.passwordHash,
217
+ isActive: true,
218
+ emailVerified: true,
219
+ phoneVerified: false
220
+ });
221
+ await userRepository.save(user);
222
+ // Handle company feature if enabled
223
+ if (this.authConfig.isCompanyFeatureEnabled() && pending.companyData) {
224
+ return this.handleCompanyRegistrationFromPending(user, pending.companyData);
225
+ }
226
+ return this.generateRegistrationResponse(user, this.authConfig.isCompanyFeatureEnabled(), false);
227
+ }
228
+ /** Handle company registration from pending registration data */ async handleCompanyRegistrationFromPending(user, companyData) {
229
+ return this.processCompanyRegistration(user, companyData, false);
230
+ }
231
+ /** Handle company-related registration logic */ async handleCompanyRegistration(user, dto, requiresEmailVerification) {
232
+ return this.processCompanyRegistration(user, dto, requiresEmailVerification);
183
233
  }
184
- /**
185
- * Handle company-related registration logic
186
- */ async handleCompanyRegistration(user, dto) {
234
+ /** Shared logic for company registration (join existing or create new) */ async processCompanyRegistration(user, data, requiresEmailVerification) {
187
235
  const companyRepository = await this.getCompanyRepository();
188
236
  const branchRepository = await this.getBranchRepository();
189
237
  const permissionRepository = await this.getPermissionRepository();
190
238
  if (!companyRepository || !branchRepository || !permissionRepository) {
191
- return this.generateLoginResponse(user, true);
239
+ return this.generateRegistrationResponse(user, true, requiresEmailVerification);
192
240
  }
193
241
  let company = null;
194
242
  let branch = null;
195
- // Case 1: Join existing company by slug
196
- if (dto.companySlug) {
243
+ if (data.companySlug) {
197
244
  company = await companyRepository.findOne({
198
245
  where: {
199
- slug: dto.companySlug,
246
+ slug: data.companySlug,
200
247
  isActive: true
201
248
  }
202
249
  });
203
250
  if (!company) {
204
251
  throw new BadRequestException('Company not found or inactive');
205
252
  }
206
- // Find branch by slug or get default
207
- if (dto.branchSlug) {
253
+ if (data.branchSlug) {
208
254
  branch = await branchRepository.findOne({
209
255
  where: {
210
- slug: dto.branchSlug,
256
+ slug: data.branchSlug,
211
257
  companyId: company.id,
212
258
  isActive: true
213
259
  }
@@ -216,7 +262,6 @@ export class AuthenticationService {
216
262
  throw new BadRequestException('Branch not found or inactive');
217
263
  }
218
264
  } else {
219
- // Get first active branch
220
265
  branch = await branchRepository.findOne({
221
266
  where: {
222
267
  companyId: company.id,
@@ -228,9 +273,8 @@ export class AuthenticationService {
228
273
  }
229
274
  });
230
275
  }
231
- } else if (dto.newCompanyName) {
232
- const slug = this.generateSlug(dto.newCompanyName);
233
- // Check if slug exists
276
+ } else if (data.newCompanyName) {
277
+ const slug = this.generateSlug(data.newCompanyName);
234
278
  const existingCompany = await companyRepository.findOne({
235
279
  where: {
236
280
  slug
@@ -239,16 +283,14 @@ export class AuthenticationService {
239
283
  if (existingCompany) {
240
284
  throw new BadRequestException('Company creation failed. Please try a different name.');
241
285
  }
242
- // Create company
243
286
  company = companyRepository.create({
244
- name: dto.newCompanyName,
287
+ name: data.newCompanyName,
245
288
  slug,
246
- phone: dto.newCompanyPhone,
247
- address: dto.newCompanyAddress,
289
+ phone: data.newCompanyPhone,
290
+ address: data.newCompanyAddress,
248
291
  isActive: true
249
292
  });
250
293
  await companyRepository.save(company);
251
- // Create default branch
252
294
  branch = branchRepository.create({
253
295
  name: 'Main Branch',
254
296
  slug: 'main-branch',
@@ -257,16 +299,13 @@ export class AuthenticationService {
257
299
  });
258
300
  await branchRepository.save(branch);
259
301
  }
260
- // Create permissions if company exists
261
302
  if (company) {
262
- // Create company permission
263
303
  const companyPermission = permissionRepository.create({
264
304
  userId: user.id,
265
305
  permissionType: PermissionType.COMPANY,
266
306
  targetId: company.id
267
307
  });
268
308
  await permissionRepository.save(companyPermission);
269
- // Create branch permission if branch exists
270
309
  if (branch) {
271
310
  const branchPermission = permissionRepository.create({
272
311
  userId: user.id,
@@ -275,23 +314,15 @@ export class AuthenticationService {
275
314
  });
276
315
  await permissionRepository.save(branchPermission);
277
316
  }
278
- return this.generateLoginResponseWithCompany(user, company, branch ?? undefined);
317
+ return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined, requiresEmailVerification);
279
318
  }
280
- // No company assigned
281
- return this.generateLoginResponse(user, true);
319
+ return this.generateRegistrationResponse(user, true, requiresEmailVerification);
282
320
  }
283
- /**
284
- * Generate URL-friendly slug from name
285
- */ generateSlug(name) {
321
+ /** Generate URL-friendly slug from name */ generateSlug(name) {
286
322
  return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').substring(0, 100);
287
323
  }
288
- // ==========================================================================
289
- // COMPANY/BRANCH SELECTION
290
- // ==========================================================================
291
- /**
292
- * Select company and branch (step 2 of login)
293
- * Frontend receives all options and user selects both together
294
- */ async select(sessionId, companyId, branchId) {
324
+ // Company/Branch Selection
325
+ /** Select company and branch (step 2 of login). Frontend receives all options and user selects both. */ async select(sessionId, companyId, branchId) {
295
326
  this.ensureCompanyFeatureEnabled();
296
327
  if (!this.sessionService) {
297
328
  throw new BadRequestException('Session service not available');
@@ -358,9 +389,7 @@ export class AuthenticationService {
358
389
  }
359
390
  return this.generateLoginResponseWithCompany(user, company, branch);
360
391
  }
361
- /**
362
- * Switch company/branch after login
363
- */ async switchCompany(userId, companyId, branchId) {
392
+ /** Switch company/branch after login */ async switchCompany(userId, companyId, branchId) {
364
393
  this.ensureCompanyFeatureEnabled();
365
394
  const userRepository = await this.getUserRepository();
366
395
  const user = await userRepository.findOne({
@@ -419,13 +448,8 @@ export class AuthenticationService {
419
448
  }
420
449
  return this.generateLoginResponseWithCompany(user, company, branch);
421
450
  }
422
- // ==========================================================================
423
- // TOKEN MANAGEMENT
424
- // ==========================================================================
425
- /**
426
- * Refresh access token (with token rotation)
427
- * Returns new access token and new refresh token
428
- */ async refreshToken(refreshToken) {
451
+ // Token Management
452
+ /** Refresh access token (with token rotation). Returns new access token and new refresh token. */ async refreshToken(refreshToken) {
429
453
  try {
430
454
  const payload = this.jwtService.verify(refreshToken, {
431
455
  secret: this.authConfig.getRefreshTokenSecret()
@@ -491,9 +515,7 @@ export class AuthenticationService {
491
515
  throw new UnauthorizedException('Invalid or expired refresh token');
492
516
  }
493
517
  }
494
- /**
495
- * Change password
496
- */ async changePassword(userId, currentPassword, newPassword) {
518
+ /** Change password */ async changePassword(userId, currentPassword, newPassword) {
497
519
  const userRepository = await this.getUserRepository();
498
520
  const user = await userRepository.findOne({
499
521
  where: {
@@ -510,15 +532,50 @@ export class AuthenticationService {
510
532
  user.password = await bcrypt.hash(newPassword, BCRYPT_SALT_ROUNDS);
511
533
  await userRepository.save(user);
512
534
  }
513
- /**
514
- * Logout (placeholder for token blacklisting if needed)
515
- */ async logout(_userId) {}
516
- // ==========================================================================
517
- // QUERY METHODS
518
- // ==========================================================================
519
- /**
520
- * Get user's companies
521
- */ async getUserCompanies(userId) {
535
+ /** Logout - Revokes all tokens. Stores revocation timestamp in cache. */ async logout(userId) {
536
+ if (!this.cacheManager) {
537
+ // Cache not available - graceful degradation (client-side logout only)
538
+ return;
539
+ }
540
+ const revokedAt = Math.floor(Date.now() / 1000); // Unix timestamp in seconds
541
+ const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
542
+ // TTL matches refresh token expiry (tokens older than this are already expired)
543
+ const refreshTokenExpiry = this.authConfig.getRefreshTokenExpiration();
544
+ const ttlMs = this.parseExpiryToMs(refreshTokenExpiry);
545
+ await this.cacheManager.set(cacheKey, revokedAt, ttlMs);
546
+ }
547
+ /** Check if a token is revoked (issued before user's revocation timestamp) */ async isTokenRevoked(userId, tokenIssuedAt) {
548
+ if (!this.cacheManager || !tokenIssuedAt) {
549
+ return false;
550
+ }
551
+ const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
552
+ const revokedAt = await this.cacheManager.get(cacheKey);
553
+ if (!revokedAt) {
554
+ return false;
555
+ }
556
+ // Token is revoked if it was issued before the revocation timestamp
557
+ return tokenIssuedAt < revokedAt;
558
+ }
559
+ /** Parse JWT expiry string to milliseconds */ parseExpiryToMs(expiry) {
560
+ const match = expiry.match(/^(\d+)([smhd])$/);
561
+ if (!match) return 7 * 24 * 60 * 60 * 1000; // Default 7 days
562
+ const value = parseInt(match[1], 10);
563
+ const unit = match[2];
564
+ switch(unit){
565
+ case 's':
566
+ return value * 1000;
567
+ case 'm':
568
+ return value * 60 * 1000;
569
+ case 'h':
570
+ return value * 60 * 60 * 1000;
571
+ case 'd':
572
+ return value * 24 * 60 * 60 * 1000;
573
+ default:
574
+ return 7 * 24 * 60 * 60 * 1000;
575
+ }
576
+ }
577
+ // Query Methods
578
+ /** Get user's companies */ async getUserCompanies(userId) {
522
579
  this.ensureCompanyFeatureEnabled();
523
580
  const permissionRepository = await this.getPermissionRepository();
524
581
  const companyRepository = await this.getCompanyRepository();
@@ -539,9 +596,7 @@ export class AuthenticationService {
539
596
  active: true
540
597
  }).orderBy('c.name', 'ASC').getMany();
541
598
  }
542
- /**
543
- * Get branches for a company that user has access to
544
- */ async getUserBranches(userId, companyId) {
599
+ /** Get branches for a company that user has access to */ async getUserBranches(userId, companyId) {
545
600
  this.ensureCompanyFeatureEnabled();
546
601
  const permissionRepository = await this.getPermissionRepository();
547
602
  const branchRepository = await this.getBranchRepository();
@@ -576,9 +631,7 @@ export class AuthenticationService {
576
631
  active: true
577
632
  }).orderBy('b.serial', 'ASC').addOrderBy('b.name', 'ASC').getMany();
578
633
  }
579
- /**
580
- * Get company by ID
581
- */ async getCompanyById(companyId) {
634
+ /** Get company by ID */ async getCompanyById(companyId) {
582
635
  const companyRepository = await this.getCompanyRepository();
583
636
  if (!companyRepository) {
584
637
  return null;
@@ -589,9 +642,7 @@ export class AuthenticationService {
589
642
  }
590
643
  });
591
644
  }
592
- /**
593
- * Get branch by ID
594
- */ async getBranchById(branchId) {
645
+ /** Get branch by ID */ async getBranchById(branchId) {
595
646
  const branchRepository = await this.getBranchRepository();
596
647
  if (!branchRepository) {
597
648
  return null;
@@ -602,10 +653,7 @@ export class AuthenticationService {
602
653
  }
603
654
  });
604
655
  }
605
- /**
606
- * Get user info for /auth/me endpoint
607
- * Returns only user, company, and branch information (no tokens)
608
- */ async getUserInfo(userId, companyId, branchId) {
656
+ /** Get user info for /auth/me endpoint. Returns user, company, and branch info (no tokens). */ async getUserInfo(userId, companyId, branchId) {
609
657
  const userRepository = await this.getUserRepository();
610
658
  const user = await userRepository.findOne({
611
659
  where: {
@@ -621,6 +669,7 @@ export class AuthenticationService {
621
669
  id: user.id,
622
670
  email: user.email,
623
671
  name: user.name ?? undefined,
672
+ phone: user.phone ?? undefined,
624
673
  profilePictureId: user.profilePictureId ?? undefined
625
674
  }
626
675
  };
@@ -656,9 +705,7 @@ export class AuthenticationService {
656
705
  }
657
706
  return response;
658
707
  }
659
- // ==========================================================================
660
- // HELPER METHODS
661
- // ==========================================================================
708
+ // Helper Methods
662
709
  ensureCompanyFeatureEnabled() {
663
710
  if (!this.authConfig.isCompanyFeatureEnabled()) {
664
711
  throw new BadRequestException('Company feature is not enabled');
@@ -682,9 +729,7 @@ export class AuthenticationService {
682
729
  await userRepository.save(user);
683
730
  return user;
684
731
  }
685
- // ==========================================================================
686
- // RESPONSE GENERATORS
687
- // ==========================================================================
732
+ // Response Generators
688
733
  generateLoginResponse(user, companyFeatureEnabled = false) {
689
734
  const payload = {
690
735
  id: user.id,
@@ -711,6 +756,7 @@ export class AuthenticationService {
711
756
  id: user.id,
712
757
  email: user.email,
713
758
  name: user.name ?? undefined,
759
+ phone: user.phone ?? null,
714
760
  profilePictureId: user.profilePictureId ?? null
715
761
  },
716
762
  companyFeatureEnabled
@@ -746,6 +792,7 @@ export class AuthenticationService {
746
792
  id: user.id,
747
793
  email: user.email,
748
794
  name: user.name ?? undefined,
795
+ phone: user.phone ?? null,
749
796
  profilePictureId: user.profilePictureId ?? null
750
797
  },
751
798
  company: {
@@ -791,17 +838,76 @@ export class AuthenticationService {
791
838
  id: user.id,
792
839
  email: user.email,
793
840
  name: user.name ?? undefined,
841
+ phone: user.phone ?? null,
794
842
  profilePictureId: user.profilePictureId ?? null
795
843
  },
796
844
  companies: companiesWithBranches
797
845
  };
798
846
  }
847
+ /** Generate registration response - tokens included only if email verification not required */ generateRegistrationResponse(user, companyFeatureEnabled, requiresEmailVerification) {
848
+ const userInfo = {
849
+ id: user.id,
850
+ email: user.email,
851
+ name: user.name ?? undefined,
852
+ phone: user.phone ?? null,
853
+ profilePictureId: user.profilePictureId ?? null
854
+ };
855
+ if (requiresEmailVerification) {
856
+ return {
857
+ user: userInfo,
858
+ companyFeatureEnabled,
859
+ requiresEmailVerification: true
860
+ };
861
+ }
862
+ const loginResponse = this.generateLoginResponse(user, companyFeatureEnabled);
863
+ return {
864
+ ...loginResponse,
865
+ requiresEmailVerification: false
866
+ };
867
+ }
868
+ /** Generate registration response with company - tokens included only if email verification not required */ generateRegistrationResponseWithCompany(user, company, branch, requiresEmailVerification = false) {
869
+ const userInfo = {
870
+ id: user.id,
871
+ email: user.email,
872
+ name: user.name ?? undefined,
873
+ phone: user.phone ?? null,
874
+ profilePictureId: user.profilePictureId ?? null
875
+ };
876
+ const companyInfo = {
877
+ id: company.id,
878
+ name: company.name,
879
+ slug: company.slug,
880
+ logoId: company.logoId ?? null
881
+ };
882
+ const branchInfo = branch ? {
883
+ id: branch.id,
884
+ name: branch.name,
885
+ slug: branch.slug,
886
+ logoId: branch.logoId ?? null
887
+ } : undefined;
888
+ if (requiresEmailVerification) {
889
+ return {
890
+ user: userInfo,
891
+ company: companyInfo,
892
+ branch: branchInfo,
893
+ companyFeatureEnabled: true,
894
+ requiresEmailVerification: true
895
+ };
896
+ }
897
+ const loginResponse = this.generateLoginResponseWithCompany(user, company, branch);
898
+ return {
899
+ ...loginResponse,
900
+ requiresEmailVerification: false
901
+ };
902
+ }
799
903
  // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
800
- constructor(jwtService, authConfig, dataSourceProvider, sessionService, injectedUserRepository, injectedPermissionRepository, injectedCompanyRepository, injectedBranchRepository){
904
+ constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, authEmailService, injectedUserRepository, injectedPermissionRepository, injectedCompanyRepository, injectedBranchRepository){
801
905
  _define_property(this, "jwtService", void 0);
802
906
  _define_property(this, "authConfig", void 0);
907
+ _define_property(this, "cacheManager", void 0);
803
908
  _define_property(this, "dataSourceProvider", void 0);
804
909
  _define_property(this, "sessionService", void 0);
910
+ _define_property(this, "authEmailService", void 0);
805
911
  _define_property(this, "injectedUserRepository", void 0);
806
912
  _define_property(this, "injectedPermissionRepository", void 0);
807
913
  _define_property(this, "injectedCompanyRepository", void 0);
@@ -809,8 +915,10 @@ export class AuthenticationService {
809
915
  _define_property(this, "logger", void 0);
810
916
  this.jwtService = jwtService;
811
917
  this.authConfig = authConfig;
918
+ this.cacheManager = cacheManager;
812
919
  this.dataSourceProvider = dataSourceProvider;
813
920
  this.sessionService = sessionService;
921
+ this.authEmailService = authEmailService;
814
922
  this.injectedUserRepository = injectedUserRepository;
815
923
  this.injectedPermissionRepository = injectedPermissionRepository;
816
924
  this.injectedCompanyRepository = injectedCompanyRepository;
@@ -825,23 +933,29 @@ AuthenticationService = _ts_decorate([
825
933
  _ts_param(0, Inject(JwtService)),
826
934
  _ts_param(1, Inject(AuthConfigService)),
827
935
  _ts_param(2, Optional()),
828
- _ts_param(2, Inject(AuthDataSourceProvider)),
936
+ _ts_param(2, Inject('CACHE_INSTANCE')),
829
937
  _ts_param(3, Optional()),
830
- _ts_param(3, Inject(CompanySelectionSessionService)),
938
+ _ts_param(3, Inject(AuthDataSourceProvider)),
831
939
  _ts_param(4, Optional()),
832
- _ts_param(4, InjectRepository(User)),
940
+ _ts_param(4, Inject(CompanySelectionSessionService)),
833
941
  _ts_param(5, Optional()),
834
- _ts_param(5, InjectRepository(UserCompanyPermission)),
942
+ _ts_param(5, Inject(AuthEmailService)),
835
943
  _ts_param(6, Optional()),
836
- _ts_param(6, InjectRepository(Company)),
944
+ _ts_param(6, InjectRepository(User)),
837
945
  _ts_param(7, Optional()),
838
- _ts_param(7, InjectRepository(CompanyBranch)),
946
+ _ts_param(7, InjectRepository(UserCompanyPermission)),
947
+ _ts_param(8, Optional()),
948
+ _ts_param(8, InjectRepository(Company)),
949
+ _ts_param(9, Optional()),
950
+ _ts_param(9, InjectRepository(CompanyBranch)),
839
951
  _ts_metadata("design:type", Function),
840
952
  _ts_metadata("design:paramtypes", [
841
953
  typeof JwtService === "undefined" ? Object : JwtService,
842
954
  typeof AuthConfigService === "undefined" ? Object : AuthConfigService,
955
+ typeof HybridCache === "undefined" ? Object : HybridCache,
843
956
  typeof AuthDataSourceProvider === "undefined" ? Object : AuthDataSourceProvider,
844
957
  typeof CompanySelectionSessionService === "undefined" ? Object : CompanySelectionSessionService,
958
+ typeof AuthEmailService === "undefined" ? Object : AuthEmailService,
845
959
  typeof Repository === "undefined" ? Object : Repository,
846
960
  typeof Repository === "undefined" ? Object : Repository,
847
961
  typeof Repository === "undefined" ? Object : Repository,