erp-suite/apps/shared-libs/core/middleware/auth.middleware.ts

132 lines
3.0 KiB
TypeScript

/**
* 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;
}
}
}