Authentication in Node.js With Express-js and MongoDB

Authentication in Node.js With Express-js and MongoDB

ยท

6 min read

This article explains how to perform user authentication in Node.js with express-js & MongoDB. In end, I have attached the GitHub repo.

Alright, let's get down to the nitty-gritty and explore Authentication in-depth.

Folder structure:

I am assuming that you have already initiated a node.js project using npm init.

Now create the following folders:

controllers: In this folder, we will keep all the controllers.

models: Mongoose models will be kept in this folder.

routes: Routes will be kept in this folder.

Next, create an entry point file. In my case, it will be index.js.

Install Packages

npm i bcrypt

npm i body-parser

npm i cookie-parser

npm i cors

npm i express

npm i express-jwt

npm i express-validator

npm i jsonwebtoken

npm i mongoose

npm i dotenv

You can install these in one line also, For better understanding, I have split these.

Setup Express server

Create .env file and place the following variables:

DATABASE=mongodb://localhost:27017/newblog
SECRET=babluroy

Explanation:

DATABASE: This is to connect your local MongoDB database. The value may be different as per your setup.

SECRET: This value will be used to encrypt the password. You can put any value here.

After that let's come to the main part which is setting up of server and defining other important packages.

require("dotenv").config();
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors")
const mongoose = require("mongoose");
const cookieParser = require("cookie-parser");

// DB CONNECTION
mongoose.connect(process.env.DATABASE, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
}).then(()=>{
    console.log("DB CONNECTED")
})

app.use(bodyParser.json());
app.use(cookieParser());
app.use(cors());

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

app.listen(port, () => {
    console.log(`app is running at port ${port}`)
});

In the above code, we have set up the express server, body-parser, and related packages. Now you can run nodemon. The server will run on port 2000 as defined in the code.

Create user model

Next, you need to create a model using mongoose inside the model's folder. Models are responsible for creating and reading documents from the underlying MongoDB database.

To create a user model, create a file & name it user.js inside the models folder, and write the following codes.

var mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
    name: {
        type: String,
        maxlength: 32,
        trim: true,
    },
    lastname: {
        type: String,
        maxlength: 32,
        trim: true,
    },
    email: {
        type: String,
        required: true,
        trim: true,
        unique: true,
    },
    password: {
        type: String,
        required: true,
    },
    role: {
        type: Number,
        role: 0,
    },
  },
  {timestamps: true}
)

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

Explanation of the above code:

You can see on the above code that for a user we have defined 6 keys.

role: This key can be used to distinguish between the user & admin. 0 means normal user and 1 means admin.

timestamp: This key will store the timestamp of the signing up.

Other keys are self-explanatory.

Authentication controller

Now let's come to the most important part which is defining the authentication controller. It will contain the main logic for signup & login.

First of all, create a file inside the controllers folder as auth.js and write the following codes:

const { body, validationResult } = require('express-validator');
const jwt = require("jsonwebtoken");
var { expressjwt } = require("express-jwt");
const bcrypt = require("bcrypt");
const User =  require("../models/user");

exports.signup = (req, res) => {
   const errors = validationResult(req);
   const {email, password, name, lastname} = req.body;

   if(!errors.isEmpty()){
     return res.status(422).json({
      error: errors.array()[0].msg,
     })
   }

   User.findOne({email: email}).then((user)=>{
      if(user){
        return res.status(400).json({
            error: "E-mail is already registered"
        })
      }
      const newUser = new User(req.body)
      bcrypt.genSalt(10, (err, salt) => {
        bcrypt.hash(newUser.password, salt, function (err, hash) {
           if (err) throw err;
           newUser.password = hash;
           newUser.save()
           .then((user)=> res.status(200).json(user))
           .catch((error)=> res.status(400).json(error));
        });
    })
   })
   .catch((error)=>{
     return res.status(400).json({error: error});
   })
}


exports.signin = (req, res) => {
  const errors = validationResult(req);
  const {email, password} = req.body;

  if(!errors.isEmpty()){
    return res.status(422).json({
      error: errors.array()[0].msg,
    })
  }

  User.findOne({email}).then((user)=>{
    if(!user){
      return res.status(400).json({
        error: "User not found"
      })
    }

    bcrypt.compare(password, user.password).then((isCorrect) => {
      if(isCorrect){
        const payload = {
          id: user.id,
          email: user.email, 
        }
        jwt.sign(
          payload,
          process.env.SECRET,
          { expiresIn: 3600 },
          (err, token) => {
            const {name, role, email, _id} = user;
            res.json({
              token: "Bearer " + token,
              name: name,
              role: role,
              email: email,
              _id: _id,
            });
            if (err) {
              res.json({
                success: false,
                error: err,
              });
            }
          }
        );
        } else {
           res.status(400).json({
             error: "Password doesn't match"
           })
        }
    }).catch((error)=>{
      res.status(500).json({
        error: error,
      })
    });
  });
}

exports.signout = (req, res) => {
  res.clearCookie("token");
  res.json({
    status: true,
    message: "User signed-out successfully",
  });
}

exports.isSignedIn = expressjwt({
  secret: process.env.SECRET,
  algorithms: ["sha1", "RS256", "HS256"],
  userProperty: "auth",
});

Explanation of each controller function:

signup: This function will handle the signup of the user. It will take the values & express validator will check for any errors, if no errors are found then it will check if the email is already registered, if not registered before then it will go ahead to the next process which is encrypting the password of the user & save user details along with the encrypted password.

signin: This function will handle the login. Just like signup, it will take the values & check for any errors using express validator. Next, it will check if the email exists in the database. If it exists then it will check the password entered by the user using bcrypt then if it matches it will generate jwt token.

signout: As the name suggests, it will handle signout/logout by clearing cookies.

isSignedIn: This function can check if the user is signed in.

Define Routes:

Now let's create the authentication routes.

First of all, create auth.js file inside the routes folder and write the following codes:

var express = require("express");
var router = express.Router();
const {check, validationResult} = require("express-validator")
const {signup, signin, isAuthenticated, signout} =  require("../controllers/auth") 

router.post('/signup',
[
  check("name", "Name should be atleast 3 character").isLength({min: 3}),
  check("email", "E-mail is required").isEmail(),
  check("password", "Password should be atleast 8 character").isLength({min: 8}),
],
signup
)

router.post('/signin',
[
  check("email", "Email is required").isEmail(),
  check("password", "password field is required").isLength({ min: 1 }),
],
signin
)
router.get('/signout', signout);

module.exports = router;

Explanation of the above codes:

We have defined 3 routes:

/signup , /signin and /signout and imported the controllers & placed each of them in the proper routes. Also, I have defined express validators.

Defining auth route in entry file

The next step is to import auth routes into the main entry file.

Go to the index.js file and write the following code:

const auth = require("./routes/auth")
app.use("/api", auth)

The entire file will look like this:

Well done! ๐ŸŽ‰๐ŸŽ‰ You have successfully developed the authentication backend.

Let's test the API using Postman:

Signup:

As you can see in the above screenshot, a user has been successfully created

Signin:

You can see in the above screenshot, the login is successfully done & API has returned the Bearer token along with other details.

Github repo: Click here

Thanks for reading. In the next article, I will cover how you can integrate these API's in the front-end.

ย