@flusys/nestjs-auth 6.0.1 → 6.1.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 (83) hide show
  1. package/cjs/controllers/authentication.controller.spec.js +219 -0
  2. package/cjs/controllers/branch.controller.spec.js +134 -0
  3. package/cjs/controllers/company-selection.controller.spec.js +84 -0
  4. package/cjs/controllers/company.controller.spec.js +131 -0
  5. package/cjs/controllers/email-verification.controller.spec.js +142 -0
  6. package/cjs/controllers/user-permission.controller.spec.js +138 -0
  7. package/cjs/controllers/user.controller.spec.js +204 -0
  8. package/cjs/docs/auth-swagger.config.spec.js +130 -0
  9. package/cjs/entities/company-branch.entity.js +2 -3
  10. package/cjs/entities/index.spec.js +73 -0
  11. package/cjs/helpers/auth-response.mapper.js +46 -0
  12. package/cjs/helpers/auth-response.mapper.spec.js +139 -0
  13. package/cjs/helpers/index.js +19 -0
  14. package/cjs/helpers/token.helper.js +58 -0
  15. package/cjs/helpers/token.helper.spec.js +86 -0
  16. package/cjs/index.js +1 -0
  17. package/cjs/interceptors/clear-token.interceptor.spec.js +107 -0
  18. package/cjs/interceptors/set-token.interceptor.spec.js +165 -0
  19. package/cjs/modules/auth.module.spec.js +243 -0
  20. package/cjs/services/auth-config.service.spec.js +97 -0
  21. package/cjs/services/auth-datasource.provider.spec.js +161 -0
  22. package/cjs/services/auth-email.service.spec.js +404 -0
  23. package/cjs/services/authentication.service.js +37 -109
  24. package/cjs/services/authentication.service.spec.js +904 -0
  25. package/cjs/services/branch.service.js +30 -3
  26. package/cjs/services/branch.service.spec.js +154 -0
  27. package/cjs/services/company-selection-session.service.spec.js +90 -0
  28. package/cjs/services/company.service.js +34 -3
  29. package/cjs/services/company.service.spec.js +113 -0
  30. package/cjs/services/user-permission.service.js +35 -4
  31. package/cjs/services/user-permission.service.spec.js +202 -0
  32. package/cjs/services/user.service.spec.js +704 -0
  33. package/cjs/strategies/jwt.strategy.js +1 -1
  34. package/cjs/strategies/jwt.strategy.spec.js +166 -0
  35. package/cjs/validators/password.validator.spec.js +161 -0
  36. package/entities/company-branch.entity.d.ts +1 -1
  37. package/entities/pending-registration.entity.d.ts +2 -7
  38. package/fesm/controllers/authentication.controller.spec.js +215 -0
  39. package/fesm/controllers/branch.controller.spec.js +130 -0
  40. package/fesm/controllers/company-selection.controller.spec.js +80 -0
  41. package/fesm/controllers/company.controller.spec.js +127 -0
  42. package/fesm/controllers/email-verification.controller.spec.js +138 -0
  43. package/fesm/controllers/user-permission.controller.spec.js +134 -0
  44. package/fesm/controllers/user.controller.spec.js +200 -0
  45. package/fesm/docs/auth-swagger.config.spec.js +126 -0
  46. package/fesm/entities/company-branch.entity.js +2 -3
  47. package/fesm/entities/index.spec.js +69 -0
  48. package/fesm/helpers/auth-response.mapper.js +25 -0
  49. package/fesm/helpers/auth-response.mapper.spec.js +135 -0
  50. package/fesm/helpers/index.js +2 -0
  51. package/fesm/helpers/token.helper.js +40 -0
  52. package/fesm/helpers/token.helper.spec.js +82 -0
  53. package/fesm/index.js +1 -0
  54. package/fesm/interceptors/clear-token.interceptor.spec.js +103 -0
  55. package/fesm/interceptors/set-token.interceptor.spec.js +161 -0
  56. package/fesm/interfaces/authentication.interface.js +1 -1
  57. package/fesm/modules/auth.module.spec.js +239 -0
  58. package/fesm/services/auth-config.service.spec.js +93 -0
  59. package/fesm/services/auth-datasource.provider.spec.js +157 -0
  60. package/fesm/services/auth-email.service.spec.js +359 -0
  61. package/fesm/services/authentication.service.js +37 -109
  62. package/fesm/services/authentication.service.spec.js +859 -0
  63. package/fesm/services/branch.service.js +32 -5
  64. package/fesm/services/branch.service.spec.js +150 -0
  65. package/fesm/services/company-selection-session.service.spec.js +86 -0
  66. package/fesm/services/company.service.js +36 -5
  67. package/fesm/services/company.service.spec.js +109 -0
  68. package/fesm/services/user-permission.service.js +36 -5
  69. package/fesm/services/user-permission.service.spec.js +198 -0
  70. package/fesm/services/user.service.spec.js +659 -0
  71. package/fesm/strategies/jwt.strategy.js +1 -1
  72. package/fesm/strategies/jwt.strategy.spec.js +162 -0
  73. package/fesm/validators/password.validator.spec.js +157 -0
  74. package/helpers/auth-response.mapper.d.ts +22 -0
  75. package/helpers/index.d.ts +2 -0
  76. package/helpers/token.helper.d.ts +8 -0
  77. package/index.d.ts +1 -0
  78. package/interfaces/authentication.interface.d.ts +7 -0
  79. package/package.json +8 -3
  80. package/services/authentication.service.d.ts +1 -8
  81. package/services/branch.service.d.ts +7 -5
  82. package/services/company.service.d.ts +7 -4
  83. package/services/user-permission.service.d.ts +4 -1
