import { Request, Response, NextFunction } from 'express'; import { JWTService } from './JWTService'; import { RedisService } from './RedisService'; import { logAuth, logWarning } from './Logger'; export const jwtService = new JWTService(); const redisService = RedisService.getInstance(); /** * Check if a token is blacklisted */ async function isTokenBlacklisted(token: string): Promise { try { const result = await redisService.get(`blacklist:${token}`); return result === 'true'; } catch (error) { // If Redis is down, allow the request to proceed (fail open) logWarning('Failed to check token blacklist - allowing request', { error: (error as Error).message }); return false; } } /** * Extract token from request (cookie or Authorization header) */ function extractToken(req: Request, type: 'auth' | 'refresh'): string | null { // First try to get token from cookie const cookieToken = req.cookies[`${type}_token`]; if (cookieToken) { return cookieToken; } // Fallback to Authorization header const authHeader = req.headers.authorization; if (authHeader && authHeader.startsWith('Bearer ')) { return authHeader.substring(7); } return null; } export async function authRequired(req: Request, res: Response, next: NextFunction) { try { // Extract token from request const token = extractToken(req, "auth"); const refreshToken = extractToken(req, "refresh"); if (!token || !refreshToken) { logAuth('Authentication failed - No token provided', undefined, { ip: req.ip, userAgent: req.get ? req.get('User-Agent') : 'unknown', path: req.path }, req); return res.status(401).json({ error: 'Unauthorized' }); } // Check if token is blacklisted const isBlacklisted = await isTokenBlacklisted(token); if (isBlacklisted) { logAuth('Authentication failed - Token blacklisted', undefined, { ip: req.ip, userAgent: req.get ? req.get('User-Agent') : 'unknown', path: req.path }, req); return res.status(401).json({ error: 'Token has been invalidated' }); } // Verify token const payload = jwtService.verify(req); if (!payload) { logAuth('Authentication failed - Invalid token', undefined, { ip: req.ip, userAgent: req.get ? req.get('User-Agent') : 'unknown', path: req.path }, req); return res.status(401).json({ error: 'Unauthorized' }); } logAuth('Authentication successful', payload.userId, { authLevel: payload.authLevel, orgId: payload.orgId }, req); const refreshed = jwtService.refreshIfNeeded(payload, res, req); if (refreshed) { logAuth('Token refreshed', payload.userId, undefined, req); } (req as any).user = payload; next(); } catch (error) { logWarning('Authentication middleware error', { error: (error as Error).message }, req); return res.status(500).json({ error: 'Internal server error' }); } } export async function adminRequired(req: Request, res: Response, next: NextFunction) { try { // Extract token from request const token = extractToken(req, "auth"); const refreshToken = extractToken(req, "refresh"); if (!token || !refreshToken) { logWarning('Admin access denied - No token provided', { ip: req.ip, path: req.path }, req); return res.status(401).json({ error: 'Unauthorized' }); } // Check if token is blacklisted const isBlacklisted = await isTokenBlacklisted(token); if (isBlacklisted) { logWarning('Admin access denied - Token blacklisted', { ip: req.ip, path: req.path }, req); return res.status(401).json({ error: 'Token has been invalidated' }); } // Verify token and check admin privileges const payload = jwtService.verify(req); if (!payload || payload.authLevel !== 1) { logWarning('Admin access denied', { hasPayload: !!payload, authLevel: payload?.authLevel, userId: payload?.userId, ip: req.ip, path: req.path }, req); return res.status(403).json({ error: 'Forbidden' }); } logAuth('Admin authentication successful', payload.userId, { authLevel: payload.authLevel, orgId: payload.orgId }, req); const refreshed = jwtService.refreshIfNeeded(payload, res, req); if (refreshed) { logAuth('Admin token refreshed', payload.userId, undefined, req); } (req as any).user = payload; next(); } catch (error) { logWarning('Admin authentication middleware error', { error: (error as Error).message }, req); return res.status(500).json({ error: 'Internal server error' }); } }