@flusys/nestjs-auth 1.0.0-rc → 2.0.0-rc.0

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 (58) hide show
  1. package/README.md +530 -396
  2. package/cjs/controllers/authentication.controller.js +160 -28
  3. package/cjs/controllers/user-permission.controller.js +49 -136
  4. package/cjs/docs/auth-swagger.config.js +14 -7
  5. package/cjs/dtos/authentication.dto.js +0 -30
  6. package/cjs/dtos/index.js +0 -1
  7. package/cjs/interceptors/clear-token.interceptor.js +8 -18
  8. package/cjs/interceptors/set-token.interceptor.js +8 -55
  9. package/cjs/interfaces/auth-email-provider.interface.js +1 -1
  10. package/cjs/modules/auth.module.js +63 -129
  11. package/cjs/services/auth-config.service.js +6 -29
  12. package/cjs/services/auth-datasource.provider.js +8 -20
  13. package/cjs/services/auth-email.service.js +72 -87
  14. package/cjs/services/authentication.service.js +211 -355
  15. package/cjs/services/branch.service.js +9 -5
  16. package/cjs/services/user-permission.service.js +43 -71
  17. package/cjs/services/user.service.js +18 -47
  18. package/cjs/strategies/jwt.strategy.js +44 -44
  19. package/controllers/authentication.controller.d.ts +5 -3
  20. package/controllers/user-permission.controller.d.ts +5 -4
  21. package/dtos/authentication.dto.d.ts +0 -4
  22. package/dtos/index.d.ts +0 -1
  23. package/entities/index.d.ts +3 -3
  24. package/fesm/controllers/authentication.controller.js +162 -30
  25. package/fesm/controllers/user-permission.controller.js +50 -137
  26. package/fesm/docs/auth-swagger.config.js +14 -7
  27. package/fesm/dtos/authentication.dto.js +0 -30
  28. package/fesm/dtos/index.js +0 -1
  29. package/fesm/interceptors/clear-token.interceptor.js +8 -18
  30. package/fesm/interceptors/set-token.interceptor.js +8 -55
  31. package/fesm/interfaces/auth-email-provider.interface.js +1 -1
  32. package/fesm/modules/auth.module.js +63 -129
  33. package/fesm/services/auth-config.service.js +6 -29
  34. package/fesm/services/auth-datasource.provider.js +8 -20
  35. package/fesm/services/auth-email.service.js +72 -87
  36. package/fesm/services/authentication.service.js +211 -355
  37. package/fesm/services/branch.service.js +9 -5
  38. package/fesm/services/user-permission.service.js +44 -72
  39. package/fesm/services/user.service.js +18 -47
  40. package/fesm/strategies/jwt.strategy.js +44 -44
  41. package/interceptors/clear-token.interceptor.d.ts +0 -1
  42. package/interceptors/set-token.interceptor.d.ts +1 -2
  43. package/interfaces/authentication.interface.d.ts +0 -4
  44. package/modules/auth.module.d.ts +6 -2
  45. package/package.json +3 -3
  46. package/services/auth-config.service.d.ts +2 -7
  47. package/services/auth-datasource.provider.d.ts +2 -4
  48. package/services/auth-email.service.d.ts +7 -4
  49. package/services/authentication.service.d.ts +14 -8
  50. package/services/user-permission.service.d.ts +6 -8
  51. package/services/user.service.d.ts +1 -1
  52. package/strategies/jwt.strategy.d.ts +4 -1
  53. package/cjs/dtos/user-company-permission.dto.js +0 -110
  54. package/cjs/guards/auth-throttler.guard.js +0 -53
  55. package/dtos/user-company-permission.dto.d.ts +0 -10
  56. package/fesm/dtos/user-company-permission.dto.js +0 -98
  57. package/fesm/guards/auth-throttler.guard.js +0 -43
  58. package/guards/auth-throttler.guard.d.ts +0 -7
@@ -8,20 +8,20 @@ Object.defineProperty(exports, "AuthenticationService", {
8
8
  return AuthenticationService;
9
9
  }
10
10
  });
11
+ const _classes = require("@flusys/nestjs-shared/classes");
12
+ const _utils = require("@flusys/nestjs-shared/utils");
11
13
  const _common = require("@nestjs/common");
12
14
  const _jwt = require("@nestjs/jwt");
13
15
  const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
14
- const _utils = require("@flusys/nestjs-shared/utils");
15
- const _authconfigservice = require("./auth-config.service");
16
- const _authdatasourceprovider = require("./auth-datasource.provider");
17
- const _enums = require("../enums");
18
16
  const _config = require("../config");
19
- const _classes = require("@flusys/nestjs-shared/classes");
20
- const _authemailservice = require("./auth-email.service");
21
17
  const _companybranchentity = require("../entities/company-branch.entity");
22
18
  const _companyentity = require("../entities/company.entity");
23
19
  const _usercompanypermissionentity = require("../entities/user-company-permission.entity");
