Authentication System in Node Js, Express and MongoDB

Authentication System in Node Js, Express and MongoDB

Authentication is a process in which the credentials provided are compared to those on file in a database of authorized users' information on a local operating system or within an authentication server. If the credentials match, the process is completed and the user is granted authorization for access.

To build an authentication system we will use:

  • Node Js
  • Express
  • Mongo DB

We, first of all, need to initialise our backend application and make sure node js is installed on your system. Create a folder to contain your project and open your terminal and run the following command.

npm init

image.png

With this, a package will be created in your local folder and we are ready to build our application. First of all, we will install the necessary dependencies required to build our application.

"dependencies": {
    "bcryptjs": "^2.4.3",
    "cookie-parser": "^1.4.6",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^6.7.1"
  }

Copy and paste the following in your package.json and run npm install to install all the dependencies, we'll use each of them in our application.

Before starting to code our application, we'll create a rough mind map of our application is structured.

rough map.jpg

Let's create the user model. Create a folder model and add a file user.js to it. We will use mongoose to create our user schema.

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    firstName:{
        type:String,
        default:null
    },
    lastName:{
        type:String,
        default:null

    },
    email:{
        type:String,
        unique:true
    },
    password:{
        type:String
    },
    token:{
        type:String
    }
})

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

Before connecting with the database, we need to secure our sensitive information and hence we will use dotenv for the following purpose. Create a .env file to add the Mongo DB URL.

Now, create a config folder and a file db.js to establish a database connection.

const mongoose = require('mongoose');

const {MONGODB_URL} = process.env;

exports.connect = () => {
    mongoose.connect(MONGODB_URL,{
        useNewUrlParser: true,
        useUnifiedTopology: true
    })
    .then(() => console.log('DB Connection Succesfull'))
    .catch((err) => {
        console.log('MongoDB Connection Failed');
        console.log(err.message);
        process.exit(1);
    })
};

We create a file app.js to write the logic of the application and initialise the routes. If we are using the environment variables in our application we need to require them in our application. Also, we need to use our database in this file thus connecting to the database here.

require('dotenv').config();
require('./config/db').connect();

Doing all necessary imports.

const express = require('express');
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');

Initialising our app and importing the user model.

const app = express();
const User = require('./model/user');

express.json() is a built in middleware function in Express starting from v4.16.0. It parses incoming JSON requests and puts the parsed data in req.body.

app.use(express.json());

Setting up a home route of our application

app.get("/", (req,res)=>{
    res.send("<h1>Home Route</h1>")
})

Setting up our register route.

// register route
app.post('/register', async (req,res) => {

    try {

        // fetching data 
        const {firstName, lastName, email, password} = req.body;

         // validating data
        if(!(firstName && lastName && email && password)){

            res.status(400).json({
                "message":"insufficient data"
            });
        }

        // checking if user already exists
        const existUser = await User.findOne({email});
        if(existUser){
            res.status(401).json({
                email,
                "message":"already exist"
            })
        }

        // encrypting the password
        const salt = await bcrypt.genSalt(10);
        const myEncrypPassword = await bcrypt.hash(password,salt);

        // creating user in database
        const user = await User.create({
            firstName,
            lastName,
            email, 
            password:myEncrypPassword
        });

        // create a token and send it to user

        const token = jwt.sign(
            {
                id:user._id,
                email:user.email,
            },
            "shhhhh",
            {  expiresIn: "2h" }
        );

        user.token = token;
        // don't want to send the password
        user.password = undefined;

        res.status(201).json(user);


    } catch (error) {
        console.log(error);
    }

});

Similarly, setting up our login route.

app.post('/login', async(req,res) => {

    try {
        const {email, password} = req.body;

        if(!(email && password)){
            res.status('401').json({
                "message":"insufficient data"
            });
        }

        const user = await User.findOne({email});
        if(!user){
            res.status(401).json({"message":"does not exist"});
        }

        // matching the password

        if(user && (await bcrypt.compare(password,user.password))){
            // credentials are correct and thus logging in user
            const token = jwt.sign({
                id:user._id,
                email
            }, 'shhhhh', 
            {
                expiresIn: "2h"
            }
            );

            user.password  = undefined;
            user.token = token;

            const options = {
                expires: new Date(Date.now() + 3*24*60*60*1000 ),
                httpOnly:true,
            }

            res.status(200).cookie("token", token, options).json({
                success: true,
                user
            });

        }

        res.status(401).json({
            "message":"invalid credentials"
        });

    } catch (error) {
      console.log(error);
    }

})

At the end we need to export our app module so that it can be used anywhere in the application.

module.exports = app;

As per the package of our application, the entry file is index.js, so we need to listen in listen to a server in the file index.js.

const app = require('./app');
const {PORT} = process.env;
app.listen(PORT, ()=> console.log('Listening on PORT:'+ PORT));

With this, all necessities of our application are completed. We need to sign up for MongoDB and create a free cluster there. Follow the steps on the MongoDB website and generate the connection string. Put the connection string in the .env file.

Make sure to have correct Database Access Rights and Network Access Rights. If all the steps are taken correctly then we are ready to move forward.

Run the following command to start your server.

node index.js

Make sure you are on the right path, you need to be in the path containing the package.json file.

If everything went up right, you should see this printed on your console.

image.png

You can now use Postman to test your application.

Registering new users on the register route with POST request.

image.png

Trying to log in the newly registered user.

image.png

With this our basic authentication system is complete, for reference to the postman collection and the code files, the github link of the repo is provided.

Authentication System

Hit a like and share if you found it useful.