/** * Auth Middleware - JWT verification for Express/NestJS * * @module @erp-suite/core/middleware */ import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; /** * JWT payload structure */ export interface JwtPayload { userId: string; tenantId: string; email: string; roles: string[]; iat?: number; exp?: number; } /** * Extended Express Request with auth context */ export interface AuthRequest extends Request { user?: JwtPayload; } /** * Auth middleware configuration */ export interface AuthMiddlewareConfig { jwtSecret: string; skipPaths?: string[]; } /** * Creates an auth middleware that verifies JWT tokens * * @param config - Middleware configuration * @returns Express middleware function * * @example * ```typescript * import { createAuthMiddleware } from '@erp-suite/core/middleware'; * * const authMiddleware = createAuthMiddleware({ * jwtSecret: process.env.JWT_SECRET, * skipPaths: ['/health', '/login'], * }); * * app.use(authMiddleware); * ``` */ export function createAuthMiddleware(config: AuthMiddlewareConfig) { return (req: AuthRequest, res: Response, next: NextFunction): void => { // Skip authentication for certain paths if (config.skipPaths?.some((path) => req.path.startsWith(path))) { return next(); } const authHeader = req.headers.authorization; if (!authHeader) { res.status(401).json({ error: 'No authorization header' }); return; } const parts = authHeader.split(' '); if (parts.length !== 2 || parts[0] !== 'Bearer') { res.status(401).json({ error: 'Invalid authorization header format' }); return; } const token = parts[1]; try { const payload = jwt.verify(token, config.jwtSecret) as JwtPayload; req.user = payload; next(); } catch (error) { if (error instanceof jwt.TokenExpiredError) { res.status(401).json({ error: 'Token expired' }); return; } res.status(401).json({ error: 'Invalid token' }); } }; } /** * NestJS Guard for JWT authentication * * @example * ```typescript * import { AuthGuard } from '@erp-suite/core/middleware'; * * @Controller('api') * @UseGuards(AuthGuard) * export class ApiController { * // Protected routes * } * ``` */ export class AuthGuard { constructor(private readonly jwtSecret: string) {} canActivate(context: any): boolean { const request = context.switchToHttp().getRequest(); const authHeader = request.headers.authorization; if (!authHeader) { return false; } const parts = authHeader.split(' '); if (parts.length !== 2 || parts[0] !== 'Bearer') { return false; } const token = parts[1]; try { const payload = jwt.verify(token, this.jwtSecret) as JwtPayload; request.user = payload; return true; } catch { return false; } } }