<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tuhin Poddar</title>
    <description>The latest articles on DEV Community by Tuhin Poddar (@tuhin114).</description>
    <link>https://dev.to/tuhin114</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1267558%2F6b52c27e-7a2d-479e-a272-69b012f6798a.jpeg</url>
      <title>DEV Community: Tuhin Poddar</title>
      <link>https://dev.to/tuhin114</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tuhin114"/>
    <language>en</language>
    <item>
      <title>Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT</title>
      <dc:creator>Tuhin Poddar</dc:creator>
      <pubDate>Sat, 11 Jan 2025 06:12:27 +0000</pubDate>
      <link>https://dev.to/tuhin114/mastering-secure-authentication-in-nodejs-loginlogout-with-bcryptjs-and-jwt-4kcl</link>
      <guid>https://dev.to/tuhin114/mastering-secure-authentication-in-nodejs-loginlogout-with-bcryptjs-and-jwt-4kcl</guid>
      <description>&lt;p&gt;Imagine you’re building a web application that's about to launch. You've carefully designed the user interface, added exciting features, and made sure everything runs smoothly. But as the launch date gets closer, a nagging concern starts to worry you—security. Specifically, how to ensure that only the right users can access the right parts of your application. This is where authentication comes in.&lt;/p&gt;

&lt;p&gt;Authentication is the process of verifying who a user is, and it's a critical aspect of web development. In the vast digital landscape, ensuring that users can securely log in and log out of your application is paramount. One slip, and your app could be vulnerable to attacks, putting user data at risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will explore secure authentication in Node.js, using &lt;code&gt;bcrypt.js&lt;/code&gt; to hash passwords and JWT tokens to manage user sessions. By the end, you'll have a solid understanding of how to implement a strong login/logout system, keeping your users’ data safe and secure.&lt;/p&gt;

&lt;p&gt;So, let’s embark on this journey to build a bulletproof authentication system, starting from setting up our environment to securing our routes with JWT. Ready to lock down your Node.js app? Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Your Node.js Project Environment
&lt;/h2&gt;

&lt;p&gt;First, initialize your Node.js project with &lt;code&gt;npm init -y&lt;/code&gt;, which creates a &lt;code&gt;package.json&lt;/code&gt; file with default settings. Next, install essential packages: &lt;code&gt;express&lt;/code&gt; for setting up the server, &lt;code&gt;mongoose&lt;/code&gt; for managing MongoDB, &lt;code&gt;jsonwebtoken&lt;/code&gt; for handling JWT tokens, &lt;code&gt;bcryptjs&lt;/code&gt; for hashing passwords, &lt;code&gt;dotenv&lt;/code&gt; for environment variables, &lt;code&gt;cors&lt;/code&gt; for enabling Cross-Origin Resource Sharing, &lt;code&gt;cookie-parser&lt;/code&gt; for parsing cookies. Finally, add &lt;code&gt;nodemon&lt;/code&gt; as a development dependency to automatically restart the server when code changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.`npm init -y`
2.`npm install express mongoose jsonwebtoken bcryptjs dotenv cors cookie-parser`
3.`npm install nodemon -D`

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now modify the &lt;code&gt;package.json&lt;/code&gt; file. Add scripts like my code and type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
    "dev": "nodemon backend/index.js",
    "start": "node backend/index.js"
  },
"type": "module",

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic Server Setup
&lt;/h2&gt;

&lt;p&gt;Next, we'll set up a basic Express server. Create a file named &lt;code&gt;index.js&lt;/code&gt; . This code initializes Express and creates an instance of the application. We'll then define a route for the root URL ("/") to handle incoming HTTP GET requests. After that, we'll start the server on port 8000, allowing it to listen for incoming requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
const app = express();

app.get("/", (req, res) =&amp;gt; {
  res.send("Server is ready");
});

