Spaces:
Running
Running
| const jwt = require('jsonwebtoken'); | |
| const AdminUser = require('../models/AdminUser'); | |
| const { env } = require('../configs/env'); | |
| const JWT_SECRET = env.jwtSecret; | |
| const readTokenFromRequest = (req) => { | |
| const header = req.headers?.authorization || req.headers?.Authorization; | |
| if (typeof header === 'string') { | |
| const [scheme, value] = header.split(' '); | |
| if (scheme && scheme.toLowerCase() === 'bearer' && value) { | |
| return value.trim(); | |
| } | |
| } | |
| return req.cookies?.token || ''; | |
| }; | |
| const protect = async (req, res, next) => { | |
| if (!JWT_SECRET) { | |
| return res.status(503).json({ message: "Authentication is not configured" }); | |
| } | |
| const token = readTokenFromRequest(req); | |
| if (!token) { | |
| return res.status(401).json({ message: "Not authorized, no token" }); | |
| } | |
| try { | |
| const decoded = jwt.verify(token, JWT_SECRET); | |
| const adminId = decoded?.id || decoded?.sub; | |
| if (!adminId) { | |
| return res.status(401).json({ message: "Not authorized, invalid token" }); | |
| } | |
| const admin = await AdminUser.findById(adminId).select('-passwordHash +sessionToken').lean(); | |
| if (!admin) { | |
| return res.status(401).json({ message: "User not found" }); | |
| } | |
| if (admin.isActive === false) { | |
| return res.status(403).json({ message: "Account is inactive" }); | |
| } | |
| if (decoded.st && admin.sessionToken && decoded.st !== admin.sessionToken) { | |
| return res.status(401).json({ message: "Session invalidated", code: "SESSION_CONFLICT" }); | |
| } | |
| req.user = { | |
| ...admin, | |
| _id: admin._id, | |
| id: String(admin._id), | |
| role: admin.role || "admin", | |
| }; | |
| // Belt-and-suspenders: projection already excludes this field; explicit scrub guards against future query changes. | |
| delete req.user.passwordHash; | |
| return next(); | |
| } catch (error) { | |
| return res.status(401).json({ message: "Not authorized, invalid token" }); | |
| } | |
| }; | |
| const adminOnly = (req, res, next) => { | |
| if (req.user && req.user.role === "admin") { | |
| next(); | |
| } else { | |
| res.status(403).json({ message: "Access denied: Admins only" }); | |
| } | |
| }; | |
| const optionalProtect = async (req, res, next) => { | |
| if (!JWT_SECRET) return next(); | |
| const token = readTokenFromRequest(req); | |
| if (!token) return next(); | |
| try { | |
| const decoded = jwt.verify(token, JWT_SECRET); | |
| const adminId = decoded?.id || decoded?.sub; | |
| if (adminId) { | |
| const admin = await AdminUser.findById(adminId).select('-passwordHash').lean(); | |
| if (admin && admin.isActive !== false) { | |
| req.user = { | |
| ...admin, | |
| _id: admin._id, | |
| id: String(admin._id), | |
| role: admin.role || 'admin', | |
| }; | |
| } | |
| } | |
| } catch { | |
| // Invalid token — proceed as unauthenticated | |
| } | |
| next(); | |
| }; | |
| module.exports = { protect, adminOnly, optionalProtect }; | |