@@ -42,6 +42,7 @@ import { Company } from '../entities/company.entity';
42
42
  import { PendingRegistration } from '../entities/pending-registration.entity';
43
43
  import { UserCompanyPermission } from '../entities/user-company-permission.entity';
44
44
  import { PermissionType } from '../enums';
45
+ import { generateTokens, parseExpiryToMs, toBranchInfo, toCompanyInfo, toUserInfo } from '../helpers';
45
46
  import { IUserEnricher } from '../interfaces/user-enricher.interface';
46
47
  import { AuthConfigService } from './auth-config.service';
47
48
  import { AuthDataSourceProvider } from './auth-datasource.provider';
@@ -68,17 +69,9 @@ export class AuthenticationService {
68
69
  }
69
70
  return this.dataSourceProvider.getRepository(AppUser);
70
71
  }
71
- async getPermissionRepository() {
72
+ async getCompanyScopedRepository(entity) {
72
73
  if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
73
- return this.dataSourceProvider.getRepository(UserCompanyPermission);
74
- }
75
- async getCompanyRepository() {
76
- if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
77
- return this.dataSourceProvider.getRepository(Company);
78
- }
79
- async getBranchRepository() {
80
- if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
81
- return this.dataSourceProvider.getRepository(CompanyBranch);
74
+ return this.dataSourceProvider.getRepository(entity);
82
75
  }
83
76
  async login(email, password) {
84
77
  if (this.isEmailVerificationEnabled() && await this.authEmailService.hasPendingRegistration(email)) {
@@ -110,8 +103,8 @@ export class AuthenticationService {
110
103
  return await this.handleCompanyBasedLogin(user);
111
104
  }
112
105
  async handleCompanyBasedLogin(user) {
113
- const permissionRepository = await this.getPermissionRepository();
114
- const branchRepository = await this.getBranchRepository();
106
+ const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
107
+ const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
115
108
  if (!permissionRepository || !branchRepository) {
116
109
  return this.generateLoginResponse(user);
117
110
  }
@@ -121,7 +114,7 @@ export class AuthenticationService {
121
114
  permissionType: PermissionType.COMPANY
122
115
  }
123
116
  });
124
- const companyRepository = await this.getCompanyRepository();
117
+ const companyRepository = await this.getCompanyScopedRepository(Company);
125
118
  if (!companyRepository) {
126
119
  return this.generateLoginResponse(user);
127
120
  }
@@ -199,8 +192,8 @@ export class AuthenticationService {
199
192
  messageKey: AUTH_MESSAGES.REGISTER_EMAIL_EXISTS
200
193
  });
201
194
  }
