// 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 { console.log(`[JWT Auth] Calling auth service to validate token...`); const response = await axios.post( `${AUTH_SERVICE_URL}/auth/validate-token`, { token }, { timeout: AUTH_SERVICE_TIMEOUT, headers: { 'Content-Type': 'application/json', }, } ); console.log(`[JWT Auth] Auth service responded with status: ${response.status}`); if (response.data.valid === true) { console.log(`[JWT Auth] Auth service confirmed token is valid`); return { valid: true, payload: response.data.payload, }; } else { console.log(`[JWT Auth] Auth service reported token as invalid:`, response.data.error); 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(`[JWT Auth] ❌ 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(`[JWT Auth] ❌ Auth service unavailable:`, err.message); return { valid: false, error: 'Authentication service unavailable' }; } else { // Error setting up request console.error(`[JWT Auth] ❌ 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) { console.log(`[JWT Auth] Starting authentication check for ${req.method} ${req.path}`); console.log(`[JWT Auth] Headers received:`, { authorization: req.headers.authorization ? 'Bearer ' : 'MISSING', 'content-type': req.headers['content-type'], 'user-agent': req.headers['user-agent']?.substring(0, 50), }); // Extract token from Authorization header const authHeader = req.headers.authorization || ''; const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null; if (!token) { console.log(`[JWT Auth] ❌ FAILED: No token found in Authorization header`); console.log(`[JWT Auth] Auth header value: "${authHeader}"`); return res.status(401).json({ error: 'Unauthorized', message: 'Missing Authorization header. Expected format: Authorization: Bearer ' }); } console.log(`[JWT Auth] Token found (length: ${token.length}), validating via auth service...`); console.log(`[JWT Auth] Auth service URL: ${AUTH_SERVICE_URL}/auth/validate-token`); // Validate token via auth service API const validationResult = await validateTokenViaAuthService(token); if (!validationResult.valid) { console.log(`[JWT Auth] ❌ FAILED: Token validation failed - ${validationResult.error}`); // 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; console.log(`[JWT Auth] ✅ Token validated successfully`); console.log(`[JWT Auth] Token payload:`, { userId: payload.sub, role: payload.role, userType: payload.user_type, phoneNumber: payload.phone_number ? '***' : null, tokenVersion: payload.token_version, }); // 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, }; console.log(`[JWT Auth] ✅ User authenticated:`, { userId: req.user.userId, role: req.user.role, userType: req.user.userType, }); // Log successful authentication if (req.auditLogger) { req.auditLogger.logSuccess('authenticate', { userId: req.user.userId }); } next(); } export default jwtAuthenticate;