ecrs-auth-core 1.0.99 → 1.0.101
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.
- package/dist/auth.controller.d.ts +1 -0
- package/dist/auth.controller.js +6 -8
- package/dist/auth.service.d.ts +4 -1
- package/dist/auth.service.js +45 -8
- package/dist/entities/user.entity.d.ts +1 -0
- package/dist/entities/user.entity.js +4 -0
- package/dist/jwt/jwt.strategy.d.ts +1 -1
- package/dist/jwt/jwt.strategy.js +34 -7
- package/package.json +1 -1
package/dist/auth.controller.js
CHANGED
|
@@ -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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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)) {
|
package/dist/auth.service.d.ts
CHANGED
|
@@ -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
|
|
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
|
}
|
package/dist/auth.service.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
86
|
-
return null;
|
|
85
|
+
throw new common_1.UnauthorizedException('Login not allowed from this IP address');
|
|
87
86
|
}
|
|
88
87
|
}
|
|
89
88
|
return user;
|
|
@@ -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({
|
|
@@ -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)
|
package/dist/jwt/jwt.strategy.js
CHANGED
|
@@ -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
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
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,
|