Files
sena_db_api_layer/middleware/auth.js
silverpro89 53d97ba5db update
2026-01-20 20:29:07 +07:00

211 lines
4.9 KiB
JavaScript

const jwt = require('jsonwebtoken');
const { UsersAuth, UserProfile } = require('../models');
// JWT Secret - nên lưu trong environment variable
const JWT_SECRET = process.env.JWT_SECRET || 'sena-secret-key-2026';
/**
* Middleware xác thực token
*/
const authenticateToken = async (req, res, next) => {
try {
// Lấy token từ header
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.replace('Bearer ', '');
if (!token) {
return res.status(401).json({
success: false,
message: 'Token không được cung cấp',
});
}
// Verify token
const decoded = jwt.verify(token, JWT_SECRET);
// Kiểm tra user còn tồn tại và session hợp lệ
const user = await UsersAuth.findByPk(decoded.userId, {
include: [{
model: UserProfile,
as: 'profile',
}],
});
if (!user || !user.is_active) {
return res.status(401).json({
success: false,
message: 'Token không hợp lệ hoặc tài khoản đã bị vô hiệu hóa',
});
}
// Kiểm tra session
if (user.current_session_id !== decoded.sessionId) {
return res.status(401).json({
success: false,
message: 'Phiên đăng nhập đã hết hạn',
});
}
// Gắn thông tin user vào request
req.user = {
userId: user.id,
username: user.username,
email: user.email,
role: decoded.role,
sessionId: decoded.sessionId,
};
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
success: false,
message: 'Token đã hết hạn',
});
}
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({
success: false,
message: 'Token không hợp lệ',
});
}
console.error('Auth middleware error:', error);
return res.status(500).json({
success: false,
message: 'Lỗi xác thực',
error: error.message,
});
}
};
/**
* Middleware kiểm tra role
*/
const requireRole = (allowedRoles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
success: false,
message: 'Chưa xác thực',
});
}
const userRole = req.user.role;
if (!allowedRoles.includes(userRole)) {
return res.status(403).json({
success: false,
message: 'Không có quyền truy cập',
required_role: allowedRoles,
your_role: userRole,
});
}
next();
};
};
/**
* Middleware kiểm tra permission cụ thể
*/
const requirePermission = (permission) => {
return async (req, res, next) => {
if (!req.user) {
return res.status(401).json({
success: false,
message: 'Chưa xác thực',
});
}
try {
const user = await UsersAuth.findByPk(req.user.userId, {
include: [{
model: Role,
as: 'role',
include: [{
model: Permission,
as: 'permissions',
}],
}],
});
if (!user) {
return res.status(401).json({
success: false,
message: 'User không tồn tại',
});
}
// Kiểm tra user có permission này không
const hasPermission = user.role?.permissions?.some(
p => p.permission_code === permission
);
if (!hasPermission) {
return res.status(403).json({
success: false,
message: 'Không có quyền thực hiện hành động này',
required_permission: permission,
});
}
next();
} catch (error) {
console.error('Permission check error:', error);
return res.status(500).json({
success: false,
message: 'Lỗi kiểm tra quyền',
error: error.message,
});
}
};
};
/**
* Middleware xác thực optional - không bắt buộc phải có token
*/
const optionalAuth = async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.replace('Bearer ', '');
if (!token) {
// Không có token, tiếp tục nhưng không có req.user
req.user = null;
return next();
}
// Có token, thử verify
const decoded = jwt.verify(token, JWT_SECRET);
const user = await UsersAuth.findByPk(decoded.userId);
if (user && user.is_active && user.current_session_id === decoded.sessionId) {
req.user = {
userId: user.id,
username: user.username,
email: user.email,
role: decoded.role,
sessionId: decoded.sessionId,
};
} else {
req.user = null;
}
next();
} catch (error) {
// Token không hợp lệ, tiếp tục nhưng không có req.user
req.user = null;
next();
}
};
module.exports = {
authenticateToken,
requireRole,
requirePermission,
optionalAuth,
};