1 import passport from "passport";
2 import { Strategy as LocalStrategy } from "passport-local";
3 import { Express, RequestHandler } from "express";
4 import session from "express-session";
5 import bcrypt from "bcrypt";
6 import { storage } from "./storage";
7 import { User as SelectUser } from "@shared/schema";
8
9 declare global {
10 namespace Express {
11 interface User extends SelectUser {}
12 }
13 }
14
15 export const isAuthenticated: RequestHandler = (req, res, next) => {
16 if (req.isAuthenticated()) return next();
17 res.status(401).json({ message: "Unauthorized" });
18 };
19
20 export function setupAuth(app: Express) {
21 app.set("trust proxy", 1);
22
23 const sessionSettings: session.SessionOptions = {
24 secret: process.env.SESSION_SECRET || "jcrguru-secret-key-2025",
25 resave: false,
26 saveUninitialized: false,
27 cookie: {
28 secure: "auto",
29 sameSite: "lax",
30 httpOnly: true,
31 maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
32 },
33 };
34
35 app.use(session(sessionSettings));
36 app.use(passport.initialize());
37 app.use(passport.session());
38
39 passport.use(
40 new LocalStrategy({ usernameField: "email" }, async (rawEmail, password, done) => {
41 try {
42 const email = (rawEmail || "").trim().toLowerCase();
43 if (!email || !password) {
44 return done(null, false, { message: "Email and password are required." });
45 }
46
47 const user = await storage.getUserByEmail(email);
48 if (!user) {
49 return done(null, false, { message: "No account found for this email. Please register or use Quick Demo Sign-In." });
50 }
51
52 const isMatch = await bcrypt.compare(password, user.password);
53 if (!isMatch) {
54 return done(null, false, { message: "Incorrect password. Please verify your credentials." });
55 }
56
57 return done(null, user);
58 } catch (err) {
59 return done(err);
60 }
61 }),
62 );
63
64 passport.serializeUser((user, done) => {
65 done(null, user.id);
66 });
67
68 passport.deserializeUser(async (id: string, done) => {
69 try {
70 const user = await storage.getUser(id);
71 done(null, user || null);
72 } catch (err) {
73 done(err);
74 }
75 });
76
77 app.post("/api/register", async (req, res, next) => {
78 try {
79 const { email: rawEmail, password, firstName, lastName, role } = req.body;
80 const email = (rawEmail || "").trim().toLowerCase();
81
82 if (!email || !password) {
83 return res.status(400).json({ message: "Email and password are required." });
84 }
85
86 const existingUser = await storage.getUserByEmail(email);
87 if (existingUser) {
88 return res.status(400).json({ message: "An account with this email already exists. Please sign in." });
89 }
90
91 const hashedPassword = await bcrypt.hash(password, 10);
92 const user = await storage.createUser({
93 email,
94 password: hashedPassword,
95 firstName: firstName?.trim() || "Technician",
96 lastName: lastName?.trim() || "",
97 role: role || "user",
98 });
99
100 req.login(user, (err) => {
101 if (err) return next(err);
102 res.status(201).json(user);
103 });
104 } catch (err) {
105 next(err);
106 }
107 });
108
109 app.post("/api/login", (req, res, next) => {
110 passport.authenticate("local", (err: any, user: Express.User | false, info?: { message?: string }) => {
111 if (err) return next(err);
112 if (!user) {
113 return res.status(401).json({
114 message: info?.message || "Invalid email or password. Please verify your credentials."
115 });
116 }
117 req.login(user, (loginErr) => {
118 if (loginErr) return next(loginErr);
119 return res.json(user);
120 });
121 })(req, res, next);
122 });
123
124 // Quick Demo / Technician auto-login endpoint
125 app.post("/api/quick-login", async (req, res, next) => {
126 try {
127 const { email: rawEmail } = req.body;
128 const email = (rawEmail || "Jhustle44@gmail.com").trim().toLowerCase();
129 let user = await storage.getUserByEmail(email);
130
131 if (!user) {
132 // Auto-provision if not present
133 const defaultHash = await bcrypt.hash("password123", 10);
134 user = await storage.createUser({
135 email,
136 password: defaultHash,
137 firstName: email.includes("jhustle") ? "Master" : "Technician",
138 lastName: "Pro",
139 role: email.includes("admin") || email.includes("jhustle") ? "admin" : "technician",
140 });
141 }
142
143 req.login(user, (err) => {
144 if (err) return next(err);
145 res.json(user);
146 });
147 } catch (err) {
148 next(err);
149 }
150 });
151
152 app.post("/api/logout", (req, res, next) => {
153 req.logout((err) => {
154 if (err) return next(err);
155 res.sendStatus(200);
156 });
157 });
158
159 app.get("/api/user", (req, res) => {
160 if (!req.isAuthenticated() || !req.user) {
161 return res.status(401).json({ message: "Not authenticated" });
162 }
163 res.json(req.user);
164 });
165 }