Skip to content

Commit 56bc6b4

Browse files
authored
auth
1 parent f3aa0fb commit 56bc6b4

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

middleware/auth.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// middleware/auth.js
2+
// Authentication & authorization middleware
3+
4+
const jwt = require('jsonwebtoken');
5+
const { promisify } = require('util');
6+
const config = require('../config/config');
7+
const { can } = require('../config/rbacConfig');
8+
9+
/**
10+
* Verify JWT, attach decoded payload to req.user
11+
*/
12+
exports.authenticate = async (req, res, next) => {
13+
try {
14+
const token =
15+
req.cookies.jid ||
16+
req.headers.authorization?.split(' ')[1];
17+
if (!token) {
18+
return res.status(401).json({ error: 'Authentication required' });
19+
}
20+
const decoded = await promisify(jwt.verify)(token, config.jwt.secret);
21+
req.user = decoded;
22+
next();
23+
} catch (err) {
24+
res.status(401).json({ error: 'Invalid or expired token' });
25+
}
26+
};
27+
28+
/**
29+
* Check that user has at least one of the allowed roles *and*
30+
* is permitted to perform this HTTP method on the current resource.
31+
*/
32+
exports.authorizeRoles = (...allowedRoles) => (req, res, next) => {
33+
if (!req.user) {
34+
return res.status(401).json({ error: 'Authentication required' });
35+
}
36+
37+
const method = req.method.toLowerCase(); // e.g., 'get', 'post'
38+
const resource = req.baseUrl.split('/').pop(); // e.g., 'users', 'auditLogs'
39+
40+
const permitted = req.user.roles.some(role =>
41+
allowedRoles.includes(role) && can(role, resource, method)
42+
);
43+
44+
if (!permitted) {
45+
return res.status(403).json({ error: 'Forbidden' });
46+
}
47+
next();
48+
};

0 commit comments

Comments
 (0)