Home > Web Front-end > JS Tutorial > Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT

Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT

Susan Sarandon
Release: 2025-01-11 14:34:43
Original
576 people have browsed it

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.

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.

Introduction

In this article, we will explore secure authentication in Node.js, using bcrypt.js 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.

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.

Setting Up Your Node.js Project Environment

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

1.`npm init -y`
2.`npm install express mongoose jsonwebtoken bcryptjs dotenv cors cookie-parser`
3.`npm install nodemon -D`

Copy after login
Copy after login
Copy after login
Copy after login

Now modify the package.json file. Add scripts like my code and type.

"scripts": {
    "dev": "nodemon backend/index.js",
    "start": "node backend/index.js"
  },
"type": "module",

Copy after login
Copy after login
Copy after login
Copy after login

Basic Server Setup

Next, we'll set up a basic Express server. Create a file named index.js . 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.

import express from "express";
const app = express();

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

app.listen(8000, () => {
  console.log("Server is running on PORT 8000");
});
Copy after login
Copy after login
Copy after login
Copy after login

Setting Up Basic Authentication Routes

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

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: /signup, /login, and /logout, 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.

1.`npm init -y`
2.`npm install express mongoose jsonwebtoken bcryptjs dotenv cors cookie-parser`
3.`npm install nodemon -D`

Copy after login
Copy after login
Copy after login
Copy after login

Now change the index.js adding the auth route to test out your end points.

"scripts": {
    "dev": "nodemon backend/index.js",
    "start": "node backend/index.js"
  },
"type": "module",

Copy after login
Copy after login
Copy after login
Copy after login

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.

Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT

Similarly you can see the other routes like Logout and SignUp.

So, Our basic app is ready...now make it a robust and a proper authentication system.

Creating the User Model with Mongoose Schema

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

import express from "express";
const app = express();

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

app.listen(8000, () => {
  console.log("Server is running on PORT 8000");
});
Copy after login
Copy after login
Copy after login
Copy after login

Connecting to MongoDB with Mongoose

Now let's connect to our database. We'll create a folder named db and inside it, a file called connectDB.js. In this file, we'll define an asynchronous function connectMongoDB that tries to connect to a MongoDB database using Mongoose. It gets the database connection string from the MONGO_URI 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.

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) => {
  // 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) => {
  // 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) => {
  // 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;
Copy after login
Copy after login
Copy after login

Now to use MONGO_URI we have to make it in .env file. Here I have used local mongoDB setup connection string. If you want then you can also use mongoDB atlas.

import express from "express";
import authRoute from "./routes/authRoutes.js";
const app = express();

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

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

app.listen(8000, () => {
  console.log("Server is running on PORT 8000");
});
Copy after login
Copy after login

Signup function

Now make the signup function. For this 1st make a folder controller and there file authController.js

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;
Copy after login

First, it extracts fullName, username, email, and password from the request body. It validates the email format using a regular expression, returning a 400 status if the format is invalid.

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.

The password is then securely hashed using bcrypt. A new User instance is created with the provided data and saved to the database.

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.

To make this function active you have to import these

1.`npm init -y`
2.`npm install express mongoose jsonwebtoken bcryptjs dotenv cors cookie-parser`
3.`npm install nodemon -D`

Copy after login
Copy after login
Copy after login
Copy after login

Notice something? a new thing called generateTokenAndSetCookie...lets see its code...make a folder utils and there generateTokenAndSetCookie.js.

"scripts": {
    "dev": "nodemon backend/index.js",
    "start": "node backend/index.js"
  },
"type": "module",

Copy after login
Copy after login
Copy after login
Copy after login

The **generateTokenAndSetCookie **function creates a JWT and stores it in a cookie for user authentication.

JWT Generation:

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

Setting the Cookie:

The token is then stored in a cookie on the user's browser. The cookie is configured with several security attributes:

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

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

Here we have to add another environment variable JWT_SECRET in .env. You can add any type of mix of number and string like this.

import express from "express";
const app = express();

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

app.listen(8000, () => {
  console.log("Server is running on PORT 8000");
});
Copy after login
Copy after login
Copy after login
Copy after login

Now our signUp function is complete..so make its route now.

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) => {
  // 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) => {
  // 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) => {
  // 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;
Copy after login
Copy after login
Copy after login

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

Here is updated code of index.js

1.`npm init -y`
2.`npm install express mongoose jsonwebtoken bcryptjs dotenv cors cookie-parser`
3.`npm install nodemon -D`

Copy after login
Copy after login
Copy after login
Copy after login

So. now it’s time to test our signup function in Postman. Let’s see if it’s working or not.

So, here is the results.

Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT

Here you can see it’s working properly and you can check it your mongoDB database as well.

Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT

Login function

Now make the login function. Let’s go again to our authController.js file

"scripts": {
    "dev": "nodemon backend/index.js",
    "start": "node backend/index.js"
  },
"type": "module",

Copy after login
Copy after login
Copy after login
Copy after login

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 generateTokenAndSetCookie, and responds with a success message, indicating the user is logged in successfully.

Let’s add our login route in authRoutes.js

import express from "express";
const app = express();

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

app.listen(8000, () => {
  console.log("Server is running on PORT 8000");
});
Copy after login
Copy after login
Copy after login
Copy after login

Let’s test it in Postman.

Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT
Here you can see it is successfully showing Logged in.

Logout function

Okay. Now the last function i.e. the logout function. Let’s implement this. It’s pretty simple.

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) => {
  // 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) => {
  // 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) => {
  // 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;
Copy after login
Copy after login
Copy after login

The logout controller securely logs out a user by clearing the JWT cookie from the client's browser using res.cookie, setting its value to an empty string and its maxAge 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.

Add this route to our authRoute.js

import express from "express";
import authRoute from "./routes/authRoutes.js";
const app = express();

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

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

app.listen(8000, () => {
  console.log("Server is running on PORT 8000");
});
Copy after login
Copy after login

okay. Let’s test our last feature, if it’s working fine or not.

Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT

Oh!…It’s working super fine. ??

So, now our complete backend of this authentication is ready. ??

My npm package

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 here.

Github Repository

You can get my all above code here in this github repo here.

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 ??.

Conclusion

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.

The above is the detailed content of Mastering Secure Authentication in Node.js: Login/Logout with bcrypt.js and JWT. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template