// middleware/jwtAuthenticate.js /** * JWT Authentication Middleware * * Validates JWT tokens by calling the auth service API * Auth service handles all token validation (signature, expiry, issuer, audience, token_version) * Extracts and attaches user information to req.user * * Flow: * 1. Extract token from Authorization header * 2. Call auth service /auth/validate-token endpoint * 3. Auth service validates token and returns user info * 4. Attach user info to req.user and continue */ import axios from 'axios'; const AUTH_SERVICE_URL = process.env.AUTH_SERVICE_URL || 'http://localhost:3000'; const AUTH_SERVICE_TIMEOUT = parseInt(process.env.AUTH_SERVICE_TIMEOUT || '5000', 10); // 5 seconds default /** * Validate JWT token via auth service API */ async function validateTokenViaAuthService(token) { try { const response = await axios.post( `${AUTH_SERVICE_URL}/auth/validate-token`, { token }, { timeout: AUTH_SERVICE_TIMEOUT, headers: { 'Content-Type': 'application/json', }, } ); if (response.data.valid === true) { return { valid: true, payload: response.data.payload, }; } else { return { valid: false, error: response.data.error || 'Token validation failed' }; } } catch (err) { // Handle different error types if (err.response) { // Auth service returned an error response console.error('Auth service validation error:', err.response.status, err.response.data); return { valid: false, error: err.response.data?.error || 'Token validation failed' }; } else if (err.request) { // Request was made but no response received console.error('Auth service unavailable:', err.message); return { valid: false, error: 'Authentication service unavailable' }; } else { // Error setting up request console.error('Error calling auth service:', err.message); return { valid: false, error: 'Failed to validate token' }; } } } /** * JWT Authentication Middleware * * Calls auth service to validate token and authorize the request */ async function jwtAuthenticate(req, res, next) { // Extract token from Authorization header const authHeader = req.headers.authorization || ''; const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null; if (!token) { return res.status(401).json({ error: 'Unauthorized', message: 'Missing Authorization header. Expected format: Authorization: Bearer ' }); } // Validate token via auth service API const validationResult = await validateTokenViaAuthService(token); if (!validationResult.valid) { // Log failed authentication attempt if (req.auditLogger) { req.auditLogger.logFailure('authenticate', validationResult.error || 'Token validation failed'); } return res.status(401).json({ error: 'Unauthorized', message: 'Invalid or expired token', details: validationResult.error }); } const payload = validationResult.payload; // Extract user information from auth service response req.user = { userId: payload.sub, // Subject (user ID) role: payload.role || 'user', userType: payload.user_type || null, phoneNumber: payload.phone_number || null, tokenVersion: payload.token_version || 1, highAssurance: payload.high_assurance || false, // Add tenantId if present in payload (for multi-tenant apps) tenantId: payload.tenant_id || null, }; // Log successful authentication if (req.auditLogger) { req.auditLogger.logSuccess('authenticate', { userId: req.user.userId }); } next(); } export default jwtAuthenticate;