import { Request, Response, NextFunction } from "express"; import jwt from "jsonwebtoken"; import { randomUUID } from "crypto"; import { eq } from "drizzle-orm"; import { db, usersTable } from "@workspace/db"; const JWT_SECRET = process.env.SESSION_SECRET || "raqim-secret-key-change-in-prod"; export interface AuthRequest extends Request { userId?: string; userRole?: string; userStatus?: string; } export async function requireAuth(req: AuthRequest, res: Response, next: NextFunction): Promise { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith("Bearer ")) { res.status(401).json({ error: "unauthorized", message: "No token provided" }); return; } const token = authHeader.slice(7); try { const payload = jwt.verify(token, JWT_SECRET) as { userId: string; role: string; status: string }; if (payload.status !== "active") { res.status(403).json({ error: "forbidden", message: "Account is not active" }); return; } // Verify the user ID from the token still exists in the DB. // This catches stale tokens issued before a DB wipe/re-seed. const row = await db .select({ id: usersTable.id }) .from(usersTable) .where(eq(usersTable.id, payload.userId)) .get(); if (!row) { console.error(`[auth] Token user ${payload.userId} not found in DB — rejecting as stale`); res.status(401).json({ error: "session_expired", message: "انتهت جلستك، يرجى تسجيل الدخول مجدداً" }); return; } req.userId = payload.userId; req.userRole = payload.role; req.userStatus = payload.status; next(); } catch { res.status(401).json({ error: "unauthorized", message: "Invalid or expired token" }); } } export async function requireAdmin(req: AuthRequest, res: Response, next: NextFunction): Promise { let calledNext = false; await requireAuth(req, res, () => { calledNext = true; }); if (!calledNext) return; if (req.userRole !== "admin") { res.status(403).json({ error: "forbidden", message: "Admin access required" }); return; } next(); } export function generateAccessToken(userId: string, role: string, status: string): string { return jwt.sign({ userId, role, status }, JWT_SECRET, { expiresIn: "1h" }); } export function generateRefreshToken(userId: string): string { return jwt.sign({ userId, type: "refresh", jti: randomUUID() }, JWT_SECRET, { expiresIn: "30d" }); }