import passport from "passport"; import { Strategy as LocalStrategy } from "passport-local"; import { Express } from "express"; import session from "express-session"; import { scrypt, randomBytes, timingSafeEqual } from "crypto"; import { promisify } from "util"; import { storage } from "./storage"; import { User as SelectUser, insertUserSchema } from "@shared/schema"; declare global { namespace Express { interface User extends SelectUser {} } } const scryptAsync = promisify(scrypt); async function hashPassword(password: string) { const salt = randomBytes(16).toString("hex"); const buf = (await scryptAsync(password, salt, 64)) as Buffer; return `${buf.toString("hex")}.${salt}`; } async function comparePasswords(supplied: string, stored: string) { const [hashed, salt] = stored.split("."); const hashedBuf = Buffer.from(hashed, "hex"); const suppliedBuf = (await scryptAsync(supplied, salt, 64)) as Buffer; return timingSafeEqual(hashedBuf, suppliedBuf); } export function setupAuth(app: Express) { const sessionSettings: session.SessionOptions = { secret: process.env.SESSION_SECRET || "pilateswithfadia-secret-key", resave: false, saveUninitialized: false, store: storage.sessionStore, cookie: { maxAge: 1000 * 60 * 60 * 24, // 1 day httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax" } }; app.set("trust proxy", 1); app.use(session(sessionSettings)); app.use(passport.initialize()); app.use(passport.session()); passport.use( new LocalStrategy(async (username, password, done) => { const user = await storage.getUserByUsername(username); if (!user || !(await comparePasswords(password, user.password))) { return done(null, false); } else { return done(null, user); } }), ); passport.serializeUser((user, done) => done(null, user.id)); passport.deserializeUser(async (id: number, done) => { const user = await storage.getUser(id); done(null, user); }); app.post("/api/register", async (req, res, next) => { try { const validatedUser = insertUserSchema.parse(req.body); // Check if username already exists const existingUserByUsername = await storage.getUserByUsername(validatedUser.username); if (existingUserByUsername) { return res.status(400).json({ message: "Username already exists" }); } // Check if email already exists const existingUserByEmail = await storage.getUserByEmail(validatedUser.email); if (existingUserByEmail) { return res.status(400).json({ message: "Email already in use" }); } const user = await storage.createUser({ ...validatedUser, password: await hashPassword(validatedUser.password), }); req.login(user, (err) => { if (err) return next(err); // Return user without password const { password, ...userWithoutPassword } = user; res.status(201).json(userWithoutPassword); }); } catch (error) { res.status(400).json({ message: "Invalid registration data", error }); } }); app.post("/api/login", (req, res, next) => { passport.authenticate("local", (err: Error, user: SelectUser) => { if (err) return next(err); if (!user) { return res.status(401).json({ message: "Invalid username or password" }); } req.login(user, (err) => { if (err) return next(err); // Return user without password const { password, ...userWithoutPassword } = user; res.status(200).json(userWithoutPassword); }); })(req, res, next); }); app.post("/api/logout", (req, res, next) => { req.logout((err) => { if (err) return next(err); res.sendStatus(200); }); }); app.get("/api/user", (req, res) => { if (!req.isAuthenticated()) return res.sendStatus(401); // Return user without password const { password, ...userWithoutPassword } = req.user; res.json(userWithoutPassword); }); }