const express = require("express"); const jwt = require("jsonwebtoken"); const { OAuth2Client } = require("google-auth-library"); const { User } = require("../models"); // Import from models/index.js to get models with associations const { sanitizeInput, validateRegistration, validateLogin, validateGoogleAuth } = require("../middleware/validation"); const router = express.Router(); const googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID); router.post("/register", sanitizeInput, validateRegistration, async (req, res) => { try { const { username, email, password, firstName, lastName, phone } = req.body; const existingUser = await User.findOne({ where: { [require("sequelize").Op.or]: [{ email }, { username }], }, }); if (existingUser) { return res.status(400).json({ error: "Registration failed", details: [{ field: "email", message: "An account with this email already exists" }] }); } const user = await User.create({ username, email, password, firstName, lastName, phone, }); const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: "7d", }); res.status(201).json({ user: { id: user.id, username: user.username, email: user.email, firstName: user.firstName, lastName: user.lastName, }, token, }); } catch (error) { console.error('Registration error:', error); res.status(500).json({ error: "Registration failed. Please try again." }); } }); router.post("/login", sanitizeInput, validateLogin, async (req, res) => { try { const { email, password } = req.body; const user = await User.findOne({ where: { email } }); if (!user) { return res.status(401).json({ error: "Invalid credentials" }); } // Check if account is locked if (user.isLocked()) { return res.status(423).json({ error: "Account is temporarily locked due to too many failed login attempts. Please try again later." }); } // Verify password const isPasswordValid = await user.comparePassword(password); if (!isPasswordValid) { // Increment login attempts await user.incLoginAttempts(); return res.status(401).json({ error: "Invalid credentials" }); } // Reset login attempts on successful login await user.resetLoginAttempts(); const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: "7d", }); res.json({ user: { id: user.id, username: user.username, email: user.email, firstName: user.firstName, lastName: user.lastName, }, token, }); } catch (error) { console.error('Login error:', error); res.status(500).json({ error: "Login failed. Please try again." }); } }); router.post("/google", sanitizeInput, validateGoogleAuth, async (req, res) => { try { const { idToken } = req.body; if (!idToken) { return res.status(400).json({ error: "ID token is required" }); } // Verify the Google ID token const ticket = await googleClient.verifyIdToken({ idToken, audience: process.env.GOOGLE_CLIENT_ID, }); const payload = ticket.getPayload(); const { sub: googleId, email, given_name: firstName, family_name: lastName, picture, } = payload; if (!email || !firstName || !lastName) { return res .status(400) .json({ error: "Required user information not provided by Google" }); } // Check if user exists by Google ID first let user = await User.findOne({ where: { providerId: googleId, authProvider: "google" }, }); if (!user) { // Check if user exists with same email but different auth provider const existingUser = await User.findOne({ where: { email } }); if (existingUser) { return res.status(409).json({ error: "An account with this email already exists. Please use email/password login.", }); } // Create new user user = await User.create({ email, firstName, lastName, authProvider: "google", providerId: googleId, profileImage: picture, username: email.split("@")[0] + "_" + googleId.slice(-6), // Generate unique username }); } // Generate JWT token const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: "7d", }); res.json({ user: { id: user.id, username: user.username, email: user.email, firstName: user.firstName, lastName: user.lastName, profileImage: user.profileImage, }, token, }); } catch (error) { if (error.message && error.message.includes("Token used too late")) { return res .status(401) .json({ error: "Google token has expired. Please try again." }); } if (error.message && error.message.includes("Invalid token")) { return res .status(401) .json({ error: "Invalid Google token. Please try again." }); } if (error.message && error.message.includes("Wrong number of segments")) { return res .status(400) .json({ error: "Malformed Google token. Please try again." }); } console.error('Google auth error:', error); res.status(500).json({ error: "Google authentication failed. Please try again." }); } }); module.exports = router;