From b895a0963ce331e8209f14ce30a39e306207ddee Mon Sep 17 00:00:00 2001 From: Ad1th <90973387+Ad1th@users.noreply.github.com> Date: Sun, 10 May 2026 23:10:45 +0530 Subject: [PATCH 1/2] feat: implement authentication system with signup, login, and protected routes --- backend/middleware/authMiddleware.js | 9 +++ backend/routes/auth.js | 67 +++++++++++++------ backend/server.js | 50 ++++++++------ src/Routes/Router.tsx | 10 ++- src/components/Navbar.tsx | 98 ++++++++++++++++++++++------ src/components/ProtectedRoute.tsx | 30 +++++++++ src/context/AuthContext.tsx | 90 +++++++++++++++++++++++++ src/hooks/useAuth.ts | 10 +++ src/main.tsx | 9 ++- src/pages/Login/Login.tsx | 19 +++--- src/pages/Signup/Signup.tsx | 63 +++++++----------- src/utils/axiosConfig.ts | 13 ++++ 12 files changed, 355 insertions(+), 113 deletions(-) create mode 100644 backend/middleware/authMiddleware.js create mode 100644 src/components/ProtectedRoute.tsx create mode 100644 src/context/AuthContext.tsx create mode 100644 src/hooks/useAuth.ts create mode 100644 src/utils/axiosConfig.ts diff --git a/backend/middleware/authMiddleware.js b/backend/middleware/authMiddleware.js new file mode 100644 index 0000000..eca1d83 --- /dev/null +++ b/backend/middleware/authMiddleware.js @@ -0,0 +1,9 @@ +// Middleware to check if user is authenticated +const isAuthenticated = (req, res, next) => { + if (req.isAuthenticated()) { + return next(); + } + res.status(401).json({ message: 'Unauthorized - Please log in' }); +}; + +module.exports = { isAuthenticated }; diff --git a/backend/routes/auth.js b/backend/routes/auth.js index e26c7a9..9ffa01d 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -1,42 +1,67 @@ const express = require("express"); const passport = require("passport"); const User = require("../models/User"); +const { isAuthenticated } = require("../middleware/authMiddleware"); // I'm calling the Middleware I just created here const router = express.Router(); // Signup route router.post("/signup", async (req, res) => { + const { username, email, password } = req.body; - const { username, email, password } = req.body; + try { + const existingUser = await User.findOne({ email }); - try { - const existingUser = await User.findOne( {email} ); + if (existingUser) + return res.status(400).json({ message: "User already exists" }); - if (existingUser) - return res.status(400).json( {message: 'User already exists'} ); + const newUser = new User({ username, email, password }); + await newUser.save(); + res.status(201).json({ message: "User created successfully" }); + } catch (err) { + res + .status(500) + .json({ message: "Error creating user", error: err.message }); + } +}); - const newUser = new User( {username, email, password} ); - await newUser.save(); - res.status(201).json( {message: 'User created successfully'} ); - } catch (err) { - res.status(500).json({ message: 'Error creating user', error: err.message }); +// I'm writing a much more complex login route here because I want to handle all the possible errors that can happen during the login process, and I also want to return the user data if the login is successful. This way, the frontend can easily access the user information after logging in without needing to make an additional request to get the current user. +router.post("/login", (req, res, next) => { + passport.authenticate("local", (err, user, info) => { + if (err) { + return res + .status(500) + .json({ message: "Login error", error: err.message }); + } + if (!user) { + return res + .status(401) + .json({ message: info.message || "Invalid credentials" }); } + req.logIn(user, (err) => { + if (err) { + return res + .status(500) + .json({ message: "Session error", error: err.message }); + } + res.status(200).json({ message: "Login successful", user: req.user }); + }); + })(req, res, next); }); -// Login route -router.post("/login", passport.authenticate('local'), (req, res) => { - res.status(200).json( { message: 'Login successful', user: req.user } ); +// Get current authenticated user +router.get("/me", isAuthenticated, (req, res) => { + res.status(200).json({ user: req.user }); }); // Logout route router.get("/logout", (req, res) => { - - req.logout((err) => { - - if (err) - return res.status(500).json({ message: 'Logout failed', error: err.message }); - else - res.status(200).json({ message: 'Logged out successfully' }); - }); + req.logout((err) => { + if (err) + return res + .status(500) + .json({ message: "Logout failed", error: err.message }); + else res.status(200).json({ message: "Logged out successfully" }); + }); }); module.exports = router; diff --git a/backend/server.js b/backend/server.js index 3f19f00..41491a9 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,39 +1,51 @@ -const express = require('express'); -const mongoose = require('mongoose'); -const session = require('express-session'); -const passport = require('passport'); -const bodyParser = require('body-parser'); -require('dotenv').config(); -const cors = require('cors'); +const express = require("express"); +const mongoose = require("mongoose"); +const session = require("express-session"); +const passport = require("passport"); +const bodyParser = require("body-parser"); +require("dotenv").config(); +const cors = require("cors"); // Passport configuration -require('./config/passportConfig'); +require("./config/passportConfig"); const app = express(); // CORS configuration -app.use(cors('*')); +//Ok so this needs to be taken care of, Cannot expose the BE like that, only FE can call it, else you'll face issues +// For testing, if anyone else uses any other port, pls change the default vite port to that port, and also change the FRONTEND_URL in .env to that port, else you'll face CORS issues, and you won't be able to call the BE from the FE. +app.use( + cors({ + origin: process.env.FRONTEND_URL || "http://localhost:5173", + credentials: true, + }), +); // Middleware app.use(bodyParser.json()); -app.use(session({ +app.use( + session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, -})); + }), +); app.use(passport.initialize()); app.use(passport.session()); // Routes -const authRoutes = require('./routes/auth'); -app.use('/api/auth', authRoutes); +const authRoutes = require("./routes/auth"); +app.use("/api/auth", authRoutes); // Connect to MongoDB -mongoose.connect(process.env.MONGO_URI, {}).then(() => { - console.log('Connected to MongoDB'); +mongoose + .connect(process.env.MONGO_URI, {}) + .then(() => { + console.log("Connected to MongoDB"); app.listen(process.env.PORT, () => { - console.log(`Server running on port ${process.env.PORT}`); + console.log(`Server running on port ${process.env.PORT}`); }); -}).catch((err) => { - console.log('MongoDB connection error:', err); -}); + }) + .catch((err) => { + console.log("MongoDB connection error:", err); + }); diff --git a/src/Routes/Router.tsx b/src/Routes/Router.tsx index 40a7861..2dd839e 100644 --- a/src/Routes/Router.tsx +++ b/src/Routes/Router.tsx @@ -7,12 +7,20 @@ import Signup from "../pages/Signup/Signup.tsx"; import Login from "../pages/Login/Login.tsx"; import ContributorProfile from "../pages/ContributorProfile/ContributorProfile.tsx"; import Home from "../pages/Home/Home.tsx"; +import ProtectedRoute from "../components/ProtectedRoute"; const Router = () => { return ( } /> - } /> + + + + } + /> } /> } /> } /> diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index c6cc86d..bcdc28b 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,18 +1,33 @@ -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { useState, useContext } from "react"; import { ThemeContext } from "../context/ThemeContext"; -import { Moon, Sun } from 'lucide-react'; - +import { Moon, Sun, LogOut } from "lucide-react"; +import { useAuth } from "../hooks/useAuth"; const Navbar: React.FC = () => { - const [isOpen, setIsOpen] = useState(false); + const [isLoggingOut, setIsLoggingOut] = useState(false); const themeContext = useContext(ThemeContext); + const { isAuthenticated, user, logout } = useAuth(); + const navigate = useNavigate(); - if (!themeContext) - return null; + if (!themeContext) return null; const { toggleTheme, mode } = themeContext; + //handling the logout process here, we set the isLoggingOut state to true, then we call the logout function from the AuthContext, and if it's successful, we navigate the user to the home page and close the mobile menu. If there's an error during logout, we log it to the console. Finally, we set isLoggingOut back to false regardless of the outcome. + + const handleLogout = async () => { + setIsLoggingOut(true); + try { + await logout(); + navigate("/"); + setIsOpen(false); + } catch (error) { + console.error("Logout failed:", error); + } finally { + setIsLoggingOut(false); + } + }; return (