Creating Login Endpoint Login Existing User Cloudnotebook

Posted on July 20, 2026 by Vishesh Namdev
Python C C++ Javascript React JS
Creating Login Endpoint & Login Existing User in CloudNoteBook - Node.js JWT authentication tutorial

Creating Login Endpoint & Login Existing User in CloudNoteBook 🔐👤 So far in CloudNoteBook, we've built a Sign Up endpoint that creates a new user and stores their hashed password in MongoDB. But a real app also needs a way for existing users to log back in. That means checking their email, comparing their password securely, and issuing them a fresh JWT (JSON Web Token) to prove they're authenticated on future requests.

In this tutorial, we will learn:

  • What a login endpoint does and why JWTs matter
  • Adding validation rules for email and password using express-validator
  • Finding an existing user by email with Mongoose
  • Safely comparing passwords using bcrypt.compare
  • Generating and returning a JWT auth token on successful login
  • Handling invalid credentials with proper error responses
  • ---

    What Does a Login Endpoint Do?

    A login endpoint takes a user's email and password, checks them against what's stored in the database, and — if they match — returns a JWT auth token. This token is sent back by the frontend on every future request (like fetching or adding notes) so our server knows who is making the request, without asking the user to log in again and again.

    ---

    Step 1: Import What We Need in routes/auth.js

    Open routes/auth.js — the same file where we created the Sign Up route — and make sure these packages are imported at the top.

    const express = require("express");
    const router = express.Router();
    const User = require("../models/User");
    const { body, validationResult } = require("express-validator");
    const bcrypt = require("bcryptjs");
    const jwt = require("jsonwebtoken");
     
    const JWT_SECRET = "CloudNoteBook$ecretKey";
    ---

    Step 2: Define the Login Route with Validation

    We create a new route: POST /api/auth/login. We validate that the email is a valid email address and the password is not empty, using express-validator.

    // ROUTE 2: Login an existing user using: POST "/api/auth/login" — No login required
    router.post(
      "/login",
      [
        body("email", "Enter a valid email").isEmail(),
        body("password", "Password cannot be blank").exists(),
      ],
      async (req, res) => {
        // We will fill this in the next step
      }
    );
    ---

    Step 3: Check Validation Errors and Find the User

    Inside the route handler, first check if there are any validation errors. Then look up the user in MongoDB by their email. If no such user exists, we return an error — without revealing whether it was the email or password that was wrong (this is good security practice).

    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
     
    const { email, password } = req.body;
     
    try {
      let user = await User.findOne({ email });
      if (!user) {
        return res
          .status(400)
          .json({ error: "Please try to login with correct credentials" });
      }
    } catch (error) {
      console.error(error.message);
      res.status(500).send("Internal Server Error");
    }
    ---

    Step 4: Compare the Password with bcrypt

    Since we never store plain-text passwords, we can't check password === user.password directly. Instead, we use bcrypt.compare, which hashes the entered password the same way and checks it against the stored hash.

    const passwordCompare = await bcrypt.compare(password, user.password);
    if (!passwordCompare) {
      return res
        .status(400)
        .json({ error: "Please try to login with correct credentials" });
    }
    ---

    Step 5: Generate and Return the JWT Auth Token

    Once the password matches, we sign a JWT containing the user's MongoDB id and send it back as authtoken. The frontend will store this token and attach it to future requests to prove the user is logged in.

    const data = {
      user: {
        id: user.id,
      },
    };
     
    const authtoken = jwt.sign(data, JWT_SECRET);
    res.json({ success: true, authtoken });
    ---

    Step 6: Full Updated Login Route

    Here is the complete /login route combined into one block, ready to sit alongside your existing /createuser route in routes/auth.js.

    const express = require("express");
    const router = express.Router();
    const User = require("../models/User");
    const { body, validationResult } = require("express-validator");
    const bcrypt = require("bcryptjs");
    const jwt = require("jsonwebtoken");
     
    const JWT_SECRET = "CloudNoteBook$ecretKey";
     
    // ROUTE 2: Login an existing user using: POST "/api/auth/login" — No login required
    router.post(
      "/login",
      [
        body("email", "Enter a valid email").isEmail(),
        body("password", "Password cannot be blank").exists(),
      ],
      async (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
          return res.status(400).json({ errors: errors.array() });
        }
     
        const { email, password } = req.body;
     
        try {
          let user = await User.findOne({ email });
          if (!user) {
            return res
              .status(400)
              .json({ error: "Please try to login with correct credentials" });
          }
     
          const passwordCompare = await bcrypt.compare(password, user.password);
          if (!passwordCompare) {
            return res
              .status(400)
              .json({ error: "Please try to login with correct credentials" });
          }
     
          const data = {
            user: {
              id: user.id,
            },
          };
     
          const authtoken = jwt.sign(data, JWT_SECRET);
          res.json({ success: true, authtoken });
        } catch (error) {
          console.error(error.message);
          res.status(500).send("Internal Server Error");
        }
      }
    );
     
    module.exports = router;
    ---

    Testing the Endpoint in Postman

    Send a POST request to http://localhost:5000/api/auth/login with a JSON body containing email and password. On success, you'll get back an authtoken — save this, we'll use it in the next tutorial to access protected routes like fetching a user's notes.

    Scenario Response Status Code
    Valid email & correct password { success: true, authtoken } 200
    Email not registered { error: "Please try to login with correct credentials" } 400
    Wrong password for existing email { error: "Please try to login with correct credentials" } 400
    Missing/invalid email or password field { errors: [...] } from express-validator 400
    ---

    Features and Learnings:-

  • Understood the purpose of a login endpoint and how JWTs enable stateless authentication.
  • Validated email and password input using express-validator.
  • Looked up an existing user in MongoDB with User.findOne({ email }).
  • Safely compared plain-text and hashed passwords using bcrypt.compare.
  • Returned a generic error message to avoid leaking whether the email or password was wrong.
  • Generated and returned a JWT authtoken on successful login using jsonwebtoken.
  • Tested the /api/auth/login endpoint in Postman and reviewed all response scenarios.
  • Prepared the app for the next step: creating protected routes using JWT-based middleware.
  • 📢 Important Note 📢

    How did you feel about this post?

    😍 🙂 😐 😕 😡

    Was this helpful?

    👍 👎