202
- const permissionRepository = await this.getPermissionRepository();
203
- const companyRepository = await this.getCompanyRepository();
195
+ const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
196
+ const companyRepository = await this.getCompanyScopedRepository(Company);
204
197
  if (!permissionRepository || !companyRepository) {
205
198
  throw new BadRequestException({
206
199
  message: 'Company feature repositories not available',
@@ -334,9 +327,9 @@ export class AuthenticationService {
334
327
  }
335
328
  }
336
329
  async processCompanyRegistration(user, data, throwOnUnavailable = false) {
337
- const companyRepository = await this.getCompanyRepository();
338
- const branchRepository = await this.getBranchRepository();
339
- const permissionRepository = await this.getPermissionRepository();
330
+ const companyRepository = await this.getCompanyScopedRepository(Company);
331
+ const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
332
+ const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
340
333
  if (!companyRepository || !branchRepository || !permissionRepository) {
341
334
  if (throwOnUnavailable) throw new BadRequestException({
342
335
  message: 'Company feature repositories not available',
@@ -472,9 +465,9 @@ export class AuthenticationService {
472
465
  messageKey: USER_MESSAGES.NOT_FOUND
473
466
  });
474
467
  }
475
- const permissionRepository = await this.getPermissionRepository();
476
- const companyRepository = await this.getCompanyRepository();
477
- const branchRepository = await this.getBranchRepository();
468
+ const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
469
+ const companyRepository = await this.getCompanyScopedRepository(Company);
470
+ const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
478
471
  if (!permissionRepository || !companyRepository || !branchRepository) {
479
472
  throw new BadRequestException({
480
473
  message: 'Repositories not available',
@@ -564,8 +557,8 @@ export class AuthenticationService {
564
557
  });
565
558
  }
566
559
  if (payload.companyId) {
567
- const companyRepository = await this.getCompanyRepository();
568
- const permissionRepository = await this.getPermissionRepository();
560
+ const companyRepository = await this.getCompanyScopedRepository(Company);
561
+ const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
569
562
  if (companyRepository) {
570
563
  const company = await companyRepository.findOne({
571
564
  where: {
@@ -596,7 +589,7 @@ export class AuthenticationService {
596
589
  }
597
590
  }
598
591
  if (payload.branchId) {
599
- const branchRepository = await this.getBranchRepository();
592
+ const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
600
593
  if (branchRepository) {
601
594
  const branch = await branchRepository.findOne({
602
595
  where: {
@@ -629,7 +622,7 @@ export class AuthenticationService {
629
622
  }
630
623
  }
631
624
  }
632
- return this.generateTokens({
625
+ return generateTokens(this.jwtService, this.authConfig, {
633
626
  id: user.id,
634
627
  email: user.email,
635
628
  type: 'access',
@@ -678,7 +671,7 @@ export class AuthenticationService {
678
671
  const revokedAt = Math.floor(Date.now() / 1000);
679
672
  const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
680
673
  const refreshTokenExpiry = this.authConfig.getRefreshTokenExpiration();
681
- const ttlMs = this.parseExpiryToMs(refreshTokenExpiry);
674
+ const ttlMs = parseExpiryToMs(refreshTokenExpiry);
682
675
  await this.cacheManager.set(cacheKey, revokedAt, ttlMs);
683
676
  }
684
677
  async isTokenRevoked(userId, tokenIssuedAt) {
@@ -692,28 +685,10 @@ export class AuthenticationService {
692
685
  }
693
686
  return tokenIssuedAt < revokedAt;
694
687
  }
695
- parseExpiryToMs(expiry) {
696
- const match = expiry.match(/^(\d+)([smhd])$/);
697
- if (!match) return 7 * 24 * 60 * 60 * 1000;
698
- const value = parseInt(match[1], 10);
699
- const unit = match[2];
700
- switch(unit){
701
- case 's':
702
- return value * 1000;
703
- case 'm':
704
- return value * 60 * 1000;
705
- case 'h':
706
- return value * 60 * 60 * 1000;
707
- case 'd':
708
- return value * 24 * 60 * 60 * 1000;
709
- default:
710
- return 7 * 24 * 60 * 60 * 1000;
711
- }
712
- }
713
688
  async getUserCompanies(userId) {
714
689
  this.ensureCompanyFeatureEnabled();
715
- const permissionRepository = await this.getPermissionRepository();
716
- const companyRepository = await this.getCompanyRepository();
690
+ const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
691
+ const companyRepository = await this.getCompanyScopedRepository(Company);
717
692
  if (!permissionRepository || !companyRepository) {
718
693
  throw new BadRequestException({
719
694
  message: 'Repositories not available',
@@ -739,8 +714,8 @@ export class AuthenticationService {
739
714
  }
740
715
  async getUserBranches(userId, companyId) {
741
716
  this.ensureCompanyFeatureEnabled();
742
- const permissionRepository = await this.getPermissionRepository();
743
- const branchRepository = await this.getBranchRepository();
717
+ const permissionRepository = await this.getCompanyScopedRepository(UserCompanyPermission);
718
+ const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
744
719
  if (!permissionRepository || !branchRepository) {
745
720
  throw new BadRequestException({
746
721
  message: 'Repositories not available',
@@ -780,7 +755,7 @@ export class AuthenticationService {
780
755
  }).orderBy('b.serial', 'ASC').addOrderBy('b.name', 'ASC').getMany();
781
756
  }
782
757
  async getCompanyById(companyId) {
783
- const companyRepository = await this.getCompanyRepository();
758
+ const companyRepository = await this.getCompanyScopedRepository(Company);
784
759
  if (!companyRepository) {
785
760
  return null;
786
761
  }
@@ -791,7 +766,7 @@ export class AuthenticationService {
791
766
  });
792
767
  }
793
768
  async getBranchById(branchId) {
794
- const branchRepository = await this.getBranchRepository();
769
+ const branchRepository = await this.getCompanyScopedRepository(CompanyBranch);
795
770
  if (!branchRepository) {
796
771
  return null;
797
772
  }
@@ -813,7 +788,7 @@ export class AuthenticationService {
813
788
  messageKey: USER_MESSAGES.NOT_FOUND
814
789
  });
815
790
  const response = {
816
- user: this.toUserInfo(user)
791
+ user: toUserInfo(user)
817
792
  };
818
793
  if (this.isCompanyFeatureEnabled()) {
819
794
  if (!companyId) {
@@ -827,14 +802,14 @@ export class AuthenticationService {
827
802
  message: 'Company not found.',
828
803
  messageKey: AUTH_MESSAGES.COMPANY_NOT_FOUND
829
804
  });
830
- response.company = this.toCompanyInfo(company);
805
+ response.company = toCompanyInfo(company);
831
806
  if (branchId) {
832
807
  const branch = await this.getBranchById(branchId);
833
808
  if (!branch) throw new UnauthorizedException({
834
809
  message: 'Branch not found.',
835
810
  messageKey: AUTH_MESSAGES.BRANCH_NOT_FOUND
836
811
  });
837
- response.branch = this.toBranchInfo(branch);
812
+ response.branch = toBranchInfo(branch);
838
813
  }
839
814
  }
840
815
  return response;
@@ -864,55 +839,8 @@ export class AuthenticationService {
864
839
  await userRepository.save(user);
865
840
  return user;
866
841
  }
867
- toUserInfo(user) {
868
- return {
869
- id: user.id,
870
- email: user.email,
871
- name: user.name ?? undefined,
872
- phone: user.phone ?? null,
873
- profilePictureId: user.profilePictureId ?? null
874
- };
875
- }
876
- toCompanyInfo(company) {
877
- return {
878
- id: company.id,
879
- name: company.name,
880
- slug: company.slug,
881
- logoId: company.logoId ?? null
882
- };
883
- }
884
- toBranchInfo(branch) {
885
- return {
886
- id: branch.id,
887
- name: branch.name,
888
- slug: branch.slug,
889
- logoId: branch.logoId ?? null
890
- };
891
- }
892
- generateTokens(payload) {
893
- const accessToken = this.jwtService.sign({
894
- ...payload,
895
- type: 'access'
896
- }, {
897
- secret: this.authConfig.getJwtSecret(),
898
- expiresIn: this.authConfig.getJwtExpiration(),
899
- algorithm: 'HS256'
900
- });
901
- const refreshToken = this.jwtService.sign({
902
- ...payload,
903
- type: 'refresh'
904
- }, {
905
- secret: this.authConfig.getRefreshTokenSecret(),
906
- expiresIn: this.authConfig.getRefreshTokenExpiration(),
907
- algorithm: 'HS256'
908
- });
909
- return {
910
- accessToken,
911
- refreshToken
912
- };
913
- }
914
842
  generateLoginResponse(user) {
915
- const { accessToken, refreshToken } = this.generateTokens({
843
+ const { accessToken, refreshToken } = generateTokens(this.jwtService, this.authConfig, {
916
844
  id: user.id,
917
845
  email: user.email,
918
846
  type: 'access'
@@ -921,11 +849,11 @@ export class AuthenticationService {
921
849
  return {
922
850
  accessToken,
923
851
  refreshToken,
924
- user: this.toUserInfo(user)
852
+ user: toUserInfo(user)
925
853
  };
926
854
  }
927
855
  generateLoginResponseWithCompany(user, company, branch) {
928
- const { accessToken, refreshToken } = this.generateTokens({
856
+ const { accessToken, refreshToken } = generateTokens(this.jwtService, this.authConfig, {
929
857
  id: user.id,
930
858
  email: user.email,
931
859
  type: 'access',
@@ -936,9 +864,9 @@ export class AuthenticationService {
936
864
  return {
937
865
  accessToken,
938
866
  refreshToken,
939
- user: this.toUserInfo(user),
940
- company: this.toCompanyInfo(company),
941
- branch: branch ? this.toBranchInfo(branch) : undefined
867
+ user: toUserInfo(user),
868
+ company: toCompanyInfo(company),
869
+ branch: branch ? toBranchInfo(branch) : undefined
942
870
  };
943
871
  }
944
872
  sendLoginNotification(userId, companyId) {
@@ -963,10 +891,10 @@ export class AuthenticationService {
963
891
  }
964
892
  const sessionId = await this.sessionService.createSession(user.id);
965
893
  const companiesWithBranches = companies.map((company)=>({
966
- ...this.toCompanyInfo(company),
894
+ ...toCompanyInfo(company),
967
895
  isActive: company.isActive,
968
896
  branches: branches.filter((b)=>b.companyId === company.id).map((b)=>({
969
- ...this.toBranchInfo(b),
897
+ ...toBranchInfo(b),
970
898
  isActive: b.isActive
971
899
  }))
972
900
  }));
@@ -974,7 +902,7 @@ export class AuthenticationService {
974
902
  requiresSelection: true,
975
903
  sessionId,
976
904
  expiresAt: new Date(Date.now() + 5 * 60 * 1000),
977
- user: this.toUserInfo(user),
905
+ user: toUserInfo(user),
978
906
  companies: companiesWithBranches
979
907
  };
980
908
  }