@flusys/nestjs-auth 0.1.0-beta.3 → 1.0.0-rc

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 (102) hide show
  1. package/README.md +274 -301
  2. package/cjs/config/auth.constants.js +22 -4
  3. package/cjs/controllers/authentication.controller.js +222 -38
  4. package/cjs/controllers/branch.controller.js +46 -4
  5. package/cjs/controllers/company-selection.controller.js +27 -20
  6. package/cjs/controllers/company.controller.js +46 -4
  7. package/cjs/controllers/user-permission.controller.js +40 -37
  8. package/cjs/controllers/user.controller.js +222 -23
  9. package/cjs/dtos/authentication.dto.js +119 -13
  10. package/cjs/dtos/user-permission.dto.js +28 -0
  11. package/cjs/dtos/user.dto.js +19 -4
  12. package/cjs/entities/index.js +5 -1
  13. package/cjs/entities/pending-registration.entity.js +125 -0
  14. package/cjs/entities/user-company-permission.entity.js +0 -6
  15. package/cjs/entities/user-token.entity.js +129 -0
  16. package/cjs/guards/auth-throttler.guard.js +53 -0
  17. package/cjs/interceptors/clear-token.interceptor.js +3 -6
  18. package/cjs/interceptors/set-token.interceptor.js +38 -31
  19. package/cjs/interfaces/auth-email-provider.interface.js +45 -0
  20. package/cjs/interfaces/authentication.interface.js +0 -3
  21. package/cjs/interfaces/index.js +2 -0
  22. package/cjs/interfaces/user-enricher.interface.js +4 -0
  23. package/cjs/modules/auth.module.js +8 -20
  24. package/cjs/services/auth-config.service.js +42 -47
  25. package/cjs/services/auth-datasource.provider.js +18 -34
  26. package/cjs/services/auth-email.service.js +471 -0
  27. package/cjs/services/authentication.service.js +240 -153
  28. package/cjs/services/branch.service.js +15 -48
  29. package/cjs/services/company-selection-session.service.js +1 -1
  30. package/cjs/services/company.service.js +7 -36
  31. package/cjs/services/index.js +1 -0
  32. package/cjs/services/user-permission.service.js +9 -29
  33. package/cjs/services/user.service.js +228 -107
  34. package/cjs/strategies/jwt.strategy.js +14 -1
  35. package/cjs/validators/password.validator.js +45 -0
  36. package/config/auth.constants.d.ts +5 -0
  37. package/controllers/authentication.controller.d.ts +13 -9
  38. package/controllers/company-selection.controller.d.ts +6 -2
  39. package/controllers/user.controller.d.ts +33 -0
  40. package/dtos/authentication.dto.d.ts +21 -3
  41. package/dtos/company-branch.dto.d.ts +0 -1
  42. package/dtos/company.dto.d.ts +0 -1
  43. package/dtos/user-permission.dto.d.ts +4 -0
  44. package/dtos/user.dto.d.ts +1 -2
  45. package/entities/index.d.ts +7 -3
  46. package/entities/pending-registration.entity.d.ts +17 -0
  47. package/entities/user-company-permission.entity.d.ts +0 -2
  48. package/entities/user-token.entity.d.ts +15 -0
  49. package/fesm/config/auth.constants.js +11 -8
  50. package/fesm/controllers/authentication.controller.js +224 -40
  51. package/fesm/controllers/branch.controller.js +49 -7
  52. package/fesm/controllers/company-selection.controller.js +28 -21
  53. package/fesm/controllers/company.controller.js +49 -7
  54. package/fesm/controllers/user-permission.controller.js +41 -38
  55. package/fesm/controllers/user.controller.js +225 -26
  56. package/fesm/dtos/authentication.dto.js +123 -111
  57. package/fesm/dtos/user-permission.dto.js +28 -0
  58. package/fesm/dtos/user.dto.js +20 -5
  59. package/fesm/entities/index.js +7 -1
  60. package/fesm/entities/pending-registration.entity.js +115 -0
  61. package/fesm/entities/user-company-permission.entity.js +0 -6
  62. package/fesm/entities/user-token.entity.js +113 -0
  63. package/fesm/guards/auth-throttler.guard.js +43 -0
  64. package/fesm/interceptors/clear-token.interceptor.js +2 -5
  65. package/fesm/interceptors/set-token.interceptor.js +37 -30
  66. package/fesm/interfaces/auth-email-provider.interface.js +38 -0
  67. package/fesm/interfaces/authentication.interface.js +1 -7
  68. package/fesm/interfaces/index.js +2 -0
  69. package/fesm/interfaces/user-enricher.interface.js +26 -0
  70. package/fesm/modules/auth.module.js +10 -22
  71. package/fesm/services/auth-config.service.js +42 -47
  72. package/fesm/services/auth-datasource.provider.js +19 -35
  73. package/fesm/services/auth-email.service.js +420 -0
  74. package/fesm/services/authentication.service.js +238 -151
  75. package/fesm/services/branch.service.js +16 -49
  76. package/fesm/services/company-selection-session.service.js +1 -1
  77. package/fesm/services/company.service.js +8 -37
  78. package/fesm/services/index.js +1 -0
  79. package/fesm/services/user-permission.service.js +10 -30
  80. package/fesm/services/user.service.js +229 -108
  81. package/fesm/strategies/jwt.strategy.js +14 -1
  82. package/fesm/validators/password.validator.js +48 -0
  83. package/guards/auth-throttler.guard.d.ts +7 -0
  84. package/interceptors/set-token.interceptor.d.ts +1 -0
  85. package/interfaces/auth-email-provider.interface.d.ts +6 -0
  86. package/interfaces/auth-module-options.interface.d.ts +4 -1
  87. package/interfaces/authentication.interface.d.ts +14 -0
  88. package/interfaces/index.d.ts +2 -0
  89. package/interfaces/user-enricher.interface.d.ts +64 -0
  90. package/interfaces/user.interface.d.ts +1 -0
  91. package/modules/auth.module.d.ts +0 -1
  92. package/package.json +4 -4
  93. package/services/auth-config.service.d.ts +5 -1
  94. package/services/auth-email.service.d.ts +51 -0
  95. package/services/authentication.service.d.ts +21 -12
  96. package/services/branch.service.d.ts +2 -5
  97. package/services/company-selection-session.service.d.ts +1 -1
  98. package/services/company.service.d.ts +1 -4
  99. package/services/index.d.ts +1 -0
  100. package/services/user-permission.service.d.ts +0 -1
  101. package/services/user.service.d.ts +20 -6
  102. package/validators/password.validator.d.ts +10 -0
