148 lines
5.2 KiB
TypeScript
148 lines
5.2 KiB
TypeScript
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<boolean> {
|
|
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' });
|
|
}
|
|
} |