@flusys/nestjs-auth 3.0.0-rc → 3.0.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 (40) hide show
  1. package/README.md +610 -95
  2. package/cjs/controllers/authentication.controller.js +2 -173
  3. package/cjs/controllers/email-verification.controller.js +220 -0
  4. package/cjs/controllers/index.js +1 -0
  5. package/cjs/dtos/authentication.dto.js +12 -2
  6. package/cjs/entities/index.js +16 -17
  7. package/cjs/entities/pending-registration.entity.js +9 -0
  8. package/cjs/modules/auth.module.js +33 -24
  9. package/cjs/services/auth-config.service.js +11 -7
  10. package/cjs/services/auth-datasource.provider.js +15 -27
  11. package/cjs/services/auth-email.service.js +8 -33
  12. package/cjs/services/authentication.service.js +123 -99
  13. package/cjs/services/user.service.js +28 -83
  14. package/controllers/authentication.controller.d.ts +2 -10
  15. package/controllers/email-verification.controller.d.ts +20 -0
  16. package/controllers/index.d.ts +1 -0
  17. package/dtos/authentication.dto.d.ts +1 -0
  18. package/entities/index.d.ts +5 -10
  19. package/entities/pending-registration.entity.d.ts +1 -0
  20. package/fesm/controllers/authentication.controller.js +3 -174
  21. package/fesm/controllers/email-verification.controller.js +210 -0
  22. package/fesm/controllers/index.js +1 -0
  23. package/fesm/dtos/authentication.dto.js +32 -25
  24. package/fesm/entities/index.js +17 -9
  25. package/fesm/entities/pending-registration.entity.js +9 -0
  26. package/fesm/interfaces/user-enricher.interface.js +2 -24
  27. package/fesm/modules/auth.module.js +35 -26
  28. package/fesm/services/auth-config.service.js +11 -7
  29. package/fesm/services/auth-datasource.provider.js +15 -27
  30. package/fesm/services/auth-email.service.js +8 -33
  31. package/fesm/services/authentication.service.js +124 -100
  32. package/fesm/services/user.service.js +28 -83
  33. package/interfaces/user-enricher.interface.d.ts +3 -2
  34. package/modules/auth.module.d.ts +3 -1
  35. package/package.json +3 -3
  36. package/services/auth-config.service.d.ts +1 -0
  37. package/services/auth-datasource.provider.d.ts +1 -0
  38. package/services/auth-email.service.d.ts +1 -0
  39. package/services/authentication.service.d.ts +4 -1
  40. package/services/user.service.d.ts +5 -0
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "AuthConfigService", {
8
8
  return AuthConfigService;
9
9
  }
10
10
  });
11
+ const _config = require("@flusys/nestjs-core/config");
11
12
  const _common = require("@nestjs/common");
12
13
  const _authconstants = require("../config/auth.constants");
13
14
  const _authmoduleoptionsinterface = require("../interfaces/auth-module-options.interface");