app.listen(8000, () =&amp;gt; {
  console.log("Server is running on PORT 8000");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up Basic Authentication Routes
&lt;/h2&gt;

&lt;p&gt;Now, we will create a folder named 'routes' and in that folder we will make make a new file named &lt;code&gt;authRoute.js&lt;/code&gt; and paste the below code to see basics of routes.&lt;/p&gt;

&lt;p&gt;In this code snippet, we're setting up routes for different authentication endpoints using Express. First, we import the express library and create a new router instance. Then, we define three GET routes: &lt;code&gt;/signup&lt;/code&gt;, &lt;code&gt;/login&lt;/code&gt;, and &lt;code&gt;/logout&lt;/code&gt;, each responding with a JSON object indicating that the respective endpoint was hit. Finally, we export the router instance as the default export, making it available for use in other parts of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";

// Create a new Express router instance
const router = express.Router();

// Define a GET route for the signup endpoint
router.get("/signup", (req, res) =&amp;gt; {
  // Return a JSON response indicating that the signup endpoint was hit
  res.json({
    data: "You hit signup endpoint",
  });
});

// Define a GET route for the login endpoint
router.get("/login", (req, res) =&amp;gt; {
  // Return a JSON response indicating that the login endpoint was hit
  res.json({
    data: "You hit login endpoint",
  });
});

// Define a GET route for the logout endpoint
router.get("/logout", (req, res) =&amp;gt; {
  // Return a JSON response indicating that the logout endpoint was hit
  res.json({
    data: "You hit logout endpoint",
  });
});

// Export the router instance as the default export
export default router;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now change the &lt;code&gt;index.js&lt;/code&gt; adding the auth route to test out your end points.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
import authRoute from "./routes/authRoutes.js";
const app = express();

app.get("/", (req, res) =&amp;gt; {
  res.send("Server is ready");
});

app.use("/api/auth", authRoute);

app.listen(8000, () =&amp;gt; {
  console.log("Server is running on PORT 8000");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can test it in your browser...but I will use Postman for its convenience. You can test all the end points like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuzaxcit3q9ws2ckj0he.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbuzaxcit3q9ws2ckj0he.png" alt="login testing" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly you can see the other routes like &lt;strong&gt;Logout&lt;/strong&gt; and &lt;strong&gt;SignUp&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, Our basic app is ready...now make it a robust and a proper authentication system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the User Model with Mongoose Schema
&lt;/h2&gt;

&lt;p&gt;Now, first ready our mongoDB database. To do that make a folder Model and under that a file &lt;code&gt;User.js&lt;/code&gt; and in this file add Mongoose schema and model for a &lt;code&gt;User&lt;/code&gt; in a mongoDB database. The schema includes fields for &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;fullName&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt;, and &lt;code&gt;email&lt;/code&gt;, each with specified data types and constraints like uniqueness and required status. The password field also has a minimum length of 6 characters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import mongoose from "mongoose";

// Define the User schema with various fields and their data types
const userSchema = new mongoose.Schema(
  {
    // The unique username of the user
    username: {
      type: String,
      required: true,
      unique: true,
    },
    fullName: {
      type: String,
      required: true,
    },
    // The password of the user (min length: 6)
    password: {
      type: String,
      required: true,
      minLength: 6,
    },
    // The email of the user (unique)
    email: {
      type: String,
      required: true,
      unique: true,
    },
  },
  { timestamps: true }
);

// Create the User model based on the userSchema
const User = mongoose.model("User", userSchema);

// Export the User model
export default User;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Connecting to MongoDB with Mongoose
&lt;/h2&gt;

&lt;p&gt;Now let's connect to our database. We'll create a folder named db and inside it, a file called &lt;code&gt;connectDB.js&lt;/code&gt;. In this file, we'll define an asynchronous function &lt;code&gt;connectMongoDB&lt;/code&gt; that tries to connect to a MongoDB database using Mongoose. It gets the database connection string from the &lt;code&gt;MONGO_URI&lt;/code&gt; environment variable. If the connection is successful, it logs a success message with the host name. If it fails, it logs the error and exits the process with a status code of 1. The function is exported for use in other parts of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import mongoose from "mongoose";

const connectMongoDB = async () =&amp;gt; {
  try {
    // Use the mongoose.connect() method to connect to the database
    // using the MONGO_URI environment variable
    const conn = await mongoose.connect(process.env.MONGO_URI);
    console.log(`MongoDB connected: ${conn.connection.host}`);
  } catch (error) {
    console.error(`Error connection to mongoDB: ${error.message}`);
    process.exit(1);
  }
};

export default connectMongoDB;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to use &lt;code&gt;MONGO_URI&lt;/code&gt; we have to make it in &lt;code&gt;.env&lt;/code&gt; file. Here I have used local mongoDB setup connection string. If you want then you can also use mongoDB atlas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONGO_URI=mongodb://localhost:27017/auth-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Signup function
&lt;/h2&gt;

&lt;p&gt;Now make the signup function. For this 1st make a folder controller and there file &lt;code&gt;authController.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const signup = async (req, res) =&amp;gt; {
  try {
    const { fullName, username, email, password } = req.body; // Destructuring user input

    // Regular expression for email format validation
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
      return res.status(400).json({ error: "Invalid email format" });
    }

    // Checking if username is already taken
    const existingUser = await User.findOne({ username });
    if (existingUser) {
      return res.status(400).json({ error: "Username is already taken" });
    }

    // Checking if email is already taken
    const existingEmail = await User.findOne({ email });
    if (existingEmail) {
      return res.status(400).json({ error: "Email is already taken" });
    }

    // Validating password length
    if (password.length &amp;lt; 6) {
      return res
        .status(400)
        .json({ error: "Password must be at least 6 characters long" });
    }

    // Generating salt and hashing the password
    const salt = await bcrypt.genSalt(10);
    const hashedPassword = await bcrypt.hash(password, salt);

    // Creating a new user instance
    const newUser = new User({
      fullName,
      username,
      email,
      password: hashedPassword,
    });

    // Saving the new user to the database
    await newUser.save();

    // Generating JWT token and setting it as a cookie
    generateTokenAndSetCookie(newUser._id, res);

    // Sending success response with user data
    res.status(201).json({
      _id: newUser._id,
      fullName: newUser.fullName,
      username: newUser.username,
      email: newUser.email,
    });
  } catch (error) {
    // Handling errors
    console.log("Error in signup controller", error.message);
    res.status(500).json({ error: "Internal Server Error" });
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, it extracts &lt;code&gt;fullName&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, and &lt;code&gt;password&lt;/code&gt; from the request body. It validates the email format using a regular expression, returning a 400 status if the format is invalid.&lt;/p&gt;

&lt;p&gt;Next, the function checks if the username or email already exists in the database. If either is taken, a 400 status with an error message is returned. It also ensures the password is at least 6 characters long, sending another 400 status if this condition isn't met.&lt;/p&gt;

&lt;p&gt;The password is then securely hashed using &lt;code&gt;bcrypt&lt;/code&gt;. A new &lt;code&gt;User&lt;/code&gt; instance is created with the provided data and saved to the database.&lt;/p&gt;

&lt;p&gt;After saving, the function generates a JWT token, sets it as a cookie, and returns a 201 status with the user's ID, full name, username, and email. If any errors occur, they are logged, and a 500 status is sent with an "Internal Server Error" message.&lt;/p&gt;

&lt;p&gt;To make this function active you have to import these&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { generateTokenAndSetCookie } from "../utils/generateToken.js";
import User from "../Model/User.js";
import bcrypt from "bcryptjs";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice something? a new thing called &lt;strong&gt;generateTokenAndSetCookie&lt;/strong&gt;...lets see its code...make a folder utils and there &lt;code&gt;generateTokenAndSetCookie.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Import the jsonwebtoken library
import jwt from "jsonwebtoken";

// Define a function that generates a JWT and sets it as a cookie
export const generateTokenAndSetCookie = (userId, res) =&amp;gt; {
  // Use the jsonwebtoken library to sign a JWT with the user ID and a secret key
  // Set the expiration time of the token to 15 days
  const token = jwt.sign({ userId }, process.env.JWT_SECRET, {
    expiresIn: "15d",
  });
  res.cookie("jwt", token, {
    maxAge: 15 * 24 * 60 * 60 * 1000, //MS
    httpOnly: true, // prevent XSS attacks cross-site scripting attacks
    sameSite: "strict", // CSRF attacks cross-site request forgery attacks
    secure: process.env.NODE_ENV !== "development",
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The **generateTokenAndSetCookie **function creates a JWT and stores it in a cookie for user authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT Generation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The function uses the &lt;code&gt;jsonwebtoken&lt;/code&gt; library to create a JWT. It signs the token with the user's ID and a secret key (&lt;code&gt;JWT_SECRET&lt;/code&gt; from the environment variables), setting it to expire in 15 days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting the Cookie:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The token is then stored in a cookie on the user's browser. The cookie is configured with several security attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maxAge: Sets the cookie's lifespan to 15 days.&lt;/li&gt;
&lt;li&gt;httpOnly: Ensures the cookie is not accessible via JavaScript, protecting against XSS (Cross-Site Scripting) attacks.&lt;/li&gt;
&lt;li&gt;sameSite: "strict": Prevents CSRF (Cross-Site Request Forgery) attacks by restricting the cookie to be sent only with requests from the same site.&lt;/li&gt;
&lt;li&gt;secure: Ensures the cookie is only sent over HTTPS if the environment is not development, adding an extra layer of security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this function ensures that the user's session is both secure and persistent, making it a crucial part of the authentication process.&lt;/p&gt;

&lt;p&gt;Here we have to add another environment variable &lt;code&gt;JWT_SECRET&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt;. You can add any type of mix of number and string like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONGO_URI=mongodb://localhost:27017/auth-test
JWT_SECRET=GBfChCMY8MB7wnymkWhtoD3cRlA1CC5e6iWSBmnuaSg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our &lt;code&gt;signUp&lt;/code&gt; function is complete..so make its route now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
import { signup } from "../controllers/authController.js";

const router = express.Router();

router.post("/signup", signup);

export default router;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ok, now let’s modify our &lt;code&gt;index.js&lt;/code&gt; Here we added some new imports. &lt;strong&gt;dotenv:&lt;/strong&gt; Loads environment variables securely from &lt;code&gt;.env&lt;/code&gt;; &lt;strong&gt;express.json():&lt;/strong&gt; Parses incoming JSON requests; &lt;strong&gt;express.urlencoded({ extended: true }):&lt;/strong&gt; Parses URL-encoded data; &lt;strong&gt;cookieParser:&lt;/strong&gt; Handles cookies for JWT tokens; &lt;strong&gt;connectMongoDB():&lt;/strong&gt; Connects to MongoDB for data storage; &lt;strong&gt;Routes:&lt;/strong&gt; /api/auth manages signup, login, and logout.&lt;/p&gt;

&lt;p&gt;Here is updated code of &lt;code&gt;index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
import dotenv from "dotenv";
import authRoute from "./routes/authRoutes.js";
import connectMongoDB from "./db/connectDB.js";
import cookieParser from "cookie-parser";

dotenv.config();

const app = express();

app.get("/", (req, res) =&amp;gt; {
  res.send("Server is ready");
});

// Middleware to parse JSON request bodies
app.use(express.json()); // for parsing application/json

// Middleware to parse URL-encoded request bodies (if you're using form data)
app.use(express.urlencoded({ extended: true }));

app.use(cookieParser());

app.use("/api/auth", authRoute);

app.listen(8000, () =&amp;gt; {
  console.log("Server is running on PORT 8000");
  connectMongoDB();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So. now it’s time to test our signup function in Postman. Let’s see if it’s working or not.&lt;/p&gt;

&lt;p&gt;So, here is the results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruzx5cfi2tda6ohbdwxc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fruzx5cfi2tda6ohbdwxc.png" alt="Signup" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see it’s working properly and you can check it your mongoDB database as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgitacx1ar4sjofpokfk6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgitacx1ar4sjofpokfk6.png" alt="database" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Login function
&lt;/h2&gt;

&lt;p&gt;Now make the login function. Let’s go again to our &lt;code&gt;authController.js&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const login = async (req, res) =&amp;gt; {
  try {
    const { username, password } = req.body; // Destructuring user input

    // Finding the user by username
    const user = await User.findOne({ username });

    // Comparing passwords using bcrypt
    const isPasswordCorrect = await bcrypt.compare(
      password,
      user?.password || ""
    );

    // If user or password is incorrect, return error
    if (!user || !isPasswordCorrect) {
      return res.status(400).json({ error: "Invalid username or password" });
    }

    // Generating JWT token and setting it as a cookie
    generateTokenAndSetCookie(user._id, res);

    // Sending success response with user data
    res.status(200).json({
      message: "Logged in successfully",
    });
  } catch (error) {
    // Handling errors
    console.log("Error in login controller", error.message);
    res.status(500).json({ error: "Internal Server Error" });
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The login controller authenticates a user by verifying their username and password. It first searches for the user in the database using the username. If found, it compares the provided password with the hashed password stored in the database using bcrypt. If the username or password is incorrect, it returns an error response. On successful verification, it generates a JWT token, sets it as a cookie using &lt;code&gt;generateTokenAndSetCookie&lt;/code&gt;, and responds with a success message, indicating the user is logged in successfully.&lt;/p&gt;

&lt;p&gt;Let’s add our login route in &lt;code&gt;authRoutes.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
import { login, signup } from "../controllers/authController.js";

const router = express.Router();

router.post("/signup", signup);
router.post("/login", login);

export default router;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s test it in Postman.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40q8j9mzotzm4p5udooi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40q8j9mzotzm4p5udooi.png" alt="login" width="800" height="504"&gt;&lt;/a&gt;&lt;br&gt;
Here you can see it is successfully showing Logged in.&lt;/p&gt;
&lt;h2&gt;
  
  
  Logout function
&lt;/h2&gt;

&lt;p&gt;Okay. Now the last function i.e. the logout function. Let’s implement this. It’s pretty simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const logout = async (req, res) =&amp;gt; {
  try {
    // Clearing JWT cookie
    res.cookie("jwt", "", { maxAge: 0 });
    // Sending success response
    res.status(200).json({ message: "Logged out successfully" });
  } catch (error) {
    // Handling errors
    console.log("Error in logout controller", error.message);
    res.status(500).json({ error: "Internal Server Error" });
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logout controller securely logs out a user by clearing the JWT cookie from the client's browser using &lt;code&gt;res.cookie&lt;/code&gt;, setting its value to an empty string and its &lt;code&gt;maxAge&lt;/code&gt; to 0, ensuring immediate expiration. Upon successful cookie clearance, it sends a success response with a message indicating the user is logged out successfully. If any error occurs during this process, it catches the error, logs it, and returns an Internal Server Error response.&lt;/p&gt;

&lt;p&gt;Add this route to our &lt;code&gt;authRoute.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from "express";
import { login, logout, signup } from "../controllers/authController.js";

const router = express.Router();

router.post("/signup", signup);
router.post("/login", login);
router.post("/logout", logout);

export default router;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;okay. Let’s test our last feature, if it’s working fine or not.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fai6f5ggvcksqy6phxsq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fai6f5ggvcksqy6phxsq5.png" alt="logout" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh!…It’s working super fine. 😃😃&lt;/p&gt;

&lt;p&gt;So, now our complete backend of this authentication is ready. 🎉🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  My npm package
&lt;/h2&gt;

&lt;p&gt;If you don't want to code everything yourself and want a quick solution, I have created an npm package called auth0_package. You can get it from &lt;a href="https://www.npmjs.com/package/auth0_package" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Repository
&lt;/h2&gt;

&lt;p&gt;You can get my all above code here in this github repo &lt;a href="https://github.com/Tuhin114/Auth-App" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now your backend application is complete. In the next blog, I will explain how to integrate this with your frontend. So stay tuned for that 😉😉.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, implementing secure authentication in a Node.js application is crucial for protecting user data and ensuring that only authorized users can access specific parts of your application. By using bcrypt.js for password hashing and JWT tokens for session management, you can create a robust login/logout system. This approach not only enhances security but also provides a seamless user experience. Setting up a MongoDB database and using Express for routing further strengthens the backend infrastructure. With these tools and techniques, you can confidently launch your web application, knowing that it is well-protected against unauthorized access and potential security threats.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>node</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Unlocking Backend Simplicity: Building Scalable Apps with Convex</title>
      <dc:creator>Tuhin Poddar</dc:creator>
      <pubDate>Sat, 07 Dec 2024 01:41:42 +0000</pubDate>
      <link>https://dev.to/tuhin114/unlocking-backend-simplicity-building-scalable-apps-with-convex-3bnk</link>
      <guid>https://dev.to/tuhin114/unlocking-backend-simplicity-building-scalable-apps-with-convex-3bnk</guid>
      <description>&lt;p&gt;&lt;strong&gt;Building scalable, efficient applications can be challenging right ? Specially you have less time or in a hackathon. What if I told you there’s a backend solution that can simplify this process?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recently I was working on a project where I used the Convex backend for the first time and guess what, it feels simply awesome.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.convex.dev/" rel="noopener noreferrer"&gt;Convex&lt;/a&gt; is more than just a database; it’s a comprehensive backend solution tailored for modern developers. It offers everything from cloud functions in TypeScript to real-time data synchronization, enabling you to focus entirely on your front-end code. This has contributed to its growing popularity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sets It Apart 🤔🤔
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Data Sync:&lt;/strong&gt;
The most amazing feature I love most about it is, the real time data synchronization means there is hustle to set up the socket io then send it from backend to frontend. instead here Data is synchronized in real-time between client applications and the database, making it ideal for collaborative or live applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Functions:&lt;/strong&gt;
Convex provides serverless functions, called "Convex Functions," which allow you to run backend logic without managing servers. These functions are written in JavaScript or TypeScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in Authentication:&lt;/strong&gt;
Although there are services like clerk, next auth which works super fine with Next.js + typescript, Convex also supports user authentication, including third-party providers, so you can easily add user login to applications without setting up a custom authentication system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable Database:&lt;/strong&gt;
Convex’s database is automatically scalable and designed to support high concurrency, so it can handle large datasets and traffic spikes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema-less Data Model:&lt;/strong&gt;
Convex uses a schema-less data model, allowing you to store flexible data structures, which is beneficial for rapidly evolving projects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the features I personally used and there are many more features like &lt;strong&gt;ACID Transactions&lt;/strong&gt;, &lt;strong&gt;TypeScript Support&lt;/strong&gt;, &lt;strong&gt;Security and Access Control&lt;/strong&gt;, &lt;strong&gt;Automatic Caching and Optimization&lt;/strong&gt;, you can definitely try out.&lt;/p&gt;

&lt;p&gt;Now Let’s see how is the approach in normal backend and in a convex backend by a simple &lt;code&gt;getGroupMembers&lt;/code&gt; function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s build a backend function using MongoDB and Node.js
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First we start by verifying the user’s identity by a typical JWT and for error lets just return 401 Unauthorized response.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const identity = await verifyToken(req.headers.authorization);
  if (!identity) {
    res.status(401).send("Unauthorized");
    return;
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Once the user is authenticated, we retrieve the conversation details. This step involves querying the conversations collection in MongoDB by the provided conversation ID.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const conversation = await db.collection("conversations").findOne({ _id: conversationId });
  if (!conversation) {
    res.status(404).send("Conversation not found");
    return;
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next, we retrieve all users from the users collection and filter for those whose IDs match the participants in the conversation.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const users = await db.collection("users").find().toArray();
  const groupMembers = users.filter(user =&amp;gt; conversation.participants.includes(user._id));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Finally, we return the list of groupMembers to the client. This data includes only the users who are participants in the specified conversation.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  res.status(200).send(groupMembers);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here is the representable diagram of the above code snippet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21topbkwzbkfa262589v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21topbkwzbkfa262589v.png" alt="Normal backend fuction" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s build a backend function using Convex
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Convex provides built-in user authentication with &lt;code&gt;ctx.auth.getUserIdentity()&lt;/code&gt;, making it easy to check if a user is logged in. If the user is not authenticated, we throw a &lt;code&gt;ConvexError&lt;/code&gt;, which automatically returns an “Unauthorized” response to the client.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const identity = await ctx.auth.getUserIdentity();
  if (!identity) {
    throw new ConvexError("Unauthorized");
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;With Convex, database queries are simplified. Using &lt;code&gt;ctx.db.query&lt;/code&gt;, we retrieve the conversation by filtering for a match with the provided conversation ID.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const conversation = await ctx.db.query("conversations")
    .filter((q) =&amp;gt; q.eq(q.field("_id"), args.conversationId))
    .first();
  if (!conversation) {
    throw new ConvexError("Conversation not found");
  }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Convex allows us to query all users with &lt;code&gt;ctx.db.query("users").collect()&lt;/code&gt;. We then use filter to select only users who are participants in the conversation. Convex’s built-in data retrieval methods make it easier to manage collections without dealing with manual database connection handling.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const users = await ctx.db.query("users").collect();
  const groupMembers = users.filter((user) =&amp;gt; conversation.participants.includes(user._id));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;That’s all. Since Convex handles response management, simply returning groupMembers from the function sends the data to the client.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  return groupMembers;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;And here is the overall simplified explanation diagram how convex process the backend -&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wvd60j4jeqiegufxnqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wvd60j4jeqiegufxnqt.png" alt="overall simplified convex architecture" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How have I used Convex in my projects
&lt;/h2&gt;

&lt;p&gt;I just recreated freeCodeCamp MERN stack Book Store Project using Next.js, TypeScript, and most importantly, the Convex backend.&lt;/p&gt;

&lt;p&gt;So, if you want a good idea of how to use Convex backend then you can follow my github projects where I have shifted my tech stack from MERN stack to NEXT.js + TS + Convex.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;𝐁𝐨𝐨𝐤-𝐒𝐭𝐨𝐫𝐞 (𝐌𝐄𝐑𝐍 𝐒𝐭𝐚𝐜𝐤) - &lt;a href="https://github.com/Tuhin114/Book-Store" rel="noopener noreferrer"&gt;Check it out here&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;𝐁𝐨𝐨𝐤-𝐒𝐭𝐨𝐫𝐞_𝐂𝐨𝐧𝐯𝐞𝐱 (𝐍𝐞𝐱𝐭.𝐣𝐬 + 𝐓𝐒 + 𝐂𝐨𝐧𝐯𝐞𝐱) - &lt;a href="https://github.com/Tuhin114/Book-Store_Convex" rel="noopener noreferrer"&gt;Check it out here&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;if you want you can also visit my &lt;a href="https://www.linkedin.com/posts/tuhin-poddar-a2a84b274_linkedincommunity-frontend-backend-activity-7266069080208531456-2e0-/?utm_source=share&amp;amp;utm_medium=member_desktop" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; post regarding this 🙂🙂.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In short in a traditional backend setup, you manually handle authentication, database connections, queries, and errors, leading to more complex and verbose code. In Convex, these tasks are abstracted, simplifying authentication, database querying, and error management with minimal code, allowing for faster development and cleaner code.&lt;/p&gt;

&lt;p&gt;Happy Learning ☺☺!!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
