Files
fleetforge/apps/api/src/middleware/auth.ts
T

81 lines
2.0 KiB
TypeScript

import type { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
import type { AuthenticatedUser, Role } from "@fleetforge/shared";
const JWT_SECRET = process.env.JWT_SECRET || "dev-secret";
const ROLE_HIERARCHY: Record<Role, number> = {
owner: 4,
admin: 3,
operator: 2,
read_only: 1,
};
export function generateToken(user: AuthenticatedUser): string {
return jwt.sign(
{
userId: user.userId,
organizationId: user.organizationId,
role: user.role,
email: user.email,
},
JWT_SECRET,
{ expiresIn: (process.env.JWT_EXPIRES_IN || "24h") as any }
);
}
export function verifyToken(token: string): AuthenticatedUser {
return jwt.verify(token, JWT_SECRET) as AuthenticatedUser;
}
export function authenticate(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
res.status(401).json({
success: false,
error: { code: "UNAUTHORIZED", message: "Missing or invalid token" },
});
return;
}
try {
const token = authHeader.substring(7);
const user = verifyToken(token);
(req as any).user = user;
next();
} catch {
res.status(401).json({
success: false,
error: { code: "UNAUTHORIZED", message: "Invalid or expired token" },
});
}
}
export function authorize(...allowedRoles: Role[]) {
return (req: Request, res: Response, next: NextFunction) => {
const user = (req as any).user as AuthenticatedUser;
if (!user) {
res.status(401).json({
success: false,
error: { code: "UNAUTHORIZED", message: "Not authenticated" },
});
return;
}
if (allowedRoles.length > 0) {
const hasRole = allowedRoles.some(
(role) => ROLE_HIERARCHY[user.role] >= ROLE_HIERARCHY[role]
);
if (!hasRole) {
res.status(403).json({
success: false,
error: { code: "FORBIDDEN", message: "Insufficient permissions" },
});
return;
}
}
next();
};
}