24
20
  const _userentity = require("../entities/user.entity");
21
+ const _enums = require("../enums");
22
+ const _authconfigservice = require("./auth-config.service");
23
+ const _authdatasourceprovider = require("./auth-datasource.provider");
24
+ const _authemailservice = require("./auth-email.service");
25
25
  const _companyselectionsessionservice = require("./company-selection-session.service");
26
26
  function _define_property(obj, key, value) {
27
27
  if (key in obj) {
@@ -92,6 +92,10 @@ function _ts_param(paramIndex, decorator) {
92
92
  };
93
93
  }
94
94
  let AuthenticationService = class AuthenticationService {
95
+ // Feature Flags
96
+ /** Check if company feature is enabled (tenant-aware with global fallback) */ isCompanyFeatureEnabled() {
97
+ return this.dataSourceProvider?.isCompanyFeatureEnabled() ?? this.authConfig.isCompanyFeatureEnabled();
98
+ }
95
99
  // Repository Getters
96
100
  async getUserRepository() {
97
101
  if (!this.dataSourceProvider) {
@@ -100,24 +104,31 @@ let AuthenticationService = class AuthenticationService {
100
104
  return this.dataSourceProvider.getRepository(_userentity.User);
101
105
  }
102
106
  async getPermissionRepository() {
103
- if (!this.authConfig.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
107
+ if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
104
108
  return this.dataSourceProvider.getRepository(_usercompanypermissionentity.UserCompanyPermission);
105
109
  }
106
110
  async getCompanyRepository() {
107
- if (!this.authConfig.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
111
+ if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
108
112
  return this.dataSourceProvider.getRepository(_companyentity.Company);
109
113
  }
110
114
  async getBranchRepository() {
111
- if (!this.authConfig.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
115
+ if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
112
116
  return this.dataSourceProvider.getRepository(_companybranchentity.CompanyBranch);
113
117
  }
114
118
  // Login
115
- /** Login with email and password. Returns tokens or requires company/branch selection. */ async login(email, password) {
119
+ 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)) {
122
+ return {
123
+ requiresEmailVerification: true,
124
+ email,
125
+ message: 'Please verify your email address to complete registration.'
126
+ };
127
+ }
116
128
  const user = await this.validateUser(email, password);
117
129
  if (!user.isActive) {
118
130
  throw new _common.UnauthorizedException('User account is deactivated');
119
131
  }
120
- // Check email verification if email service is enabled
121
132
  if (this.authEmailService?.isEmailEnabled() && !user.emailVerified) {
122
133
  return {
123
134
  requiresEmailVerification: true,
@@ -125,7 +136,7 @@ let AuthenticationService = class AuthenticationService {
125
136
  message: 'Please verify your email address before logging in.'
126
137
  };
127
138
  }
128
- if (!this.authConfig.isCompanyFeatureEnabled()) {
139
+ if (!this.isCompanyFeatureEnabled()) {
129
140
  return this.generateLoginResponse(user);
130
141
  }
131
142
  return this.handleCompanyBasedLogin(user);
@@ -157,7 +168,7 @@ let AuthenticationService = class AuthenticationService {
157
168
  // If user has no company assignments yet, return simple login response
158
169
  // This allows users to login and be assigned to companies later
159
170
  if (companies.length === 0) {
160
- return this.generateLoginResponse(user, true);
171
+ return this.generateLoginResponse(user);
161
172
  }
162
173
  // Get branch permissions
163
174
  const branchPermissions = await permissionRepository.find({
@@ -188,12 +199,17 @@ let AuthenticationService = class AuthenticationService {
188
199
  email: dto.email
189
200
  }
190
201
  });
202
+ // When company feature is enabled and user exists, allow joining/creating company
203
+ if (existingUser && this.isCompanyFeatureEnabled()) {
204
+ return this.handleExistingUserCompanyRegistration(existingUser, dto);
205
+ }
206
+ // User exists but company feature is off - email must be unique
191
207
  if (existingUser) {
192
- throw new _common.BadRequestException('Registration failed. Please try again or contact support.');
208
+ throw new _common.BadRequestException('Registration failed due to email exists. Please try again or contact support.');
193
209
  }
194
210
  // If email verification is enabled, create pending registration instead of user
195
211
  if (this.authEmailService?.isEmailEnabled() && verifyUrlBase) {
196
- const companyData = this.authConfig.isCompanyFeatureEnabled() ? {
212
+ const companyData = this.isCompanyFeatureEnabled() ? {
197
213
  companySlug: dto.companySlug,
198
214
  branchSlug: dto.branchSlug,
199
215
  newCompanyName: dto.newCompanyName,
@@ -216,6 +232,38 @@ let AuthenticationService = class AuthenticationService {
216
232
  // No email verification - create user directly
217
233
  return this.createUserDirectly(dto);
218
234
  }
235
+ /** Handle registration for existing user when company feature is enabled */ async handleExistingUserCompanyRegistration(existingUser, dto) {
236
+ if (!dto.companySlug && !dto.newCompanyName) {
237
+ throw new _common.BadRequestException('Email already registered. Please login or provide a company to join/create.');
238
+ }
239
+ const permissionRepository = await this.getPermissionRepository();
240
+ const companyRepository = await this.getCompanyRepository();
241
+ if (!permissionRepository || !companyRepository) {
242
+ throw new _common.BadRequestException('Company feature repositories not available');
243
+ }
244
+ // For existing users joining a company, check if already assigned
245
+ if (dto.companySlug) {
246
+ const company = await companyRepository.findOne({
247
+ where: {
248
+ slug: dto.companySlug,
249
+ isActive: true
250
+ }
251
+ });
252
+ if (company) {
253
+ const existingPermission = await permissionRepository.findOne({
254
+ where: {
255
+ userId: existingUser.id,
256
+ permissionType: _enums.PermissionType.COMPANY,
257
+ targetId: company.id
258
+ }
259
+ });
260
+ if (existingPermission) {
261
+ throw new _common.BadRequestException('You are already assigned to this company. Please login.');
262
+ }
263
+ }
264
+ }
265
+ return this.processCompanyRegistration(existingUser, dto, true);
266
+ }
219
267
  /** Create user directly (no email verification) */ async createUserDirectly(dto) {
220
268
  const userRepository = await this.getUserRepository();
221
269
  const hashedPassword = await _bcrypt.hash(dto.password, _config.BCRYPT_SALT_ROUNDS);
@@ -229,14 +277,13 @@ let AuthenticationService = class AuthenticationService {
229
277
  phoneVerified: false
230
278
  });
231
279
  await userRepository.save(user);
232
- if (!this.authConfig.isCompanyFeatureEnabled()) {
233
- return this.generateRegistrationResponse(user, false, false);
280
+ if (!this.isCompanyFeatureEnabled()) {
281
+ return this.generateRegistrationResponse(user);
234
282
  }
235
- return this.handleCompanyRegistration(user, dto, false);
283
+ return this.processCompanyRegistration(user, dto);
236
284
  }
237
285
  /** Create user from pending registration (after email verification) */ async createUserFromPendingRegistration(pending) {
238
286
  const userRepository = await this.getUserRepository();
239
- // Check if user already exists (race condition protection)
240
287
  const existingUser = await userRepository.findOne({
241
288
  where: {
242
289
  email: pending.email
@@ -245,7 +292,6 @@ let AuthenticationService = class AuthenticationService {
245
292
  if (existingUser) {
246
293
  throw new _common.BadRequestException('This email has already been registered.');
247
294
  }
248
- // Create user with verified email
249
295
  const user = userRepository.create({
250
296
  name: pending.name,
251
297
  email: pending.email,
@@ -256,107 +302,103 @@ let AuthenticationService = class AuthenticationService {
256
302
  phoneVerified: false
257
303
  });
258
304
  await userRepository.save(user);
259
- // Handle company feature if enabled
260
- if (this.authConfig.isCompanyFeatureEnabled() && pending.companyData) {
261
- return this.handleCompanyRegistrationFromPending(user, pending.companyData);
305
+ if (this.isCompanyFeatureEnabled() && pending.companyData) {
306
+ return this.processCompanyRegistration(user, pending.companyData);
262
307
  }
263
- return this.generateRegistrationResponse(user, this.authConfig.isCompanyFeatureEnabled(), false);
264
- }
265
- /** Handle company registration from pending registration data */ async handleCompanyRegistrationFromPending(user, companyData) {
266
- return this.processCompanyRegistration(user, companyData, false);
308
+ return this.generateRegistrationResponse(user);
267
309
  }
268
- /** Handle company-related registration logic */ async handleCompanyRegistration(user, dto, requiresEmailVerification) {
269
- return this.processCompanyRegistration(user, dto, requiresEmailVerification);
270
- }
271
- /** Shared logic for company registration (join existing or create new) */ async processCompanyRegistration(user, data, requiresEmailVerification) {
310
+ /** Process company registration (join existing or create new) */ async processCompanyRegistration(user, data, throwOnUnavailable = false) {
272
311
  const companyRepository = await this.getCompanyRepository();
273
312
  const branchRepository = await this.getBranchRepository();
274
313
  const permissionRepository = await this.getPermissionRepository();
275
314
  if (!companyRepository || !branchRepository || !permissionRepository) {
276
- return this.generateRegistrationResponse(user, true, requiresEmailVerification);
315
+ if (throwOnUnavailable) throw new _common.BadRequestException('Company feature repositories not available');
316
+ return this.generateRegistrationResponse(user);
277
317
  }
278
- let company = null;
279
- let branch = null;
318
+ const { company, branch } = await this.findOrCreateCompanyAndBranch(data, companyRepository, branchRepository);
319
+ if (company) {
320
+ await this.assignUserPermissions(user.id, company.id, branch?.id, permissionRepository);
321
+ return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined);
322
+ }
323
+ return this.generateRegistrationResponse(user);
324
+ }
325
+ /** Find existing company/branch or create new ones */ async findOrCreateCompanyAndBranch(data, companyRepo, branchRepo) {
280
326
  if (data.companySlug) {
281
- company = await companyRepository.findOne({
327
+ const company = await companyRepo.findOne({
282
328
  where: {
283
329
  slug: data.companySlug,
284
330
  isActive: true
285
331
  }
286
332
  });
287
- if (!company) {
288
- throw new _common.BadRequestException('Company not found or inactive');
289
- }
290
- if (data.branchSlug) {
291
- branch = await branchRepository.findOne({
292
- where: {
293
- slug: data.branchSlug,
294
- companyId: company.id,
295
- isActive: true
296
- }
297
- });
298
- if (!branch) {
299
- throw new _common.BadRequestException('Branch not found or inactive');
333
+ if (!company) throw new _common.BadRequestException('Company not found or inactive');
334
+ const branch = data.branchSlug ? await branchRepo.findOne({
335
+ where: {
336
+ slug: data.branchSlug,
337
+ companyId: company.id,
338
+ isActive: true
300
339
  }
301
- } else {
302
- branch = await branchRepository.findOne({
303
- where: {
304
- companyId: company.id,
305
- isActive: true
306
- },
307
- order: {
308
- serial: 'ASC',
309
- createdAt: 'ASC'
310
- }
311
- });
312
- }
313
- } else if (data.newCompanyName) {
314
- const slug = this.generateSlug(data.newCompanyName);
315
- const existingCompany = await companyRepository.findOne({
340
+ }) : await branchRepo.findOne({
316
341
  where: {
317
- slug
342
+ companyId: company.id,
343
+ isActive: true
344
+ },
345
+ order: {
346
+ serial: 'ASC',
347
+ createdAt: 'ASC'
318
348
  }
319
349
  });
320
- if (existingCompany) {
321
- throw new _common.BadRequestException('Company creation failed. Please try a different name.');
322
- }
323
- company = companyRepository.create({
350
+ if (data.branchSlug && !branch) throw new _common.BadRequestException('Branch not found or inactive');
351
+ return {
352
+ company,
353
+ branch
354
+ };
355
+ }
356
+ if (data.newCompanyName) {
357
+ const slug = await (0, _utils.generateUniqueSlug)(data.newCompanyName, async (baseSlug)=>{
358
+ const rows = await companyRepo.createQueryBuilder('c').select('c.slug').where('c.slug = :base OR c.slug LIKE :pattern', {
359
+ base: baseSlug,
360
+ pattern: `${baseSlug}-%`
361
+ }).getMany();
362
+ return rows.map((r)=>r.slug);
363
+ });
364
+ const company = companyRepo.create({
324
365
  name: data.newCompanyName,
325
366
  slug,
326
367
  phone: data.newCompanyPhone,
327
368
  address: data.newCompanyAddress,
328
369
  isActive: true
329
370
  });
330
- await companyRepository.save(company);
331
- branch = branchRepository.create({
371
+ await companyRepo.save(company);
372
+ const branch = branchRepo.create({
332
373
  name: 'Main Branch',
333
374
  slug: 'main-branch',
334
375
  companyId: company.id,
335
376
  isActive: true
336
377
  });
337
- await branchRepository.save(branch);
338
- }
339
- if (company) {
340
- const companyPermission = permissionRepository.create({
341
- userId: user.id,
342
- permissionType: _enums.PermissionType.COMPANY,
343
- targetId: company.id
344
- });
345
- await permissionRepository.save(companyPermission);
346
- if (branch) {
347
- const branchPermission = permissionRepository.create({
348
- userId: user.id,
349
- permissionType: _enums.PermissionType.BRANCH,
350
- targetId: branch.id
351
- });
352
- await permissionRepository.save(branchPermission);
353
- }
354
- return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined, requiresEmailVerification);
378
+ await branchRepo.save(branch);
379
+ return {
380
+ company,
381
+ branch
382
+ };
355
383
  }
356
- return this.generateRegistrationResponse(user, true, requiresEmailVerification);
384
+ return {
385
+ company: null,
386
+ branch: null
387
+ };
357
388
  }
358
- /** Generate URL-friendly slug from name */ generateSlug(name) {
359
- return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').substring(0, 100);
389
+ /** Assign user to company and optionally branch */ async assignUserPermissions(userId, companyId, branchId, permissionRepo) {
390
+ await permissionRepo.save(permissionRepo.create({
391
+ userId,
392
+ permissionType: _enums.PermissionType.COMPANY,
393
+ targetId: companyId
394
+ }));
395
+ if (branchId) {
396
+ await permissionRepo.save(permissionRepo.create({
397
+ userId,
398
+ permissionType: _enums.PermissionType.BRANCH,
399
+ targetId: branchId
400
+ }));
401
+ }
360
402
  }
361
403
  // Company/Branch Selection
362
404
  /** Select company and branch (step 2 of login). Frontend receives all options and user selects both. */ async select(sessionId, companyId, branchId) {
@@ -368,66 +410,13 @@ let AuthenticationService = class AuthenticationService {
368
410
  if (!session) {
369
411
  throw new _common.UnauthorizedException('Invalid or expired session');
370
412
  }
371
- const userRepository = await this.getUserRepository();
372
- const user = await userRepository.findOne({
373
- where: {
374
- id: session.userId
375
- }
376
- });
377
- if (!user) {
378
- throw new _common.NotFoundException('User not found');
379
- }
380
- const permissionRepository = await this.getPermissionRepository();
381
- const companyRepository = await this.getCompanyRepository();
382
- const branchRepository = await this.getBranchRepository();
383
- if (!permissionRepository || !companyRepository || !branchRepository) {
384
- throw new _common.BadRequestException('Repositories not available');
385
- }
386
- // Validate company access
387
- const companyPermission = await permissionRepository.findOne({
388
- where: {
389
- userId: user.id,
390
- permissionType: _enums.PermissionType.COMPANY,
391
- targetId: companyId
392
- }
393
- });
394
- if (!companyPermission) {
395
- throw new _common.UnauthorizedException('No access to this company');
396
- }
397
- const company = await companyRepository.findOne({
398
- where: {
399
- id: companyId,
400
- isActive: true
401
- }
402
- });
403
- if (!company) {
404
- throw new _common.UnauthorizedException('Company not found or inactive');
405
- }
406
- // Validate branch access
407
- const branchPermission = await permissionRepository.findOne({
408
- where: {
409
- userId: user.id,
410
- permissionType: _enums.PermissionType.BRANCH,
411
- targetId: branchId
412
- }
413
- });
414
- if (!branchPermission) {
415
- throw new _common.UnauthorizedException('No access to this branch');
416
- }
417
- const branch = await branchRepository.findOne({
418
- where: {
419
- id: branchId,
420
- companyId,
421
- isActive: true
422
- }
423
- });
424
- if (!branch) {
425
- throw new _common.UnauthorizedException('Branch not found or inactive');
426
- }
427
- return this.generateLoginResponseWithCompany(user, company, branch);
413
+ return this.validateAndLoginWithCompany(session.userId, companyId, branchId);
428
414
  }
429
415
  /** Switch company/branch after login */ async switchCompany(userId, companyId, branchId) {
430
416
  this.ensureCompanyFeatureEnabled();
417
+ return this.validateAndLoginWithCompany(userId, companyId, branchId);
418
+ }
419
+ /** Shared: Validate user access to company/branch and generate login response */ async validateAndLoginWithCompany(userId, companyId, branchId) {
431
420
  const userRepository = await this.getUserRepository();
432
421
  const user = await userRepository.findOne({
433
422
  where: {
@@ -444,14 +433,14 @@ let AuthenticationService = class AuthenticationService {
444
433
  throw new _common.BadRequestException('Repositories not available');
445
434
  }
446
435
  // Validate company access
447
- const companyPermission = await permissionRepository.findOne({
436
+ const hasCompanyAccess = await permissionRepository.findOne({
448
437
  where: {
449
438
  userId,
450
439
  permissionType: _enums.PermissionType.COMPANY,
451
440
  targetId: companyId
452
441
  }
453
442
  });
454
- if (!companyPermission) {
443
+ if (!hasCompanyAccess) {
455
444
  throw new _common.UnauthorizedException('No access to this company');
456
445
  }
457
446
  const company = await companyRepository.findOne({
@@ -463,14 +452,15 @@ let AuthenticationService = class AuthenticationService {
463
452
  if (!company) {
464
453
  throw new _common.UnauthorizedException('Company not found or inactive');
465
454
  }
466
- const branchPermission = await permissionRepository.findOne({
455
+ // Validate branch access
456
+ const hasBranchAccess = await permissionRepository.findOne({
467
457
  where: {
468
458
  userId,
469
459
  permissionType: _enums.PermissionType.BRANCH,
470
460
  targetId: branchId
471
461
  }
472
462
  });
473
- if (!branchPermission) {
463
+ if (!hasBranchAccess) {
474
464
  throw new _common.UnauthorizedException('No access to this branch');
475
465
  }
476
466
  const branch = await branchRepository.findOne({
@@ -491,9 +481,7 @@ let AuthenticationService = class AuthenticationService {
491
481
  const payload = this.jwtService.verify(refreshToken, {
492
482
  secret: this.authConfig.getRefreshTokenSecret()
493
483
  });
494
- // Check if token has been revoked (user logged out after token was issued)
495
- const isRevoked = await this.isTokenRevoked(payload.id, payload.iat);
496
- if (isRevoked) {
484
+ if (await this.isTokenRevoked(payload.id, payload.iat)) {
497
485
  throw new _common.UnauthorizedException('Token has been revoked');
498
486
  }
499
487
  const userRepository = await this.getUserRepository();
@@ -505,7 +493,7 @@ let AuthenticationService = class AuthenticationService {
505
493
  if (!user || !user.isActive) {
506
494
  throw new _common.UnauthorizedException('Invalid refresh token');
507
495
  }
508
- const accessPayload = {
496
+ return this.generateTokens({
509
497
  id: user.id,
510
498
  email: user.email,
511
499
  type: 'access',
@@ -515,44 +503,9 @@ let AuthenticationService = class AuthenticationService {
515
503
  },
516
504
  ...payload.branchId && {
517
505
  branchId: payload.branchId
518
- },
519
- ...payload.companyLogoId && {
520
- companyLogoId: payload.companyLogoId
521
- },
522
- ...payload.branchLogoId && {
523
- branchLogoId: payload.branchLogoId
524
- }
525
- };
526
- const refreshPayload = {
527
- id: user.id,
528
- email: user.email,
529
- type: 'refresh',
530
- profilePictureId: user.profilePictureId ?? undefined,
531
- ...payload.companyId && {
532
- companyId: payload.companyId
533
- },
534
- ...payload.branchId && {
535
- branchId: payload.branchId
536
- },
537
- ...payload.companyLogoId && {
538
- companyLogoId: payload.companyLogoId
539
- },
540
- ...payload.branchLogoId && {
541
- branchLogoId: payload.branchLogoId
542
506
  }
543
- };
544
- return {
545
- accessToken: this.jwtService.sign(accessPayload, {
546
- secret: this.authConfig.getJwtSecret(),
547
- expiresIn: this.authConfig.getJwtExpiration()
548
- }),
549
- refreshToken: this.jwtService.sign(refreshPayload, {
550
- secret: this.authConfig.getRefreshTokenSecret(),
551
- expiresIn: this.authConfig.getRefreshTokenExpiration()
552
- })
553
- };
507
+ });
554
508
  } catch (error) {
555
- // Log error details for debugging (not exposed to client)
556
509
  _utils.ErrorHandler.logError(this.logger, error, 'refreshToken');
557
510
  throw new _common.UnauthorizedException('Invalid or expired refresh token');
558
511
  }
@@ -564,11 +517,8 @@ let AuthenticationService = class AuthenticationService {
564
517
  id: userId
565
518
  }
566
519
  });
567
- if (!user) {
568
- throw new _common.UnauthorizedException('User not found');
569
- }
570
- const isPasswordValid = await _bcrypt.compare(currentPassword, user.password || '');
571
- if (!isPasswordValid) {
520
+ if (!user) throw new _common.UnauthorizedException('User not found');
521
+ if (!await _bcrypt.compare(currentPassword, user.password || '')) {
572
522
  throw new _common.UnauthorizedException('Current password is incorrect');
573
523
  }
574
524
  user.password = await _bcrypt.hash(newPassword, _config.BCRYPT_SALT_ROUNDS);
@@ -704,54 +654,28 @@ let AuthenticationService = class AuthenticationService {
704
654
  id: userId
705
655
  }
706
656
  });
707
- if (!user) {
708
- throw new _common.UnauthorizedException('User not found');
709
- }
710
- // Build response (no tokens)
657
+ if (!user) throw new _common.UnauthorizedException('User not found');
711
658
  const response = {
712
- user: {
713
- id: user.id,
714
- email: user.email,
715
- name: user.name ?? undefined,
716
- phone: user.phone ?? undefined,
717
- profilePictureId: user.profilePictureId ?? undefined
718
- }
659
+ user: this.toUserInfo(user)
719
660
  };
720
- // Add company/branch if available (when company feature is enabled)
721
- if (this.authConfig.isCompanyFeatureEnabled()) {
722
- // When company feature is enabled, companyId MUST be present
661
+ if (this.isCompanyFeatureEnabled()) {
723
662
  if (!companyId) {
724
- throw new _common.UnauthorizedException('Company feature is enabled but user has no company assigned. Please select a company.');
663
+ throw new _common.UnauthorizedException('Company feature is enabled but user has no company assigned.');
725
664
  }
726
665
  const company = await this.getCompanyById(companyId);
727
- if (!company) {
728
- throw new _common.UnauthorizedException(`Company with ID ${companyId} not found. Please select a valid company.`);
729
- }
730
- response.company = {
731
- id: company.id,
732
- name: company.name,
733
- slug: company.slug,
734
- logoId: company.logoId ?? null
735
- };
736
- // Branch is optional in some configurations, but if provided it must exist
666
+ if (!company) throw new _common.UnauthorizedException('Company not found.');
667
+ response.company = this.toCompanyInfo(company);
737
668
  if (branchId) {
738
669
  const branch = await this.getBranchById(branchId);
739
- if (!branch) {
740
- throw new _common.UnauthorizedException(`Branch with ID ${branchId} not found. Please select a valid branch.`);
741
- }
742
- response.branch = {
743
- id: branch.id,
744
- name: branch.name,
745
- slug: branch.slug,
746
- logoId: branch.logoId ?? null
747
- };
670
+ if (!branch) throw new _common.UnauthorizedException('Branch not found.');
671
+ response.branch = this.toBranchInfo(branch);
748
672
  }
749
673
  }
750
674
  return response;
751
675
  }
752
676
  // Helper Methods
753
677
  ensureCompanyFeatureEnabled() {
754
- if (!this.authConfig.isCompanyFeatureEnabled()) {
678
+ if (!this.isCompanyFeatureEnabled()) {
755
679
  throw new _common.BadRequestException('Company feature is not enabled');
756
680
  }
757
681
  }
@@ -762,11 +686,7 @@ let AuthenticationService = class AuthenticationService {
762
686
  email
763
687
  }
764
688
  });
765
- if (!user) {
766
- throw new _common.UnauthorizedException('Invalid credentials');
767
- }
768
- const isPasswordValid = await _bcrypt.compare(password, user.password || '');
769
- if (!isPasswordValid) {
689
+ if (!user || !await _bcrypt.compare(password, user.password || '')) {
770
690
  throw new _common.UnauthorizedException('Invalid credentials');
771
691
  }
772
692
  user.lastLoginAt = new Date();
@@ -774,84 +694,77 @@ let AuthenticationService = class AuthenticationService {
774
694
  return user;
775
695
  }
776
696
  // Response Generators
777
- generateLoginResponse(user, companyFeatureEnabled = false) {
778
- const payload = {
697
+ /** Create user info object */ toUserInfo(user) {
698
+ return {
779
699
  id: user.id,
780
700
  email: user.email,
781
- type: 'access'
701
+ name: user.name ?? undefined,
702
+ phone: user.phone ?? null,
703
+ profilePictureId: user.profilePictureId ?? null
782
704
  };
783
- const accessToken = this.jwtService.sign(payload, {
705
+ }
706
+ /** Create company info object */ toCompanyInfo(company) {
707
+ return {
708
+ id: company.id,
709
+ name: company.name,
710
+ slug: company.slug,
711
+ logoId: company.logoId ?? null
712
+ };
713
+ }
714
+ /** Create branch info object */ toBranchInfo(branch) {
715
+ return {
716
+ id: branch.id,
717
+ name: branch.name,
718
+ slug: branch.slug,
719
+ logoId: branch.logoId ?? null
720
+ };
721
+ }
722
+ /** Generate JWT tokens */ generateTokens(payload) {
723
+ const accessToken = this.jwtService.sign({
724
+ ...payload,
725
+ type: 'access'
726
+ }, {
784
727
  secret: this.authConfig.getJwtSecret(),
785
728
  expiresIn: this.authConfig.getJwtExpiration()
786
729
  });
787
- const refreshPayload = {
788
- id: user.id,
789
- email: user.email,
730
+ const refreshToken = this.jwtService.sign({
731
+ ...payload,
790
732
  type: 'refresh'
791
- };
792
- const refreshToken = this.jwtService.sign(refreshPayload, {
733
+ }, {
793
734
  secret: this.authConfig.getRefreshTokenSecret(),
794
735
  expiresIn: this.authConfig.getRefreshTokenExpiration()
795
736
  });
796
737
  return {
797
738
  accessToken,
798
- refreshToken,
799
- user: {
800
- id: user.id,
801
- email: user.email,
802
- name: user.name ?? undefined,
803
- phone: user.phone ?? null,
804
- profilePictureId: user.profilePictureId ?? null
805
- },
806
- companyFeatureEnabled
739
+ refreshToken
807
740
  };
808
741
  }
809
- generateLoginResponseWithCompany(user, company, branch) {
810
- const payload = {
742
+ generateLoginResponse(user) {
743
+ const { accessToken, refreshToken } = this.generateTokens({
811
744
  id: user.id,
812
745
  email: user.email,
813
- type: 'access',
814
- companyId: company.id,
815
- branchId: branch?.id
816
- };
817
- const accessToken = this.jwtService.sign(payload, {
818
- secret: this.authConfig.getJwtSecret(),
819
- expiresIn: this.authConfig.getJwtExpiration()
746
+ type: 'access'
820
747
  });
821
- const refreshPayload = {
748
+ return {
749
+ accessToken,
750
+ refreshToken,
751
+ user: this.toUserInfo(user)
752
+ };
753
+ }
754
+ generateLoginResponseWithCompany(user, company, branch) {
755
+ const { accessToken, refreshToken } = this.generateTokens({
822
756
  id: user.id,
823
757
  email: user.email,
824
- type: 'refresh',
758
+ type: 'access',
825
759
  companyId: company.id,
826
760
  branchId: branch?.id
827
- };
828
- const refreshToken = this.jwtService.sign(refreshPayload, {
829
- secret: this.authConfig.getRefreshTokenSecret(),
830
- expiresIn: this.authConfig.getRefreshTokenExpiration()
831
761
  });
832
762
  return {
833
763
  accessToken,
834
764
  refreshToken,
835
- user: {
836
- id: user.id,
837
- email: user.email,
838
- name: user.name ?? undefined,
839
- phone: user.phone ?? null,
840
- profilePictureId: user.profilePictureId ?? null
841
- },
842
- company: {
843
- id: company.id,
844
- name: company.name,
845
- slug: company.slug,
846
- logoId: company.logoId ?? null
847
- },
848
- branch: branch ? {
849
- id: branch.id,
850
- name: branch.name,
851
- slug: branch.slug,
852
- logoId: branch.logoId ?? null
853
- } : undefined,
854
- companyFeatureEnabled: true
765
+ user: this.toUserInfo(user),
766
+ company: this.toCompanyInfo(company),
767
+ branch: branch ? this.toBranchInfo(branch) : undefined
855
768
  };
856
769
  }
857
770
  async generateCompanySelectionResponse(user, companies, branches) {
@@ -859,88 +772,31 @@ let AuthenticationService = class AuthenticationService {
859
772
  throw new _common.BadRequestException('Session service not available');
860
773
  }
861
774
  const sessionId = await this.sessionService.createSession(user.id);
862
- // Build companies with their branches
863
775
  const companiesWithBranches = companies.map((company)=>({
864
- id: company.id,
865
- name: company.name,
866
- slug: company.slug,
776
+ ...this.toCompanyInfo(company),
867
777
  isActive: company.isActive,
868
- logoId: company.logoId ?? null,
869
778
  branches: branches.filter((b)=>b.companyId === company.id).map((b)=>({
870
- id: b.id,
871
- name: b.name,
872
- slug: b.slug,
873
- isActive: b.isActive,
874
- logoId: b.logoId ?? null
779
+ ...this.toBranchInfo(b),
780
+ isActive: b.isActive
875
781
  }))
876
782
  }));
877
783
  return {
878
784
  requiresSelection: true,
879
785
  sessionId,
880
786
  expiresAt: new Date(Date.now() + 5 * 60 * 1000),
881
- user: {
882
- id: user.id,
883
- email: user.email,
884
- name: user.name ?? undefined,
885
- phone: user.phone ?? null,
886
- profilePictureId: user.profilePictureId ?? null
887
- },
787
+ user: this.toUserInfo(user),
888
788
  companies: companiesWithBranches
889
789
  };
890
790
  }
891
- /** Generate registration response - tokens included only if email verification not required */ generateRegistrationResponse(user, companyFeatureEnabled, requiresEmailVerification) {
892
- const userInfo = {
893
- id: user.id,
894
- email: user.email,
895
- name: user.name ?? undefined,
896
- phone: user.phone ?? null,
897
- profilePictureId: user.profilePictureId ?? null
898
- };
899
- if (requiresEmailVerification) {
900
- return {
901
- user: userInfo,
902
- companyFeatureEnabled,
903
- requiresEmailVerification: true
904
- };
905
- }
906
- const loginResponse = this.generateLoginResponse(user, companyFeatureEnabled);
791
+ generateRegistrationResponse(user) {
907
792
  return {
908
- ...loginResponse,
793
+ ...this.generateLoginResponse(user),
909
794
  requiresEmailVerification: false
910
795
  };
911
796
  }
912
- /** Generate registration response with company - tokens included only if email verification not required */ generateRegistrationResponseWithCompany(user, company, branch, requiresEmailVerification = false) {
913
- const userInfo = {
914
- id: user.id,
915
- email: user.email,
916
- name: user.name ?? undefined,
917
- phone: user.phone ?? null,
918
- profilePictureId: user.profilePictureId ?? null
919
- };
920
- const companyInfo = {
921
- id: company.id,
922
- name: company.name,
923
- slug: company.slug,
924
- logoId: company.logoId ?? null
925
- };
926
- const branchInfo = branch ? {
927
- id: branch.id,
928
- name: branch.name,
929
- slug: branch.slug,
930
- logoId: branch.logoId ?? null
931
- } : undefined;
932
- if (requiresEmailVerification) {
933
- return {
934
- user: userInfo,
935
- company: companyInfo,
936
- branch: branchInfo,
937
- companyFeatureEnabled: true,
938
- requiresEmailVerification: true
939
- };
940
- }
941
- const loginResponse = this.generateLoginResponseWithCompany(user, company, branch);
797
+ generateRegistrationResponseWithCompany(user, company, branch) {
942
798
  return {
943
- ...loginResponse,
799
+ ...this.generateLoginResponseWithCompany(user, company, branch),
944
800
  requiresEmailVerification: false
945
801
  };
946
802
  }