How to build an Authentication system in Node.js with JWT Token

Hello programmers, In this article, you will learn about how we can build a user authentication system in Node.js where users can sign up and log in to their account in Nodejs.

So before we get started building the same let’s learn about Javascript Web Token or in short form JWT which we will be using in this tutorial.

What is JWT

JSON Web Tokens are used for representing claims securely between two parties. Basically, it is a token that contains three distinct parts. First, it has a header which decides the algorithm to encode and decode this. It has a payload which is all the information stored in the token and lastly it has a signature that allows us to verify that the client hasn’t tampered the token.

How it verifies the token is it actually takes the first two portions of the token and it do base64 encoding and then hash it with the particular algorithm and a secret key and match it with this section. If matched it is verified, the user has not tampered with the data.

Prerequisites

To follow this tutorial you would need working knowledge of the following

  1. JavaScipt
  2. Node.js
  3. A basic understanding of MongoDB
  4. Postman and how to use it

Step 1 – Creating directory and initializing npm

First, open VS Code and create an empty directory. Here in this tutorial, we will name the directory “authentication”. Now initialise npm by writing following code in terminal.

npm init -y

Now we will be having a package.json file that contains information, scripts, and a dependency list of our app.

Step 2 – Creating files and directories

We will create index.js and app.js in the root directory. We will be requiring a model, and config directory so we will be creating them. Now in the model folder, we will be defining our User model so we will create a user.js file. In the config folder we will be writing code to connect to our database, so create a file named database.js in the same.

Our file and folder structure will be something like this.

Creating routes for signup and login

Step 3 – Installing dependencies

We will install express to build our server. We will also install the following dependencies in our app – mongoose, jsonwebtoken, bcryptjs . I will be explaining them as we proceed further in this tutorial. For development purpose, we will install nodemon which will restart our server every time we make any changes in our code. Run the following code in the terminal.

npm i express mongoose express jsonwebtoken dotenv bcryptjs nodemon

Step 4 – Creating our server and connecting to the database

Now we will write code to build our server. Add this code in the app.js file

const express = require("express");
const app = express();

// To parse json data
app.use(express.json());


module.exports = app;

In the index.js file add the following code

const http = require("http");
const app = require("./app");
const server = http.createServer(app);

const port = process.env.PORT || 5000;

// listening to server
server.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

We will be using environment variable so for that we will install dotenv dependency by writing the following code

npm i dotenv

Now we will write code to connect to our database so add the following lines of code in database.js. We are using mongoose for the same.

const mongoose = require("mongoose");

const { MONGO_URI } = process.env;

exports.connect = () => {
  // Connecting DB
  mongoose
    .connect(MONGO_URI, {
      useUnifiedTopology: true,
      useNewUrlParser: true,
      useCreateIndex: true,
      useFindAndModify: false,
    })
    .then(() => {
      console.log("Connected to the database");
    })
    .catch((err) => {
      console.log("DB connection failed.");
      console.error(err);
      process.exit(1);
    });
};

We will update our app.js file with the following lines of code to add DB connection.

const dotenv = require("dotenv");
dotenv.config(); 
const db = require("./config/database"); 
db.connect();
const express = require("express");
const app = express();

// To parse json data
app.use(express.json());

module.exports = app;

We will create .env file in the root directory for adding environment variables.

To start our server we add the following script in our package.json file.

"scripts": {
    "dev": "nodemon index.js",
}

Now we will be needing the MONGO_URI to connect to our database. For that go to the MongoDB website register there and create a free cluster and get the MONGO_URI. We are not discussing how to do the same as we have assumed users have a working knowledge of MongoDB and how to create a database. Add the MONGO_URI in the .env file by writing MONGO_URI = “…the uri”

Now we can start our server by executing “npm run dev” in the terminal. The database should get connected and our server should be started.

Step 5 – Creating user model

Now we will be defining our user schema and making user model to register user and validating them at the time of login.

Add the following code in user.js file inside model folder

const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  f_name: { type: String, default: null },
  l_name: { type: String, default: null },
  email: { type: String, unique: true },
  password: { type: String },
  token: { type: String },
});

module.exports = mongoose.model("user", userSchema);

Step 6 – Creating routes for signup and login

Now we are going to create routes for signing up and logging in.

Goto app.js, import user context, require bcrypt and jwt, and create a route to register users. This will be a post route as we are going to accept user details and add user to the database.

//importing user model
const User = require("./model/user");

// Register a User
app.post("/register", (req, res) => {
// code for register goes here...
});

We will be getting user information from the request object. We will use that and check if those data are valid. After that, we will check if the user is already present in the database. If it is present then we will send an error stating the user is already registered. Otherwise, first, we will first hash the password using bcrypt then we will create a new user object, create a user token and add it to the object and save it to the database. Edit the register route as the code given below. Read the comments to get more insight into what each piece of code does. Remember to enclose the code in a try-catch block.

app.post("/register", async (req, res) => {
    try {
        // Retriving user details
        const { first_name, last_name, email, password } = req.body;

        // Validating input
        if (!(email && password && first_name && last_name)) {
            res.status(400).send("Please enter all fields");
        }

        // Checking if user already exist
        const oldUser = await User.findOne({ email });

        if (oldUser) {
            return res.status(409).send("User Already Exist");
        }

        //Here we are using bcrypt to encrypt the password.
        encryptedPassword = await bcrypt.hash(password, 14);

        // Creating user in our database
        const user = await User.create({
            first_name,
            last_name,
            email,
            password: encryptedPassword,
        });

        // Creating token
        const token = jwt.sign(
            { user_id: user._id, email },
            process.env.TOKEN_KEY,
            {
                expiresIn: "12h",
            }
        );

        // saving user token
        user.token = token;

        // sending back new user
        res.status(201).json(user);
    } catch (err) {
        console.log(err);
    }
});

Add a token key in the env file which can be any random string. For instance TOKEN_KEY = “somerandomtext”.
Now we are done with creating the register route. We will now use Postman to test our register API. Give dummy data to test the API.

Creating routes for signup and login node.js

If we get a response, something like this then we are good to go. Now we can proceed further to create our login route.

This route will be a post route as we will be sending user credentials to get them validated. In this route our workflow will be like this – We will get user details. Check if any of them is invalid. After that, we will find the user using the email provided. We will match the hashed password, if validated we will create and save token and send back user data as a response. The code goes something like this

app.post("/login", async (req, res) => {
    try {
        //user input
        const { email, password } = req.body;

        // Check if any of the input is blank.
        if (!(email && password)) {
            res.status(400).send("All input is required");
        }
        // Validate user
        const user = await User.findOne({ email });

        if (user && (await bcrypt.compare(password, user.password))) {
            // Create token
            const token = jwt.sign(
                { user_id: user._id, email },
                process.env.TOKEN_KEY,
                {
                    expiresIn: "2h",
                }
            );

            // save user token
            user.token = token;

            // sending back user
            res.status(200).json(user);
        }
        res.status(400).send("Invalid Credentials");
    } catch (err) {
        console.log(err);
    }
});

We will now test our API using Postman.

How to build an Authentication system in Node.js with JWT Token

So we have successfully made an authentication system in Node.js using JWT. Thank you for reading.

You may also learn

Leave a Reply

Your email address will not be published. Required fields are marked *