@@ -27,46 +27,21 @@ function _ts_param(paramIndex, decorator) {
27
27
  }
28
28
  import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
29
29
  import { UtilsService } from '@flusys/nestjs-shared/modules';
30
- import { Inject, Injectable, Logger, NotFoundException, Scope } from '@nestjs/common';
30
+ import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
31
+ import { Inject, Injectable, Scope } from '@nestjs/common';
31
32
  import { CompanyBranch } from '../entities/company-branch.entity';
32
33
  import { AuthConfigService } from './auth-config.service';
33
34
  import { AuthDataSourceProvider } from './auth-datasource.provider';
34
35
  export class BranchService extends RequestScopedApiService {
35
- /**
36
- * Resolve entity class for this service
37
- * @returns CompanyBranch entity class
38
- */ resolveEntity() {
36
+ resolveEntity() {
39
37
  return CompanyBranch;
40
38
  }
41
- /**
42
- * Get DataSource provider for this service
43
- * @returns AuthDataSourceProvider instance
44
- */ getDataSourceProvider() {
39
+ getDataSourceProvider() {
45
40
  return this.dataSourceProvider;
46
41
  }
47
- // ==================== Entity Conversion ====================
48
- async convertSingleDtoToEntity(dto, _user) {
49
- // Repository initialization is handled automatically by RequestScopedApiService
50
- let branch = new CompanyBranch();
51
- if ('id' in dto && dto.id && typeof dto.id === 'string') {
52
- const dbData = await this.repository.findOne({
53
- where: {
54
- id: dto.id
55
- }
56
- });
57
- if (!dbData) {
58
- throw new NotFoundException('Branch not found');
59
- }
60
- branch = dbData;
61
- }
62
- return {
63
- ...branch,
64
- ...dto
65
- };
66
- }
42
+ // Query Customization
67
43
  async getSelectQuery(query, _user, select) {
68
- // Repository initialization is handled automatically by RequestScopedApiService
69
- if (!select || !select.length) {
44
+ if (!select?.length) {
70
45
  select = [
71
46
  'id',
72
47
  'name',
@@ -75,14 +50,13 @@ export class BranchService extends RequestScopedApiService {
75
50
  'createdAt'
76
51
  ];
77
52
  }
78
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
79
- query.select(selectFields);
53
+ query.select(select.map((f)=>`${this.entityName}.${f}`));
80
54
  return {
81
55
  query,
82
56
  isRaw: false
83
57
  };
84
58
  }
85
- async getGlobalSearchQuery(query, search, _user) {
59
+ async getGlobalSearchQuery(query, search) {
86
60
  query.andWhere('(company_branch.name LIKE :search OR company_branch.slug LIKE :search OR company_branch.email LIKE :search)', {
87
61
  search: `%${search}%`
88
62
  });
@@ -91,24 +65,17 @@ export class BranchService extends RequestScopedApiService {
91
65
  isRaw: false
92
66
  };
93
67
  }
94
- /**
95
- * Override: Filter branches by user's company
96
- * Ensures users can only see branches belonging to their company
97
- */ async getExtraManipulateQuery(query, _filterAndPaginationDto, user) {
98
- const result = await super.getExtraManipulateQuery(query, _filterAndPaginationDto, user);
99
- // Only apply company filtering if company feature is enabled and user has company context
100
- const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
101
- if (isCompanyFeatureEnabled && user?.companyId) {
102
- query.andWhere('company_branch.company_id = :companyId', {
103
- companyId: user.companyId
104
- });
105
- }
68
+ async getExtraManipulateQuery(query, filterDto, user) {
69
+ const result = await super.getExtraManipulateQuery(query, filterDto, user);
70
+ applyCompanyFilter(query, {
71
+ isCompanyFeatureEnabled: this.authConfig.isCompanyFeatureEnabled(),
72
+ entityAlias: 'company_branch',
73
+ columnName: 'company_id'
74
+ }, user);
106
75
  return result;
107
76
  }
108
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
109
77
  constructor(cacheManager, utilsService, dataSourceProvider, authConfig){
110
- // Repository will be set asynchronously by RequestScopedApiService
111
- super('company_branch', null, cacheManager, utilsService, BranchService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "authConfig", void 0), _define_property(this, "logger", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.logger = new Logger(BranchService.name);
78
+ super('company_branch', null, cacheManager, utilsService, BranchService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "authConfig", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig;
112
79
  }
113
80
  }
114
81
  BranchService = _ts_decorate([
@@ -33,7 +33,7 @@ const SESSION_TTL_MS = 5 * 60 * 1000; // 5 minutes
33
33
  export class CompanySelectionSessionService {
34
34
  /**
35
35
  * Create a new session for company/branch selection
36
- */ async createSession(userId, companyId) {
36
+ */ async createSession(userId) {
37
37
  const sessionId = randomUUID();
38
38
  const now = new Date();
39
39
  const expiresAt = new Date(now.getTime() + SESSION_TTL_MS);
@@ -27,45 +27,19 @@ function _ts_param(paramIndex, decorator) {
27
27
  }
28
28
  import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
29
29
  import { UtilsService } from '@flusys/nestjs-shared/modules';
30
- import { Inject, Injectable, Logger, NotFoundException, Scope } from '@nestjs/common';
30
+ import { Inject, Injectable, Scope } from '@nestjs/common';
31
31
  import { Company } from '../entities/company.entity';
32
32
  import { AuthDataSourceProvider } from './auth-datasource.provider';
33
33
  export class CompanyService extends RequestScopedApiService {
34
- /**
35
- * Resolve entity class for this service
36
- * @returns Company entity class
37
- */ resolveEntity() {
34
+ resolveEntity() {
38
35
  return Company;
39
36
  }
40
- /**
41
- * Get DataSource provider for this service
42
- * @returns AuthDataSourceProvider instance
43
- */ getDataSourceProvider() {
37
+ getDataSourceProvider() {
44
38
  return this.dataSourceProvider;
45
39
  }
46
- // ==================== Entity Conversion ====================
47
- async convertSingleDtoToEntity(dto, _user) {
48
- // Repository initialization is handled automatically by RequestScopedApiService
49
- let company = new Company();
50
- if ('id' in dto && dto.id && typeof dto.id === 'string') {
51
- const dbData = await this.repository.findOne({
52
- where: {
53
- id: dto.id
54
- }
55
- });
56
- if (!dbData) {
57
- throw new NotFoundException('Company not found');
58
- }
59
- company = dbData;
60
- }
61
- return {
62
- ...company,
63
- ...dto
64
- };
65
- }
40
+ // Query Customization
66
41
  async getSelectQuery(query, _user, select) {
67
- // Repository initialization is handled automatically by RequestScopedApiService
68
- if (!select || !select.length) {
42
+ if (!select?.length) {
69
43
  select = [
70
44
  'id',
71
45
  'name',
@@ -74,14 +48,13 @@ export class CompanyService extends RequestScopedApiService {
74
48
  'createdAt'
75
49
  ];
76
50
  }
77
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
78
- query.select(selectFields);
51
+ query.select(select.map((f)=>`${this.entityName}.${f}`));
79
52
  return {
80
53
  query,
81
54
  isRaw: false
82
55
  };
83
56
  }
84
- async getGlobalSearchQuery(query, search, _user) {
57
+ async getGlobalSearchQuery(query, search) {
85
58
  query.andWhere('(company.name LIKE :search OR company.slug LIKE :search OR company.email LIKE :search)', {
86
59
  search: `%${search}%`
87
60
  });
@@ -90,10 +63,8 @@ export class CompanyService extends RequestScopedApiService {
90
63
  isRaw: false
91
64
  };
92
65
  }
93
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
94
66
  constructor(cacheManager, utilsService, dataSourceProvider){
95
- // Repository will be set asynchronously by RequestScopedApiService
96
- super('company', null, cacheManager, utilsService, CompanyService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "logger", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.logger = new Logger(CompanyService.name);
67
+ super('company', null, cacheManager, utilsService, CompanyService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
97
68
  }
98
69
  }
99
70
  CompanyService = _ts_decorate([
@@ -1,4 +1,5 @@
1
1
  export * from './authentication.service';
2
+ export * from './auth-email.service';
2
3
  export * from './company-selection-session.service';
3
4
  export * from './user.service';
4
5
  export * from './company.service';
@@ -25,24 +25,18 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { BadRequestException, Inject, Injectable, Logger, NotFoundException, Scope } from '@nestjs/common';
28
+ import { BadRequestException, Inject, Injectable, NotFoundException, Scope } from '@nestjs/common';
29
29
  import { PermissionType, UserCompanyPermission } from '../entities/user-company-permission.entity';
30
30
  import { AuthDataSourceProvider } from './auth-datasource.provider';
31
31
  export class UserPermissionService {
32
- /**
33
- * Get repository lazily - initialized on first access
34
- */ async getRepository() {
32
+ /** Get repository lazily - initialized on first access */ async getRepository() {
35
33
  if (!this._repository) {
36
34
  this._repository = await this.dataSourceProvider.getRepository(UserCompanyPermission);
37
35
  }
38
36
  return this._repository;
39
37
  }
40
- // ==========================================================================
41
- // COMPANY PERMISSIONS
42
- // ==========================================================================
43
- /**
44
- * Find all companies a user has access to
45
- */ async findUserCompanies(userId) {
38
+ // Company Permissions
39
+ /** Find all companies a user has access to */ async findUserCompanies(userId) {
46
40
  const repository = await this.getRepository();
47
41
  return repository.find({
48
42
  where: {
@@ -57,9 +51,7 @@ export class UserPermissionService {
57
51
  }
58
52
  });
59
53
  }
60
- /**
61
- * Assign user to a company
62
- */ async assignUserToCompany(dto) {
54
+ /** Assign user to a company */ async assignUserToCompany(dto) {
63
55
  const repository = await this.getRepository();
64
56
  // Check if already exists
65
57
  const existing = await repository.findOne({
@@ -80,9 +72,7 @@ export class UserPermissionService {
80
72
  });
81
73
  await repository.save(permission);
82
74
  }
83
- /**
84
- * Revoke user access to a company
85
- */ async revokeCompanyAccess(userId, companyId) {
75
+ /** Revoke user access to a company */ async revokeCompanyAccess(userId, companyId) {
86
76
  const repository = await this.getRepository();
87
77
  const permission = await repository.findOne({
88
78
  where: {
@@ -96,12 +86,8 @@ export class UserPermissionService {
96
86
  }
97
87
  await repository.remove(permission);
98
88
  }
99
- // ==========================================================================
100
- // BRANCH PERMISSIONS
101
- // ==========================================================================
102
- /**
103
- * Find all branches a user has access to
104
- */ async findUserBranches(userId) {
89
+ // Branch Permissions
90
+ /** Find all branches a user has access to */ async findUserBranches(userId) {
105
91
  const repository = await this.getRepository();
106
92
  return repository.find({
107
93
  where: {
@@ -116,9 +102,7 @@ export class UserPermissionService {
116
102
  }
117
103
  });
118
104
  }
119
- /**
120
- * Assign user to a branch
121
- */ async assignUserToBranch(dto) {
105
+ /** Assign user to a branch */ async assignUserToBranch(dto) {
122
106
  const repository = await this.getRepository();
123
107
  // Check if already exists
124
108
  const existing = await repository.findOne({
@@ -139,9 +123,7 @@ export class UserPermissionService {
139
123
  });
140
124
  await repository.save(permission);
141
125
  }
142
- /**
143
- * Revoke user access to a branch
144
- */ async revokeBranchAccess(userId, branchId) {
126
+ /** Revoke user access to a branch */ async revokeBranchAccess(userId, branchId) {
145
127
  const repository = await this.getRepository();
146
128
  const permission = await repository.findOne({
147
129
  where: {
@@ -158,10 +140,8 @@ export class UserPermissionService {
158
140
  // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
159
141
  constructor(dataSourceProvider){
160
142
  _define_property(this, "dataSourceProvider", void 0);
161
- _define_property(this, "logger", void 0);
162
143
  _define_property(this, "_repository", void 0);
163
144
  this.dataSourceProvider = dataSourceProvider;
164
- this.logger = new Logger(UserPermissionService.name);
165
145
  this._repository = null;
166
146
  }
167
147
  }