ecrs-auth-core 1.0.99 → 1.0.100

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.
@@ -16,6 +16,7 @@ export declare class AuthController {
16
16
  roleId: number;
17
17
  roleName: string | null;
18
18
  moduleId: number;
19
+ tokenVersion: number;
19
20
  name: string;
20
21
  firstName: string;
21
22
  lastName: string;
@@ -33,9 +33,7 @@ let AuthController = class AuthController {
33
33
  console.log(`📍 Login attempt from IP: ${clientIp}, User-Agent: ${userAgent}`);
34
34
  // Validate user with IP restriction check
35
35
  const user = await this.authService.validateUser(body.email, body.password, clientIp);
36
- console.log(`🔐 User validation result for ${body.email}: ${user ? 'Success' : 'Failed'}`);
37
36
  if (!user) {
38
- console.warn(`⚠️ Failed login attempt for ${body.email} from IP ${clientIp}`);
39
37
  // Save failed login attempt to both tables
40
38
  await this.authService.saveLastLogin({ email: body.email }, clientIp, 'failed', 'Invalid credentials or IP not allowed', additionalData).catch((err) => {
41
39
  console.error('❌ Error saving failed login to tbl_user_last_login:', err.message);
@@ -55,12 +53,12 @@ let AuthController = class AuthController {
55
53
  console.warn(`⚠️ Invalid module ID provided by user ${body.email}: ${body.moduleId}`);
56
54
  throw new common_1.UnauthorizedException('You are not authorized to access this module');
57
55
  }
58
- // console.log(`🔍 Checking module access for user ID ${user.id} and module ID ${requestedModuleId}...`);
59
- // // const allowedDb = await this.authService.hasModuleAccess(user.id, requestedModuleId);
60
- // console.log(`📊 Module access check result for user ID ${user.id} and module ID ${requestedModuleId}: ${allowedDb ? 'Allowed' : 'Denied'}`);
61
- // if (!allowedDb) {
62
- // throw new UnauthorizedException('You are not authorized to access this module');
63
- // }
56
+ console.log(`🔍 Checking module access for user ID ${user.id} and module ID ${requestedModuleId}...`);
57
+ const allowedDb = await this.authService.hasModuleAccess(user.id, requestedModuleId);
58
+ console.log(`📊 Module access check result for user ID ${user.id} and module ID ${requestedModuleId}: ${allowedDb ? 'Allowed' : 'Denied'}`);
59
+ if (!allowedDb) {
60
+ throw new common_1.UnauthorizedException('You are not authorized to access this module');
61
+ }
64
62
  // const perms = await this.authService.getPermissions(user.id);
65
63
  // console.log(`📊 User permissions for ${body.email}:`, perms);
66
64
  // if (!Array.isArray(perms.modules) || !perms.modules.includes(requestedModuleId)) {
@@ -25,7 +25,7 @@ export declare class AuthService {
25
25
  private readonly employeeWorkProfileRepo;
26
26
  private uploadPhotoDir;
27
27
  constructor(jwtService: JwtService, options: AuthCoreOptions);
28
- validateUser(email: string, password: string, clientIp?: string): Promise<User | null>;
28
+ validateUser(email: string, password: string, clientIp?: string): Promise<User>;
29
29
  /**
30
30
  * Validate IP restriction for a user
31
31
  *
@@ -88,6 +88,7 @@ export declare class AuthService {
88
88
  roleId: number;
89
89
  roleName: string | null;
90
90
  moduleId: number;
91
+ tokenVersion: number;
91
92
  name: string;
92
93
  firstName: string;
93
94
  lastName: string;
@@ -114,5 +115,7 @@ export declare class AuthService {
114
115
  */
115
116
  extractUserIpv4(clientIp: string | undefined): string;
116
117
  findUserById(id: number): Promise<User | null>;
118
+ findUserLastLogin(userId: number): Promise<any>;
119
+ updateLastActivity(loginId: number): Promise<void>;
117
120
  private loadModulePermissions;
118
121
  }
@@ -72,18 +72,17 @@ let AuthService = class AuthService {
72
72
  }
73
73
  async validateUser(email, password, clientIp) {
74
74
  const user = await this.userRepo.findOne({ where: { email } });
75
- if (!user)
76
- return null;
75
+ if (!user) {
76
+ throw new common_1.UnauthorizedException('Invalid email');
77
+ }
77
78
  const isValid = await bcrypt.compare(password, user.password);
78
- if (!isValid)
79
- return null;
80
- // console.log(this.ipRestrictionsRepo);
81
- // Check IP restrictions if provided and repository is available
79
+ if (!isValid) {
80
+ throw new common_1.UnauthorizedException('Invalid password');
81
+ }
82
82
  if (clientIp && this.ipRestrictionsRepo) {
83
83
  const ipAllowed = await this.validateIpRestriction(user.id, clientIp);
84
84
  if (!ipAllowed) {
85
- // IP restriction exists but doesn't match - return null to block login
86
- return null;
85
+ throw new common_1.UnauthorizedException('Login not allowed from this IP address');
87
86
  }
88
87
  }
89
88
  return user;
@@ -172,7 +171,7 @@ let AuthService = class AuthService {
172
171
  AND status = 1
173
172
  AND is_deleted = 0
174
173
  LIMIT 1`, [userId, moduleId]);
175
- console.log('Raw Query Result:', result);
174
+ // console.log('Raw Query Result:', result);
176
175
  const allowed = result.length > 0;
177
176
  console.log(`📊 Module access check result for user ID ${userId} and module ID ${moduleId}: ${allowed ? 'Allowed' : 'Denied'}`);
178
177
  return allowed;
@@ -489,6 +488,7 @@ let AuthService = class AuthService {
489
488
  roleId: user.roleId,
490
489
  roleName: roleName,
491
490
  moduleId: effectiveModuleId,
491
+ tokenVersion: user.token_version || 1,
492
492
  name: `${user.firstName} ${user.lastName}`,
493
493
  firstName: user.firstName,
494
494
  lastName: user.lastName,
@@ -515,6 +515,7 @@ let AuthService = class AuthService {
515
515
  roleId: user.roleId,
516
516
  roleName: roleName,
517
517
  moduleId: effectiveModuleId,
518
+ tokenVersion: user.token_version || 1,
518
519
  name: `${user.firstName} ${user.lastName}`,
519
520
  firstName: user.firstName,
520
521
  lastName: user.lastName,
@@ -548,6 +549,42 @@ let AuthService = class AuthService {
548
549
  async findUserById(id) {
549
550
  return this.userRepo.findOne({ where: { id } });
550
551
  }
552
+ async findUserLastLogin(userId) {
553
+ let lastLoginTime = null;
554
+ if (this.userLastLoginRepo) {
555
+ try {
556
+ const result = await this.userLastLoginRepo
557
+ .createQueryBuilder('login')
558
+ .select('login.login_time', 'login_time')
559
+ .where('login.user_id = :userId', { userId })
560
+ .andWhere('login.logout_time IS NULL')
561
+ .orderBy('login.login_time', 'DESC')
562
+ .limit(1)
563
+ .getRawOne();
564
+ if (result) {
565
+ lastLoginTime = result?.login_time || null;
566
+ }
567
+ }
568
+ catch (error) {
569
+ console.error('Error fetching last login details:', error);
570
+ // Continue with null values if fetch fails
571
+ }
572
+ }
573
+ return lastLoginTime;
574
+ }
575
+ async updateLastActivity(loginId) {
576
+ try {
577
+ if (this.userLastLoginRepo) {
578
+ await this.userLastLoginRepo.update({ id: loginId }, {
579
+ login_time: new Date(), // 🔥 update activity
580
+ updated_at: new Date(),
581
+ });
582
+ }
583
+ }
584
+ catch (error) {
585
+ console.error('Error updating last activity:', error);
586
+ }
587
+ }
551
588
  // private async loadPermissions(userId: number): Promise<PermissionsTree> {
552
589
  // // Feature Permissions
553
590
  // const featureAccessList = await this.featureAccessRepo.find({
@@ -16,6 +16,7 @@ export declare class User {
16
16
  otpNumber: number;
17
17
  isOtpVerified: number;
18
18
  moduleId: number;
19
+ token_version: number;
19
20
  createdBy: number;
20
21
  updatedBy: number;
21
22
  deletedBy: number;
@@ -83,6 +83,10 @@ __decorate([
83
83
  (0, typeorm_1.Column)({ nullable: true }),
84
84
  __metadata("design:type", Number)
85
85
  ], User.prototype, "moduleId", void 0);
86
+ __decorate([
87
+ (0, typeorm_1.Column)({ type: 'int', default: 1 }),
88
+ __metadata("design:type", Number)
89
+ ], User.prototype, "token_version", void 0);
86
90
  __decorate([
87
91
  (0, typeorm_1.Column)(),
88
92
  __metadata("design:type", Number)
@@ -9,6 +9,6 @@ export declare class JwtStrategy extends JwtStrategy_base {
9
9
  email: string;
10
10
  roleId: number;
11
11
  moduleId: any;
12
- } | null>;
12
+ }>;
13
13
  }
14
14
  export {};
@@ -25,13 +25,40 @@ let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(pas
25
25
  }
26
26
  async validate(payload) {
27
27
  const user = await this.authService.findUserById(payload.id);
28
- if (!user)
29
- return null;
30
- // if (payload.moduleId) {
31
- // const hasAccess = await this.authService.hasModuleAccess(user.id, payload.moduleId);
32
- // if (!hasAccess) return null;
33
- // console.log(`✅ User ${user.id} has access to module ${payload.moduleId}`);
34
- // }
28
+ if (!user) {
29
+ throw new common_1.UnauthorizedException('INVALID_USER');
30
+ }
31
+ // 🔥 1. TOKEN VERSION CHECK (DO FIRST → fast reject)
32
+ if (user.token_version !== payload.tokenVersion) {
33
+ console.warn(`⚠️ Token version mismatch for user ${user.id}. Expected ${user.token_version}, got ${payload.tokenVersion}`);
34
+ throw new common_1.UnauthorizedException('TOKEN_EXPIRED');
35
+ }
36
+ // 🔥 2. MODULE ACCESS CHECK
37
+ if (payload.moduleId) {
38
+ const hasAccess = await this.authService.hasModuleAccess(user.id, payload.moduleId);
39
+ if (!hasAccess) {
40
+ throw new common_1.UnauthorizedException('MODULE_ACCESS_DENIED');
41
+ }
42
+ console.log(`✅ User ${user.id} has access to module ${payload.moduleId}`);
43
+ }
44
+ // 🔥 3. SESSION / LAST ACTIVITY CHECK
45
+ const lastLogin = await this.authService.findUserLastLogin(user.id);
46
+ if (!lastLogin) {
47
+ throw new common_1.UnauthorizedException('SESSION_NOT_FOUND');
48
+ }
49
+ const lastActivityTime = new Date(lastLogin.lastActivityTime || lastLogin.login_time).getTime();
50
+ const currentTime = Date.now();
51
+ const inactivityMinutes = (currentTime - lastActivityTime) / (1000 * 60);
52
+ const INACTIVITY_TIMEOUT_MINUTES = 20;
53
+ // 🔥 Idle timeout
54
+ if (inactivityMinutes > INACTIVITY_TIMEOUT_MINUTES) {
55
+ console.log(`❌ User ${user.id} token expired due to inactivity (${inactivityMinutes.toFixed(2)} minutes)`);
56
+ throw new common_1.UnauthorizedException('Your session has expired due to inactivity. Please login again.');
57
+ }
58
+ // 🔥 4. UPDATE LAST ACTIVITY (avoid DB overload)
59
+ if (inactivityMinutes > 2) {
60
+ await this.authService.updateLastActivity(lastLogin.id);
61
+ }
35
62
  console.log(`✅ JWT validated for user ${user.id}`);
36
63
  return {
37
64
  id: user.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecrs-auth-core",
3
- "version": "1.0.99",
3
+ "version": "1.0.100",
4
4
  "description": "Centralized authentication and authorization module for ECRS apps",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",