@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
@@ -12,12 +12,12 @@ const _common = require("@nestjs/common");
12
12
  const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
13
13
  const _crypto = /*#__PURE__*/ _interop_require_wildcard(require("crypto"));
14
14
  const _typeorm = require("typeorm");
15
- const _authdatasourceprovider = require("./auth-datasource.provider");
15
+ const _config = require("../config");
16
16
  const _pendingregistrationentity = require("../entities/pending-registration.entity");
17
17
  const _usertokenentity = require("../entities/user-token.entity");
18
18
  const _userentity = require("../entities/user.entity");
19
19
  const _interfaces = require("../interfaces");
20
- const _config = require("../config");
20
+ const _authdatasourceprovider = require("./auth-datasource.provider");
21
21
  function _define_property(obj, key, value) {
22
22
  if (key in obj) {
23
23
  Object.defineProperty(obj, key, {
@@ -87,44 +87,66 @@ function _ts_param(paramIndex, decorator) {
87
87
  };
88
88
  }
89
89
  let AuthEmailService = class AuthEmailService {
90
- async getUserRepository() {
91
- if (this.dataSourceProvider) {
92
- return this.dataSourceProvider.getRepository(_userentity.User);
93
- }
94
- throw new _common.InternalServerErrorException('User repository not available');
95
- }
96
- async getUserTokenRepository() {
97
- if (this.dataSourceProvider) {
98
- return this.dataSourceProvider.getRepository(_usertokenentity.UserToken);
99
- }
100
- throw new _common.InternalServerErrorException('UserToken repository not available');
101
- }
102
- async getPendingRegistrationRepository() {
103
- if (this.dataSourceProvider) {
104
- return this.dataSourceProvider.getRepository(_pendingregistrationentity.PendingRegistration);
105
- }
106
- throw new _common.InternalServerErrorException('PendingRegistration repository not available');
107
- }
108
90
  isEmailEnabled() {
109
91
  return !!this.emailProvider;
110
92
  }
93
+ // ============================================
94
+ // Private Helpers
95
+ // ============================================
96
+ async getRepository(entity) {
97
+ if (!this.dataSourceProvider) {
98
+ throw new _common.InternalServerErrorException(`${String(entity)} repository not available`);
99
+ }
100
+ return this.dataSourceProvider.getRepository(entity);
101
+ }
111
102
  generateToken() {
112
103
  return _crypto.randomBytes(32).toString('hex');
113
104
  }
114
105
  /** Hash token for secure storage - prevents token theft if database is compromised */ hashToken(token) {
115
106
  return _crypto.createHash('sha256').update(token).digest('hex');
116
107
  }
117
- // ============================================
118
- // Pending Registration (New Users)
119
- // ============================================
120
- /**
121
- * Create pending registration - stores data until email verified
122
- */ async createPendingRegistration(data) {
108
+ getErrorMessage(error) {
109
+ return error instanceof Error ? error.message : 'Unknown error';
110
+ }
111
+ assertEmailEnabled() {
123
112
  if (!this.emailProvider) {
124
113
  throw new _common.BadRequestException('Email service is not configured');
125
114
  }
126
- const userRepository = await this.getUserRepository();
127
- const pendingRepository = await this.getPendingRegistrationRepository();
115
+ }
116
+ async sendVerificationEmailSafe(email, token, verifyUrl, logContext) {
117
+ try {
118
+ await this.emailProvider.sendVerificationEmail(email, token, verifyUrl);
119
+ this.logger.log(`${logContext} verification email sent to: ${email}`);
120
+ } catch (error) {
121
+ this.logger.error(`Failed to send verification email: ${this.getErrorMessage(error)}`);
122
+ throw new _common.BadRequestException('Failed to send verification email. Please try again.');
123
+ }
124
+ }
125
+ validateTokenNotExpiredOrUsed(token, expiredMessage, usedMessage) {
126
+ if (token.expiresAt < new Date()) {
127
+ throw new _common.BadRequestException(expiredMessage);
128
+ }
129
+ if (usedMessage && token.usedAt) {
130
+ throw new _common.BadRequestException(usedMessage);
131
+ }
132
+ }
133
+ // ============================================
134
+ // Pending Registration (New Users)
135
+ // ============================================
136
+ async hasPendingRegistration(email) {
137
+ if (!this.isEmailEnabled()) return false;
138
+ const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
139
+ const pending = await pendingRepository.findOne({
140
+ where: {
141
+ email
142
+ }
143
+ });
144
+ return !!pending && pending.expiresAt > new Date();
145
+ }
146
+ async createPendingRegistration(data) {
147
+ this.assertEmailEnabled();
148
+ const userRepository = await this.getRepository(_userentity.User);
149
+ const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
128
150
  // Check if user already exists
129
151
  const existingUser = await userRepository.findOne({
130
152
  where: {
@@ -155,14 +177,7 @@ let AuthEmailService = class AuthEmailService {
155
177
  await pendingRepository.save(pending);
156
178
  // Send verification email with raw token (user receives this)
157
179
  const verifyUrl = `${data.verifyUrlBase}?token=${token}`;
158
- try {
159
- await this.emailProvider.sendVerificationEmail(data.email, token, verifyUrl);
160
- this.logger.log(`Registration verification email sent to: ${data.email}`);
161
- } catch (error) {
162
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
163
- this.logger.error(`Failed to send verification email: ${errorMessage}`);
164
- throw new _common.BadRequestException('Failed to send verification email. Please try again.');
165
- }
180
+ await this.sendVerificationEmailSafe(data.email, token, verifyUrl, 'Registration');
166
181
  return {
167
182
  success: true,
168
183
  message: 'Please check your email to verify your registration.'
@@ -174,11 +189,9 @@ let AuthEmailService = class AuthEmailService {
174
189
  /**
175
190
  * Request password reset - sends reset email
176
191
  */ async forgotPassword(email, resetUrlBase) {
177
- if (!this.emailProvider) {
178
- throw new _common.BadRequestException('Email service is not configured');
179
- }
180
- const userRepository = await this.getUserRepository();
181
- const tokenRepository = await this.getUserTokenRepository();
192
+ this.assertEmailEnabled();
193
+ const userRepository = await this.getRepository(_userentity.User);
194
+ const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
182
195
  const user = await userRepository.findOne({
183
196
  where: {
184
197
  email
@@ -213,9 +226,7 @@ let AuthEmailService = class AuthEmailService {
213
226
  await this.emailProvider.sendPasswordResetEmail(email, token, resetUrl);
214
227
  this.logger.log(`Password reset email sent to: ${email}`);
215
228
  } catch (error) {
216
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
217
- this.logger.error(`Failed to send password reset email: ${errorMessage}`);
218
- // Don't silently fail - but still use same message to prevent enumeration
229
+ this.logger.error(`Failed to send password reset email: ${this.getErrorMessage(error)}`);
219
230
  throw new _common.BadRequestException('Failed to process request. Please try again later.');
220
231
  }
221
232
  return {
@@ -226,8 +237,8 @@ let AuthEmailService = class AuthEmailService {
226
237
  /**
227
238
  * Reset password using token
228
239
  */ async resetPassword(token, newPassword) {
229
- const userRepository = await this.getUserRepository();
230
- const tokenRepository = await this.getUserTokenRepository();
240
+ const userRepository = await this.getRepository(_userentity.User);
241
+ const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
231
242
  // Hash the incoming token to compare with stored hash
232
243
  const tokenHash = this.hashToken(token);
233
244
  const userToken = await tokenRepository.findOne({
@@ -239,15 +250,7 @@ let AuthEmailService = class AuthEmailService {
239
250
  if (!userToken) {
240
251
  throw new _common.BadRequestException('Invalid or expired reset token');
241
252
  }
242
- if (userToken.expiresAt < new Date()) {
243
- await tokenRepository.delete({
244
- id: userToken.id
245
- });
246
- throw new _common.BadRequestException('Reset token has expired. Please request a new one.');
247
- }
248
- if (userToken.usedAt) {
249
- throw new _common.BadRequestException('This reset token has already been used.');
250
- }
253
+ this.validateTokenNotExpiredOrUsed(userToken, 'Reset token has expired. Please request a new one.', 'This reset token has already been used.');
251
254
  const user = await userRepository.findOne({
252
255
  where: {
253
256
  id: userToken.userId
@@ -275,9 +278,9 @@ let AuthEmailService = class AuthEmailService {
275
278
  * Verify email using token
276
279
  * Handles both pending registration and existing user verification
277
280
  */ async verifyEmail(token) {
278
- const pendingRepository = await this.getPendingRegistrationRepository();
279
- const userRepository = await this.getUserRepository();
280
- const tokenRepository = await this.getUserTokenRepository();
281
+ const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
282
+ const userRepository = await this.getRepository(_userentity.User);
283
+ const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
281
284
  // Hash the incoming token to compare with stored hash
282
285
  const tokenHash = this.hashToken(token);
283
286
  // Check for pending registration first
@@ -302,7 +305,7 @@ let AuthEmailService = class AuthEmailService {
302
305
  pending
303
306
  };
304
307
  }
305
- // Check for existing user email verification token (token already hashed above)
308
+ // Check for existing user email verification token
306
309
  const userToken = await tokenRepository.findOne({
307
310
  where: {
308
311
  token: tokenHash,
@@ -347,12 +350,10 @@ let AuthEmailService = class AuthEmailService {
347
350
  /**
348
351
  * Resend verification email - handles both pending registrations and existing users
349
352
  */ async resendVerificationEmail(email, verifyUrlBase) {
350
- if (!this.emailProvider) {
351
- throw new _common.BadRequestException('Email service is not configured');
352
- }
353
- const pendingRepository = await this.getPendingRegistrationRepository();
354
- const userRepository = await this.getUserRepository();
355
- const tokenRepository = await this.getUserTokenRepository();
353
+ this.assertEmailEnabled();
354
+ const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
355
+ const userRepository = await this.getRepository(_userentity.User);
356
+ const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
356
357
  // Check pending registration first
357
358
  const pending = await pendingRepository.findOne({
358
359
  where: {
@@ -361,19 +362,11 @@ let AuthEmailService = class AuthEmailService {
361
362
  });
362
363
  if (pending) {
363
364
  const token = this.generateToken();
364
- const tokenHash = this.hashToken(token);
365
- pending.token = tokenHash; // Store hashed token
365
+ pending.token = this.hashToken(token);
366
366
  pending.expiresAt = new Date(Date.now() + this.EMAIL_VERIFICATION_EXPIRY);
367
367
  await pendingRepository.save(pending);
368
368
  const verifyUrl = `${verifyUrlBase}?token=${token}`;
369
- try {
370
- await this.emailProvider.sendVerificationEmail(email, token, verifyUrl);
371
- this.logger.log(`Re-sent registration verification email to: ${email}`);
372
- } catch (error) {
373
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
374
- this.logger.error(`Failed to resend verification email: ${errorMessage}`);
375
- throw new _common.BadRequestException('Failed to send verification email. Please try again.');
376
- }
369
+ await this.sendVerificationEmailSafe(email, token, verifyUrl, 'Re-sent registration');
377
370
  return {
378
371
  success: true,
379
372
  message: 'If the email requires verification, a link has been sent.'
@@ -397,23 +390,15 @@ let AuthEmailService = class AuthEmailService {
397
390
  type: _usertokenentity.TokenType.EMAIL_VERIFICATION
398
391
  });
399
392
  const token = this.generateToken();
400
- const tokenHash = this.hashToken(token);
401
393
  const userToken = tokenRepository.create({
402
394
  userId: user.id,
403
395
  type: _usertokenentity.TokenType.EMAIL_VERIFICATION,
404
- token: tokenHash,
396
+ token: this.hashToken(token),
405
397
  expiresAt: new Date(Date.now() + this.EMAIL_VERIFICATION_EXPIRY)
406
398
  });
407
399
  await tokenRepository.save(userToken);
408
400
  const verifyUrl = `${verifyUrlBase}?token=${token}`;
409
- try {
410
- await this.emailProvider.sendVerificationEmail(user.email, token, verifyUrl);
411
- this.logger.log(`Verification email sent to: ${user.email}`);
412
- } catch (error) {
413
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
414
- this.logger.error(`Failed to send verification email: ${errorMessage}`);
415
- throw new _common.BadRequestException('Failed to send verification email. Please try again.');
416
- }
401
+ await this.sendVerificationEmailSafe(user.email, token, verifyUrl, 'Verification');
417
402
  return {
418
403
  success: true,
419
404
  message: 'If the email requires verification, a link has been sent.'
@@ -425,8 +410,8 @@ let AuthEmailService = class AuthEmailService {
425
410
  /**
426
411
  * Clean up expired tokens and pending registrations
427
412
  */ async cleanupExpired() {
428
- const tokenRepository = await this.getUserTokenRepository();
429
- const pendingRepository = await this.getPendingRegistrationRepository();
413
+ const tokenRepository = await this.getRepository(_usertokenentity.UserToken);
414
+ const pendingRepository = await this.getRepository(_pendingregistrationentity.PendingRegistration);
430
415
  const now = new Date();
431
416
  const tokenResult = await tokenRepository.delete({
432
417
  expiresAt: (0, _typeorm.LessThan)(now)