import session from "express-session"; import connectPg from "connect-pg-simple"; import type { Express, RequestHandler, Request, Response, NextFunction } from "express"; import bcrypt from "bcryptjs"; import { storage } from "./storage"; declare module "express-session" { interface SessionData { userId?: string; isAdmin?: boolean; isSuperAdmin?: boolean; } } export function getSession() { const sessionTtl = 7 * 24 * 60 * 60 * 1000; const PgStore = connectPg(session); const sessionStore = new PgStore({ conString: process.env.DATABASE_URL, createTableIfMissing: false, ttl: sessionTtl, tableName: "sessions", }); return session({ secret: process.env.SESSION_SECRET!, store: sessionStore, resave: false, saveUninitialized: false, cookie: { httpOnly: true, secure: process.env.NODE_ENV === "production", maxAge: sessionTtl, }, }); } export async function setupAuth(app: Express) { app.set("trust proxy", 1); app.use(getSession()); // POST /api/auth/login — email + password app.post("/api/auth/login", async (req: Request, res: Response) => { const { email, password } = req.body || {}; if (!email || !password) return res.status(400).json({ error: "Email and password required" }); try { const user = await storage.getUserByEmail(email); if (!user || !user.passwordHash) return res.status(401).json({ error: "Invalid credentials" }); const ok = await bcrypt.compare(password, user.passwordHash); if (!ok || !user.isActive) return res.status(401).json({ error: "Invalid credentials" }); req.session.userId = user.id; req.session.isAdmin = !!user.isAdmin || !!user.isSuperAdmin; req.session.isSuperAdmin = !!user.isSuperAdmin; return res.json({ user: { id: user.id, email: user.email, username: user.username, isAdmin: req.session.isAdmin } }); } catch (e: any) { console.error("Login err:", e); return res.status(500).json({ error: "Login failed" }); } }); app.post("/api/auth/logout", (req: Request, res: Response) => { req.session.destroy(() => res.json({ ok: true })); }); app.get("/api/login", (_req, res) => res.redirect("/login")); app.get("/api/callback", (_req, res) => res.redirect("/login")); app.get("/api/logout", (req, res) => { req.session.destroy(() => res.redirect("/")); }); } export const isAuthenticated: RequestHandler = (req: any, res, next) => { if (!req.session?.userId) return res.status(401).json({ message: "Unauthorized" }); req.user = { claims: { sub: req.session.userId } }; // compat with old code expecting req.user.claims.sub next(); }; export const isAdmin: RequestHandler = (req: any, res, next) => { if (!req.session?.userId) return res.status(401).json({ message: "Unauthorized" }); if (!req.session.isAdmin) return res.status(403).json({ message: "Forbidden" }); req.user = { claims: { sub: req.session.userId } }; next(); };