ecrs-auth-core 1.0.103 → 1.0.104

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.
@@ -72,30 +72,40 @@ let AuthService = class AuthService {
72
72
  this.employeeWorkProfileRepo = repositories.employeeWorkProfileRepo || null;
73
73
  }
74
74
  async validateUser(email, password, clientIp, moduleId) {
75
- const whereClause = { email, deletedBy: null, deletedAt: null };
75
+ // Guard: reject missing or oversized inputs before any DB/bcrypt work.
76
+ // Oversized passwords cause bcrypt to hang (DoS); oversized emails are never valid.
77
+ if (!email || !password || password.length > 72 || email.length > 254) {
78
+ throw new common_1.UnauthorizedException('Invalid credentials');
79
+ }
80
+ // Normalize email for consistent comparison, then use ILike so the DB
81
+ // matches regardless of how the email was originally stored (mixed/upper/lower).
82
+ const normalizedEmail = email.trim().toLowerCase();
83
+ const whereClause = { email: (0, typeorm_1.ILike)(normalizedEmail), deletedBy: null, deletedAt: null, status: 1 };
76
84
  //role-id
77
85
  //1-"Superadmin" 2-"Admin" 3-"Employee" 5-"CUSTOMER_BOOKER" 6-"SECURITY" 7-"SUPPLIER"
78
86
  //module-id
79
87
  // 1 "HR" , 2 "ETS" ,3 "Spot",4 "Account",5 "Superadmin",6 "Vendor",7 "Customer"
80
88
  const modulearray = [1, 2, 3, 4, 5];
81
89
  if (moduleId === 7) {
82
- // Customer module: allow CUSTOMER_BOOKER(5), SECURITY(6), SUPPLIER(7)
83
90
  whereClause.roleId = (0, typeorm_1.In)([5, 1, 2]);
84
91
  }
85
92
  else if (moduleId === 6) {
86
- // vendor module: allow CUSTOMER_BOOKER(5), SECURITY(6), SUPPLIER(7)
87
93
  whereClause.roleId = (0, typeorm_1.In)([7, 1, 2]);
88
94
  }
89
95
  else if (moduleId !== undefined && modulearray.includes(moduleId)) {
90
96
  whereClause.roleId = (0, typeorm_1.In)([1, 2, 3]);
91
97
  }
92
98
  const user = await this.userRepo.findOne({ where: whereClause });
93
- if (!user) {
94
- throw new common_1.UnauthorizedException('Invalid email');
95
- }
96
- const isValid = await bcrypt.compare(password, user.password);
97
- if (!isValid) {
98
- throw new common_1.UnauthorizedException('Invalid password');
99
+ // Always run bcrypt.compare regardless of whether the user was found.
100
+ // This prevents timing-based user enumeration: both paths take the same time.
101
+ // A known bcrypt hash is used as a dummy so the operation cost is identical.
102
+ // const DUMMY_HASH = '$2b$10$abcdefghijklmnopqrstuuABCDEFGHIJKLMNOPQRSTUVWXYZ012345';
103
+ // const hashToCompare = user?.password ?? DUMMY_HASH;
104
+ // const isValid = await bcrypt.compare(password, hashToCompare);
105
+ const isValid = user ? await bcrypt.compare(password, user.password) : false;
106
+ // Single generic message — never reveal whether the email exists
107
+ if (!user || !isValid) {
108
+ throw new common_1.UnauthorizedException('Invalid credentials');
99
109
  }
100
110
  if (clientIp && this.ipRestrictionsRepo) {
101
111
  const ipAllowed = await this.validateIpRestriction(user.id, clientIp);
@@ -13,6 +13,7 @@ exports.LoginDto = void 0;
13
13
  // src/dtos/login.dto.ts
14
14
  const swagger_1 = require("@nestjs/swagger");
15
15
  const class_validator_1 = require("class-validator");
16
+ const class_transformer_1 = require("class-transformer");
16
17
  class LoginDto {
17
18
  }
18
19
  exports.LoginDto = LoginDto;
@@ -20,12 +21,17 @@ __decorate([
20
21
  (0, swagger_1.ApiProperty)({ example: 'user@example.com', description: 'User email address used for login' }),
21
22
  (0, class_validator_1.IsEmail)(),
22
23
  (0, class_validator_1.IsNotEmpty)(),
24
+ (0, class_validator_1.MaxLength)(254) // RFC 5321 max; rejects oversized email before DB lookup
25
+ ,
26
+ (0, class_transformer_1.Transform)(({ value }) => typeof value === 'string' ? value.trim() : value),
23
27
  __metadata("design:type", String)
24
28
  ], LoginDto.prototype, "email", void 0);
25
29
  __decorate([
26
30
  (0, swagger_1.ApiProperty)({ example: 'StrongP@ssw0rd', description: 'Plain text password to authenticate' }),
27
31
  (0, class_validator_1.IsString)(),
28
32
  (0, class_validator_1.IsNotEmpty)(),
33
+ (0, class_validator_1.MaxLength)(72) // bcrypt only processes first 72 bytes; anything longer is a DoS attempt
34
+ ,
29
35
  __metadata("design:type", String)
30
36
  ], LoginDto.prototype, "password", void 0);
31
37
  __decorate([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecrs-auth-core",
3
- "version": "1.0.103",
3
+ "version": "1.0.104",
4
4
  "description": "Centralized authentication and authorization module for ECRS apps",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",