2 * @file JWT Authentication Middleware
3 * @module middleware/auth
5 * Express middleware for JWT token authentication. Validates Bearer tokens using
6 * JWT_SECRET (dashboard users) or AGENT_SIGN_KEY (agent enrollment). Extracts user
7 * information from token payload and attaches to req.user.
9 * Token verification precedence:
10 * 1. JWT_SECRET - Standard dashboard user authentication
11 * 2. AGENT_SIGN_KEY - Fallback for agent enrollment tokens
14 * - /api/agent/enroll - Bypasses JWT_SECRET verification, only validates AGENT_SIGN_KEY
15 * - All other routes - Requires valid JWT from either key
17 * Token payload expects:
18 * - tenant_id: Tenant ID for multi-tenancy
19 * - Standard JWT claims (sub, iat, exp, etc.)
20 * @requires jsonwebtoken
21 * @see {@link https://jwt.io/} JWT specification
24const jwt = require('jsonwebtoken');
27 * Authenticates JWT token from Authorization header.
29 * Validates Bearer token using JWT_SECRET or AGENT_SIGN_KEY. Extracts user information
30 * and attaches to req.user. Bypasses JWT_SECRET verification for agent enrollment endpoint.
31 * @function authenticateToken
32 * @param {object} req - Express request object
33 * @param {object} req.headers - HTTP headers
34 * @param {string} req.headers.authorization - Bearer token (format: "Bearer <token>")
35 * @param {string} req.originalUrl - Original request URL (for enrollment bypass check)
36 * @param {object} res - Express response object
37 * @param {Function} next - Express next middleware function
38 * @returns {void} Calls next() on success, sends 401/403 on failure
39 * @throws {401} Access denied - Missing authorization header or token
40 * @throws {403} Invalid token - JWT verification failed for both keys
42 * // Apply to protected routes
43 * const authenticateToken = require('./middleware/auth');
44 * router.get('/protected', authenticateToken, (req, res) => {
45 * const userId = req.user.sub;
46 * const tenantId = req.user.tenantId;
47 * // Handle request...
50 * // Request with valid token
51 * curl https://api.example.com/api/users \
52 * -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
54function authenticateToken(req, res, next) {
55 // Allow agent enrollment route to bypass JWT_SECRET verification
56 if (req.originalUrl === '/api/agent/enroll') {
61 // Default: JWT authentication for all other routes
62 const authHeader = req.headers['authorization'];
63 const token = authHeader && authHeader.split(' ')[1]; // Bearer <token>
65 if (!token) return res.status(401).send('Access denied');
67 // Try JWT_SECRET first, then AGENT_SIGN_KEY if verification fails
68 jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
71 req.user.tenantId = user.tenant_id || null;
75 jwt.verify(token, process.env.AGENT_SIGN_KEY, (err2, user2) => {
78 req.user.tenantId = user2.tenant_id || null;
81 return res.status(403).send('Invalid token');
86module.exports = authenticateToken;