@@ -50,21 +51,24 @@ let AuthConfigService = class AuthConfigService {
50
51
  isCompanyFeatureEnabled() {
51
52
  return this.options.bootstrapAppConfig?.enableCompanyFeature ?? false;
52
53
  }
54
+ isEmailVerificationEnabled() {
55
+ return this.options.bootstrapAppConfig?.enableEmailVerification ?? true;
56
+ }
53
57
  // JWT Configuration
54
- /** Get JWT secret key */ getJwtSecret() {
58
+ getJwtSecret() {
55
59
  if (!this.config.jwtSecret) {
56
60
  throw new _common.InternalServerErrorException('[AuthModule] JWT_SECRET is required - set via environment or module config');
57
61
  }
58
62
  return this.config.jwtSecret;
59
63
  }
60
- /** Get JWT expiration time */ getJwtExpiration() {
64
+ getJwtExpiration() {
61
65
  return this.config.jwtExpiration ?? '1h';
62
66
  }
63
- /** Get refresh token secret key */ getRefreshTokenSecret() {
67
+ getRefreshTokenSecret() {
64
68
  if (this.config.refreshTokenSecret) {
65
69
  return this.config.refreshTokenSecret;
66
70
  }
67
- if (process.env.NODE_ENV === 'production') {
71
+ if (_config.envConfig.isProduction()) {
68
72
  throw new _common.InternalServerErrorException('[AuthModule] REFRESH_TOKEN_SECRET is required in production - ' + 'using the same secret for access and refresh tokens is a security risk');
69
73
  }
70
74
  if (!this.refreshTokenSecretWarned) {
@@ -73,14 +77,14 @@ let AuthConfigService = class AuthConfigService {
73
77
  }
74
78
  return this.getJwtSecret();
75
79
  }
76
- /** Get refresh token expiration time */ getRefreshTokenExpiration() {
80
+ getRefreshTokenExpiration() {
77
81
  return this.config.refreshTokenExpiration ?? '7d';
78
82
  }
79
- /** Get refresh token cookie name */ getRefreshTokenCookieName() {
83
+ getRefreshTokenCookieName() {
80
84
  return this.config.refreshTokenCookieName ?? 'fsn_refresh_token';
81
85
  }
82
86
  // Frontend Configuration
83
- /** Get frontend URL for email links */ getFrontendUrl() {
87
+ getFrontendUrl() {
84
88
  return this.config.frontendUrl;
85
89
  }
86
90
  constructor(injectedOptions){
@@ -83,8 +83,7 @@ function _ts_param(paramIndex, decorator) {
83
83
  };
84
84
  }
85
85
  let AuthDataSourceProvider = class AuthDataSourceProvider extends _modules.MultiTenantDataSourceService {
86
- // Override Static Cache Access Methods
87
- /** Override to access Auth-specific static properties */ async getSingleDataSource() {
86
+ async getSingleDataSource() {
88
87
  if (!AuthDataSourceProvider.singleDataSource) {
89
88
  if (AuthDataSourceProvider.singleConnectionLock) {
90
89
  return AuthDataSourceProvider.singleConnectionLock;
@@ -108,18 +107,15 @@ let AuthDataSourceProvider = class AuthDataSourceProvider extends _modules.Multi
108
107
  }
109
108
  return AuthDataSourceProvider.singleDataSource;
110
109
  }
111
- /** Override to use Auth-specific static cache for tenant connections */ async getOrCreateTenantConnection(tenant) {
112
- // Return existing initialized connection from Auth-specific cache
110
+ async getOrCreateTenantConnection(tenant) {
113
111
  const existing = AuthDataSourceProvider.tenantConnections.get(tenant.id);
114
112
  if (existing?.isInitialized) {
115
113
  return existing;
116
114
  }
117
- // If another request is creating this tenant's connection, wait for it
118
115
  const pendingConnection = AuthDataSourceProvider.connectionLocks.get(tenant.id);
119
116
  if (pendingConnection) {
120
117
  return pendingConnection;
121
118
  }
122
- // Create connection with lock to prevent race conditions
123
119
  const config = this.buildTenantDatabaseConfig(tenant);
124
120
  const connectionPromise = this.createDataSourceFromConfig(config);
125
121
  AuthDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
@@ -131,8 +127,7 @@ let AuthDataSourceProvider = class AuthDataSourceProvider extends _modules.Multi
131
127
  AuthDataSourceProvider.connectionLocks.delete(tenant.id);
132
128
  }
133
129
  }
134
- // Factory Methods
135
- /** Build parent options from AuthModuleOptions */ static buildParentOptions(options) {
130
+ static buildParentOptions(options) {
136
131
  return {
137
132
  bootstrapAppConfig: options.bootstrapAppConfig,
138
133
  defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
@@ -140,35 +135,28 @@ let AuthDataSourceProvider = class AuthDataSourceProvider extends _modules.Multi
140
135
  tenants: options.config?.tenants
141
136
  };
142
137
  }
143
- // Feature Flags
144
- /** Check if company feature is enabled (tenant-specific or global fallback) */ isCompanyFeatureEnabled(tenant) {
138
+ isCompanyFeatureEnabled(tenant) {
145
139
  const effectiveTenant = tenant ?? this.getCurrentTenant();
146
140
  return effectiveTenant?.enableCompanyFeature ?? this.authOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
147
141
  }
148
- // Entity Management
149
- /** Get auth entities based on company feature flag */ async getAuthEntities() {
150
- const { Company, CompanyBranch, User, UserCompanyPermission, UserToken, PendingRegistration } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")));
151
- const coreEntities = [
152
- User,
153
- UserToken,
154
- PendingRegistration
155
- ];
156
- return this.isCompanyFeatureEnabled() ? [
157
- ...coreEntities,
158
- Company,
159
- CompanyBranch,
160
- UserCompanyPermission
161
- ] : coreEntities;
142
+ isEmailVerificationEnabled(tenant) {
143
+ const effectiveTenant = tenant ?? this.getCurrentTenant();
144
+ return effectiveTenant?.enableEmailVerification ?? this.authOptions.bootstrapAppConfig?.enableEmailVerification ?? true;
145
+ }
146
+ async getAuthEntities() {
147
+ const { getEntitiesByConfig } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")));
148
+ return getEntitiesByConfig({
149
+ enableCompanyFeature: this.isCompanyFeatureEnabled(),
150
+ enableEmailVerification: this.isEmailVerificationEnabled()
151
+ });
162
152
  }
163
- // Overrides
164
- /** Override to dynamically set entities based on tenant config */ async createDataSourceFromConfig(config) {
153
+ async createDataSourceFromConfig(config) {
165
154
  return super.createDataSourceFromConfig(config, await this.getAuthEntities());
166
155
  }
167
156
  constructor(authOptions, request){
168
157
  super(AuthDataSourceProvider.buildParentOptions(authOptions), request), _define_property(this, "authOptions", void 0), _define_property(this, "logger", void 0), this.authOptions = authOptions, this.logger = new _common.Logger(AuthDataSourceProvider.name);
169
158
  }
170
159
  };
171
- // Override parent's static properties to have Auth-specific cache (isolated from IAM/Storage)
172
160
  _define_property(AuthDataSourceProvider, "tenantConnections", new Map());
173
161
  _define_property(AuthDataSourceProvider, "singleDataSource", null);
174
162
  _define_property(AuthDataSourceProvider, "tenantsRegistry", new Map());
@@ -90,9 +90,6 @@ let AuthEmailService = class AuthEmailService {
90
90
  isEmailEnabled() {
91
91
  return !!this.emailProvider;
92
92
  }
93
- // ============================================
94
- // Private Helpers
95
- // ============================================
96
93
  async getRepository(entity) {
97
94
  if (!this.dataSourceProvider) {
98
95
  throw new _common.InternalServerErrorException(`${String(entity)} repository not available`);
@@ -102,7 +99,7 @@ let AuthEmailService = class AuthEmailService {
102
99
  generateToken() {
103
100
  return _crypto.randomBytes(32).toString('hex');
104
101
  }
105
- /** Hash token for secure storage - prevents token theft if database is compromised */ hashToken(token) {
102
+ hashToken(token) {
106
103
  return _crypto.createHash('sha256').update(token).digest('hex');
107
104
  }
108
105
  getErrorMessage(error) {
@@ -130,9 +127,6 @@ let AuthEmailService = class AuthEmailService {
130
127
  throw new _common.BadRequestException(usedMessage);
131
128
  }
132
129
  }
133
- // ============================================
134
- // Pending Registration (New Users)
135
- // ============================================
136
130
  async hasPendingRegistration(email) {
137
131
  if (!this.isEmailEnabled()) return false;
138
132
  const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
@@ -172,7 +166,8 @@ let AuthEmailService = class AuthEmailService {
172
166
  passwordHash,
173
167
  token: tokenHash,
174
168
  expiresAt,
175
- companyData: data.companyData ?? null
169
+ companyData: data.companyData ?? null,
170
+ additionalFields: data.additionalFields ?? null
176
171
  });
177
172
  await pendingRepository.save(pending);
178
173
  // Send verification email with raw token (user receives this)
@@ -183,12 +178,7 @@ let AuthEmailService = class AuthEmailService {
183
178
  message: 'Please check your email to verify your registration.'
184
179
  };
185
180
  }
186
- // ============================================
187
- // Password Reset (Existing Users)
188
- // ============================================
189
- /**
190
- * Request password reset - sends reset email
191
- */ async forgotPassword(email, resetUrlBase) {
181
+ async forgotPassword(email, resetUrlBase) {
192
182
  this.assertEmailEnabled();
193
183
  const userRepository = await this.getRepository(_userentity.User);
194
184
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
@@ -234,9 +224,7 @@ let AuthEmailService = class AuthEmailService {
234
224
  message: 'If the email exists, a password reset link has been sent.'
235
225
  };
236
226
  }
237
- /**
238
- * Reset password using token
239
- */ async resetPassword(token, newPassword) {
227
+ async resetPassword(token, newPassword) {
240
228
  const userRepository = await this.getRepository(_userentity.User);
241
229
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
242
230
  // Hash the incoming token to compare with stored hash
@@ -271,13 +259,7 @@ let AuthEmailService = class AuthEmailService {
271
259
  message: 'Password has been reset successfully.'
272
260
  };
273
261
  }
274
- // ============================================
275
- // Email Verification
276
- // ============================================
277
- /**
278
- * Verify email using token
279
- * Handles both pending registration and existing user verification
280
- */ async verifyEmail(token) {
262
+ async verifyEmail(token) {
281
263
  const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
282
264
  const userRepository = await this.getRepository(_userentity.User);
283
265
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
@@ -347,9 +329,7 @@ let AuthEmailService = class AuthEmailService {
347
329
  message: 'Email has been verified successfully.'
348
330
  };
349
331
  }
350
- /**
351
- * Resend verification email - handles both pending registrations and existing users
352
- */ async resendVerificationEmail(email, verifyUrlBase) {
332
+ async resendVerificationEmail(email, verifyUrlBase) {
353
333
  this.assertEmailEnabled();
354
334
  const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
355
335
  const userRepository = await this.getRepository(_userentity.User);
@@ -404,12 +384,7 @@ let AuthEmailService = class AuthEmailService {
404
384
  message: 'If the email requires verification, a link has been sent.'
405
385
  };
406
386
  }
407
- // ============================================
408
- // Cleanup
409
- // ============================================
410
- /**
411
- * Clean up expired tokens and pending registrations
412
- */ async cleanupExpired() {
387
+ async cleanupExpired() {
413
388
  const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
414
389
  const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
415
390
  const now = new Date();