File: /var/www/indoadvisory_new/web2/webapp/middleware/auth.js
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { db } = require('../config/database');
// Enterprise Authentication Middleware
// ===================================
/**
* Require user to be authenticated
*/
const requireAuth = async (req, res, next) => {
try {
// Check session-based authentication
if (!req.session.user) {
req.flash('error', 'Please log in to access this page');
return res.redirect('/auth/login');
}
// Verify user still exists and is active
const result = await db.query(
'SELECT id, email, name, role, is_active FROM users WHERE id = $1 AND is_active = true',
[req.session.user.id]
);
if (result.rows.length === 0) {
req.session.destroy();
req.flash('error', 'Your account is no longer active');
return res.redirect('/auth/login');
}
// Update user info in session
req.session.user = result.rows[0];
req.user = result.rows[0];
next();
} catch (error) {
console.error('Auth middleware error:', error);
res.status(500).render('error', {
title: 'Authentication Error',
message: 'An error occurred during authentication',
error: {}
});
}
};
/**
* Require user to have admin role
*/
const requireAdmin = async (req, res, next) => {
try {
if (!req.user || req.user.role !== 'admin') {
req.flash('error', 'Admin access required');
return res.redirect('/');
}
next();
} catch (error) {
console.error('Admin middleware error:', error);
res.status(403).render('error', {
title: 'Access Denied',
message: 'Admin access required',
error: { status: 403 }
});
}
};
/**
* Optional authentication (for pages that show different content for logged in users)
*/
const optionalAuth = async (req, res, next) => {
try {
if (req.session.user) {
const result = await db.query(
'SELECT id, email, name, role, is_active FROM users WHERE id = $1 AND is_active = true',
[req.session.user.id]
);
if (result.rows.length > 0) {
req.user = result.rows[0];
} else {
req.session.destroy();
}
}
next();
} catch (error) {
console.error('Optional auth middleware error:', error);
next(); // Continue even if there's an error
}
};
/**
* Redirect if already authenticated
*/
const redirectIfAuth = (req, res, next) => {
if (req.session.user) {
return res.redirect('/admin');
}
next();
};
/**
* Validate login credentials with enterprise security features
*/
const validateLogin = async (email, password, req) => {
try {
// Get user with login attempt tracking
const result = await db.query(
`SELECT id, email, password_hash, name, role, is_active,
failed_login_attempts, locked_until
FROM users
WHERE email = $1`,
[email.toLowerCase()]
);
if (result.rows.length === 0) {
// Log failed attempt for audit
await logAuditEvent(null, 'login_failed', 'user', null, req, { reason: 'user_not_found', email });
return { success: false, message: 'Invalid email or password' };
}
const user = result.rows[0];
// Check if user is active
if (!user.is_active) {
await logAuditEvent(user.id, 'login_failed', 'user', user.id, req, { reason: 'account_disabled' });
return { success: false, message: 'Your account has been disabled' };
}
// Check if account is locked
if (user.locked_until && new Date(user.locked_until) > new Date()) {
const lockTime = new Date(user.locked_until).toLocaleString();
await logAuditEvent(user.id, 'login_failed', 'user', user.id, req, { reason: 'account_locked', locked_until: lockTime });
return { success: false, message: `Account is locked until ${lockTime}` };
}
// Verify password
const isValidPassword = await bcrypt.compare(password, user.password_hash);
if (!isValidPassword) {
// Increment failed attempts
const newFailedAttempts = user.failed_login_attempts + 1;
let lockUntil = null;
// Lock account after 5 failed attempts for 30 minutes
if (newFailedAttempts >= 5) {
lockUntil = new Date(Date.now() + 30 * 60 * 1000); // 30 minutes
}
await db.query(
'UPDATE users SET failed_login_attempts = $1, locked_until = $2 WHERE id = $3',
[newFailedAttempts, lockUntil, user.id]
);
await logAuditEvent(user.id, 'login_failed', 'user', user.id, req, {
reason: 'invalid_password',
failed_attempts: newFailedAttempts,
account_locked: !!lockUntil
});
if (lockUntil) {
return { success: false, message: 'Too many failed attempts. Account locked for 30 minutes.' };
}
return { success: false, message: 'Invalid email or password' };
}
// Successful login - reset failed attempts and update last login
await db.query(
`UPDATE users
SET failed_login_attempts = 0, locked_until = NULL, last_login = CURRENT_TIMESTAMP
WHERE id = $1`,
[user.id]
);
await logAuditEvent(user.id, 'login_success', 'user', user.id, req);
return {
success: true,
user: {
id: user.id,
email: user.email,
name: user.name,
role: user.role
}
};
} catch (error) {
console.error('Login validation error:', error);
return { success: false, message: 'An error occurred during login' };
}
};
/**
* Generate secure password hash
*/
const hashPassword = async (password) => {
const rounds = parseInt(process.env.BCRYPT_ROUNDS) || 12;
return await bcrypt.hash(password, rounds);
};
/**
* Generate JWT token for API access
*/
const generateJWT = (user) => {
const payload = {
id: user.id,
email: user.email,
role: user.role
};
return jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '24h',
issuer: 'indo-advisory',
audience: 'api'
});
};
/**
* Verify JWT token
*/
const verifyJWT = (token) => {
try {
return jwt.verify(token, process.env.JWT_SECRET);
} catch (error) {
return null;
}
};
/**
* Log audit events for enterprise security
*/
const logAuditEvent = async (userId, action, resourceType, resourceId, req, details = {}) => {
try {
const clientIP = req.ip || req.connection.remoteAddress || req.socket.remoteAddress;
const userAgent = req.get('User-Agent');
await db.query(
`INSERT INTO audit_logs (user_id, action, resource_type, resource_id, ip_address, user_agent, details)
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
[userId, action, resourceType, resourceId, clientIP, userAgent, JSON.stringify(details)]
);
} catch (error) {
console.error('Audit logging error:', error);
}
};
/**
* Middleware to require API authentication
*/
const requireAPIAuth = async (req, res, next) => {
try {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
const payload = verifyJWT(token);
if (!payload) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
// Verify user still exists
const result = await db.query(
'SELECT id, email, name, role, is_active FROM users WHERE id = $1 AND is_active = true',
[payload.id]
);
if (result.rows.length === 0) {
return res.status(401).json({ error: 'User not found or inactive' });
}
req.user = result.rows[0];
next();
} catch (error) {
console.error('API auth middleware error:', error);
res.status(500).json({ error: 'Authentication error' });
}
};
module.exports = {
requireAuth,
requireAdmin,
optionalAuth,
redirectIfAuth,
validateLogin,
hashPassword,
generateJWT,
verifyJWT,
logAuditEvent,
requireAPIAuth
};