Files
patchmon.net/backend/src/middleware/auth.js
Muhammad Ibrahim c4d0d8bee8 Fixed repo count issue
Refactored code to remove duplicate backend api endpoints for counting
Improved connection persistence issues
Improved database connection pooling issues
Fixed redis connection efficiency
Changed version to 1.3.0
Fixed GO binary detection based on package manager rather than OS
2025-10-19 17:53:10 +01:00

152 lines
4.0 KiB
JavaScript

const jwt = require("jsonwebtoken");
const { getPrismaClient } = require("../config/prisma");
const {
validate_session,
update_session_activity,
is_tfa_bypassed,
} = require("../utils/session_manager");
const prisma = getPrismaClient();
// Middleware to verify JWT token with session validation
const authenticateToken = async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
const token = authHeader?.split(" ")[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({ error: "Access token required" });
}
// Verify token
if (!process.env.JWT_SECRET) {
throw new Error("JWT_SECRET environment variable is required");
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Validate session and check inactivity timeout
const validation = await validate_session(decoded.sessionId, token);
if (!validation.valid) {
const error_messages = {
"Session not found": "Session not found",
"Session revoked": "Session has been revoked",
"Session expired": "Session has expired",
"Session inactive":
validation.message || "Session timed out due to inactivity",
"Token mismatch": "Invalid token",
"User inactive": "User account is inactive",
};
return res.status(401).json({
error: error_messages[validation.reason] || "Authentication failed",
reason: validation.reason,
});
}
// Update session activity timestamp
await update_session_activity(decoded.sessionId);
// Check if TFA is bypassed for this session
const tfa_bypassed = await is_tfa_bypassed(decoded.sessionId);
// Update last login (only on successful authentication)
await prisma.users.update({
where: { id: validation.user.id },
data: {
last_login: new Date(),
updated_at: new Date(),
},
});
req.user = validation.user;
req.session_id = decoded.sessionId;
req.tfa_bypassed = tfa_bypassed;
next();
} catch (error) {
if (error.name === "JsonWebTokenError") {
return res.status(401).json({ error: "Invalid token" });
}
if (error.name === "TokenExpiredError") {
return res.status(401).json({ error: "Token expired" });
}
console.error("Auth middleware error:", error);
return res.status(500).json({ error: "Authentication failed" });
}
};
// Middleware to check admin role
const requireAdmin = (req, res, next) => {
if (req.user.role !== "admin") {
return res.status(403).json({ error: "Admin access required" });
}
next();
};
// Middleware to check if user is authenticated (optional)
const optionalAuth = async (req, _res, next) => {
try {
const authHeader = req.headers.authorization;
const token = authHeader?.split(" ")[1];
if (token) {
if (!process.env.JWT_SECRET) {
throw new Error("JWT_SECRET environment variable is required");
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await prisma.users.findUnique({
where: { id: decoded.userId },
select: {
id: true,
username: true,
email: true,
role: true,
is_active: true,
last_login: true,
created_at: true,
updated_at: true,
},
});
if (user?.is_active) {
req.user = user;
}
}
next();
} catch {
// Continue without authentication for optional auth
next();
}
};
// Middleware to check if TFA is required for sensitive operations
const requireTfaIfEnabled = async (req, res, next) => {
try {
// Check if user has TFA enabled
const user = await prisma.users.findUnique({
where: { id: req.user.id },
select: { tfa_enabled: true },
});
// If TFA is enabled and not bypassed, require TFA verification
if (user?.tfa_enabled && !req.tfa_bypassed) {
return res.status(403).json({
error: "Two-factor authentication required for this operation",
requires_tfa: true,
});
}
next();
} catch (error) {
console.error("TFA requirement check error:", error);
return res.status(500).json({ error: "Authentication check failed" });
}
};
module.exports = {
authenticateToken,
requireAdmin,
optionalAuth,
requireTfaIfEnabled,
};