59 lines
1.8 KiB
JavaScript
59 lines
1.8 KiB
JavaScript
/**
|
|
* authMiddleware.js — JWT sign/verify helpers + Express middleware for Curio HMS.
|
|
*/
|
|
|
|
const jwt = require('jsonwebtoken');
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'curio_dev_secret_change_in_production';
|
|
const JWT_EXPIRY = '7d';
|
|
|
|
/**
|
|
* Sign a JWT with the given payload.
|
|
* @param {{ userId, email, role, tenantId }} payload
|
|
* @returns {string} signed JWT
|
|
*/
|
|
function signToken(payload) {
|
|
return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRY });
|
|
}
|
|
|
|
/**
|
|
* Express middleware: verifies JWT and enforces role access.
|
|
* @param {...string} roles - allowed roles; empty = any authenticated user
|
|
*/
|
|
function requireAuth(...roles) {
|
|
return (req, res, next) => {
|
|
const header = req.headers['authorization'] || req.headers['x-auth-token'];
|
|
if (!header) return res.status(401).json({ error: 'Authentication required' });
|
|
|
|
const token = header.startsWith('Bearer ') ? header.slice(7) : header;
|
|
try {
|
|
const decoded = jwt.verify(token, JWT_SECRET);
|
|
req.user = decoded;
|
|
|
|
if (roles.length > 0 && !roles.includes(decoded.role)) {
|
|
return res.status(403).json({ error: 'Insufficient permissions' });
|
|
}
|
|
next();
|
|
} catch (err) {
|
|
return res.status(401).json({ error: 'Invalid or expired token' });
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Middleware: verifies JWT but does NOT reject unauthenticated requests.
|
|
* Attaches req.user if valid token present.
|
|
*/
|
|
function optionalAuth(req, res, next) {
|
|
const header = req.headers['authorization'] || req.headers['x-auth-token'];
|
|
if (header) {
|
|
const token = header.startsWith('Bearer ') ? header.slice(7) : header;
|
|
try {
|
|
req.user = jwt.verify(token, JWT_SECRET);
|
|
} catch (_) { /* ignore */ }
|
|
}
|
|
next();
|
|
}
|
|
|
|
module.exports = { signToken, requireAuth, optionalAuth };
|