/** * Auth Middleware for MCP Products * Verifies JWT tokens and sets user context */ import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; import { logger } from '../utils/logger'; // JWT configuration (should match mcp-auth config) const JWT_SECRET = process.env.JWT_SECRET || 'dev-jwt-secret-change-in-production-min-256-bits'; // JWT Payload interface export interface JWTPayload { sub: string; // user_id email: string; tenantId: string; isOwner: boolean; iat: number; exp: number; } // Extend Express Request to include auth info declare global { namespace Express { interface Request { userId?: string; tenantId?: string; userEmail?: string; isOwner?: boolean; isAuthenticated?: boolean; } } } /** * Auth middleware that verifies JWT tokens * If valid, sets userId, tenantId, and userEmail on request */ export function authMiddleware(req: Request, res: Response, next: NextFunction): void { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { res.status(401).json({ error: 'Unauthorized', code: 'MISSING_TOKEN', message: 'No authentication token provided', }); return; } const token = authHeader.substring(7); try { const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload; // Set auth info on request req.userId = decoded.sub; req.tenantId = decoded.tenantId; req.userEmail = decoded.email; req.isOwner = decoded.isOwner; req.isAuthenticated = true; // Also set tenant ID header for RLS queries req.headers['x-tenant-id'] = decoded.tenantId; req.headers['x-user-id'] = decoded.sub; next(); } catch (error) { if (error instanceof jwt.TokenExpiredError) { res.status(401).json({ error: 'Unauthorized', code: 'TOKEN_EXPIRED', message: 'Token has expired', }); return; } if (error instanceof jwt.JsonWebTokenError) { res.status(401).json({ error: 'Unauthorized', code: 'INVALID_TOKEN', message: 'Invalid token', }); return; } logger.error('Auth middleware error', { error }); res.status(500).json({ error: 'Internal server error', code: 'AUTH_ERROR', }); } } /** * Optional auth middleware * Sets auth info if token is valid, but allows unauthenticated requests */ export function optionalAuthMiddleware(req: Request, _res: Response, next: NextFunction): void { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { req.isAuthenticated = false; next(); return; } const token = authHeader.substring(7); try { const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload; req.userId = decoded.sub; req.tenantId = decoded.tenantId; req.userEmail = decoded.email; req.isOwner = decoded.isOwner; req.isAuthenticated = true; req.headers['x-tenant-id'] = decoded.tenantId; req.headers['x-user-id'] = decoded.sub; } catch { req.isAuthenticated = false; } next(); }