import { Request, Response, NextFunction } from 'express'; import tokenService from '../services/token.service'; import userService from '../services/user.service'; import logger from '../utils/logger'; import { JwtPayload } from '../types/auth.types'; // Extend Express Request type to include user declare global { namespace Express { interface Request { user?: { id: string; // UUID email: string; authentikSub: string; }; } } } /** * Authentication middleware * Validates JWT token and attaches user info to request */ export const authenticate = async ( req: Request, res: Response, next: NextFunction ): Promise => { try { // Extract token from Authorization header const authHeader = req.headers.authorization; if (!authHeader) { res.status(401).json({ success: false, message: 'No authorization token provided', }); return; } // Check for Bearer token format const parts = authHeader.split(' '); if (parts.length !== 2 || parts[0] !== 'Bearer') { res.status(401).json({ success: false, message: 'Invalid authorization header format. Use: Bearer ', }); return; } const token = parts[1]; // Verify token let decoded: JwtPayload; try { decoded = tokenService.verifyToken(token); } catch (error) { const message = error instanceof Error ? error.message : 'Invalid token'; res.status(401).json({ success: false, message, }); return; } // Check if user exists and is active const user = await userService.findById(decoded.userId); if (!user) { logger.warn('Token valid but user not found', { userId: decoded.userId }); res.status(401).json({ success: false, message: 'User not found', }); return; } if (!user.is_active) { logger.warn('User account is inactive', { userId: decoded.userId }); res.status(403).json({ success: false, message: 'User account is inactive', }); return; } // Attach user info to request req.user = { id: decoded.userId, email: decoded.email, authentikSub: decoded.authentikSub, }; logger.debug('User authenticated successfully', { userId: decoded.userId, email: decoded.email, }); next(); } catch (error) { logger.error('Authentication middleware error', { error }); res.status(500).json({ success: false, message: 'Internal server error during authentication', }); } }; /** * Optional authentication middleware * Attaches user if token is valid, but doesn't require it */ export const optionalAuth = async ( req: Request, _res: Response, next: NextFunction ): Promise => { try { const authHeader = req.headers.authorization; if (!authHeader) { next(); return; } const parts = authHeader.split(' '); if (parts.length !== 2 || parts[0] !== 'Bearer') { next(); return; } const token = parts[1]; try { const decoded = tokenService.verifyToken(token); const user = await userService.findById(decoded.userId); if (user && user.is_active) { req.user = { id: decoded.userId, email: decoded.email, authentikSub: decoded.authentikSub, }; } } catch (error) { // Invalid token - continue without user logger.debug('Optional auth: Invalid token', { error }); } next(); } catch (error) { logger.error('Optional authentication middleware error', { error }); next(); } };