Files
dashboard/backend/src/middleware/auth.middleware.ts
Matthias Hochmeister f09748f4a1 inital
2026-02-23 17:08:58 +01:00

156 lines
3.6 KiB
TypeScript

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<void> => {
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 <token>',
});
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<void> => {
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();
}
};