90 lines
2.3 KiB
TypeScript
90 lines
2.3 KiB
TypeScript
import { NextApiRequest, NextApiResponse } from 'next';
|
|
import { verifyToken } from '../auth';
|
|
import { UserService } from '../../backend/services/userService';
|
|
import { UserDTO } from '../../types';
|
|
|
|
// Extend NextApiRequest to include user property
|
|
export interface AuthenticatedRequest extends NextApiRequest {
|
|
user?: UserDTO;
|
|
}
|
|
|
|
/**
|
|
* Middleware to require authentication
|
|
* Returns 401 if no valid token is found
|
|
*/
|
|
export async function requireAuth(
|
|
req: AuthenticatedRequest,
|
|
res: NextApiResponse
|
|
): Promise<boolean> {
|
|
const token = getTokenFromRequest(req);
|
|
|
|
if (!token) {
|
|
res.status(401).json({ success: false, error: 'Authentication required' });
|
|
return false;
|
|
}
|
|
|
|
const decoded = verifyToken(token);
|
|
if (!decoded) {
|
|
res.status(401).json({ success: false, error: 'Invalid or expired token' });
|
|
return false;
|
|
}
|
|
|
|
// Load user from database
|
|
const user = await UserService.getUserById(decoded.userId);
|
|
if (!user) {
|
|
res.status(401).json({ success: false, error: 'User not found' });
|
|
return false;
|
|
}
|
|
|
|
// Check if user is banned
|
|
if (user.status === 'BANNED') {
|
|
res.status(403).json({ success: false, error: 'Account has been banned' });
|
|
return false;
|
|
}
|
|
|
|
// Attach user to request
|
|
req.user = user;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Middleware for optional authentication
|
|
* Attaches user to request if valid token exists, but doesn't fail if not
|
|
*/
|
|
export async function optionalAuth(req: AuthenticatedRequest): Promise<void> {
|
|
const token = getTokenFromRequest(req);
|
|
|
|
if (!token) {
|
|
return;
|
|
}
|
|
|
|
const decoded = verifyToken(token);
|
|
if (!decoded) {
|
|
return;
|
|
}
|
|
|
|
const user = await UserService.getUserById(decoded.userId);
|
|
if (user && user.status !== 'BANNED') {
|
|
req.user = user;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract JWT token from request
|
|
* Checks both cookies and Authorization header
|
|
*/
|
|
function getTokenFromRequest(req: NextApiRequest): string | null {
|
|
// Check cookie first
|
|
if (req.cookies.token) {
|
|
return req.cookies.token;
|
|
}
|
|
|
|
// Check Authorization header
|
|
const authHeader = req.headers.authorization;
|
|
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
return authHeader.substring(7);
|
|
}
|
|
|
|
return null;
|
|
}
|