DEV Community

Cover image for Authentication in NodeJS With Express and Mongo - CodeLab #1
Deepak Kumar
Deepak Kumar

Posted on • Edited on

Authentication in NodeJS With Express and Mongo - CodeLab #1

I am starting a CodeLab Series in which I will building something cool and sharing with the community.

Today, We are going to implement Authentication API in Node using JWT, express, and MongoDB.

I advise you to follow the table of content and don't miss any steps. I will provide the full app code link at the end.

Table of Content


1. Introduction

Authentication - It is a process of identifying user identity.

User Authentication contains various steps, please check out this flowchart to know more. We will be using this flow to build the authentication system in our application.

Alt Text


2. Prerequisites

You should have prior knowledge of javascript basics, nodejs. Knowledge of ES6 syntax is a plus. And, at last nodejs should be installed on your system.


3. Packages Required

You will be needing these following 'npm' packages.

  1. express
    Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications

  2. express-validator
    To Validate the body data on the server in the express framework, we will be using this library. It's a server-side data validation library. So, even if a malicious user bypasses the client-side verification, the server-side data validation will catch it and throw an error.

  3. body-parser
    It is nodejs middleware for parsing the body data.

  4. bcryptjs
    This library will be used to hash the password and then store it to database.This way even app administrators can't access the account of a user.

  5. jsonwebtoken
    jsonwebtoken will be used to encrypt our data payload on registration and return a token. We can use that token to authenticate ourselves to secured pages like the dashboard. There would also an option to set the validity of those token, so you can specify how much time that token will last.

  6. mongoose
    Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Mongoose supports both promises and callbacks.


4. Initiate Project

We will start by creating a node project. So, Create a new folder with the name 'node-auth' and follow the steps below. All the project files should be inside the 'node-auth' folder.

npm init

Enter fullscreen mode Exit fullscreen mode

npm init will ask you some basic information about project. Now, you have created the node project, it's time to install the required packages. So, go ahead and install the packages by running the below command.

npm install express express-validator body-parser bcryptjs jsonwebtoken mongoose --save
Enter fullscreen mode Exit fullscreen mode

Now, create a file index.js and add this code.

// File : index.js

const express = require("express");
const bodyParser = require("body-parser");

const app = express();

// PORT
const PORT = process.env.PORT || 4000;

app.get("/", (req, res) => {
  res.json({ message: "API Working" });
});


app.listen(PORT, (req, res) => {
  console.log(`Server Started at PORT ${PORT}`);
});

Enter fullscreen mode Exit fullscreen mode

If you type node index.js in the terminal, the server will start at PORT 4000.

You have successfully set up your NodeJS app application. It's time to set up the database to add more functionality.


5. Setup MongoDB Database

We will be using MongoDB Database to store our users. You can use either a cloud MongoDB server or a local MongoDB server.

In this CodeLab, we will be using a Cloud MongoDB server known as mLab.

So, First, go ahead and signup on mLab. And follow the below steps.

  1. After successful signup, Click on Create New Button on home page.

  2. Now, choose any cloud provider for example AWS. In the Plan Type choose the free SandBox and then Click on the Continue button at the bottom right.

  3. Select the region(any) and click continue.

  4. Enter a DB name(any). I am using node-auth. Click continue and then submit the order on the next page. Don't worry it's free of cost.

  5. Now, You will be re-directed to the homepage. Select your DB i.e node-auth.

  6. Copy the standard MongoDB URI.

  7. Now, you need to add a user to your database. From the 5 tabs below, click on Users and add a user by clicking on Add Database User.

Now, you have got your database user. Replace the && with your DB username and password.

mongodb://<dbuser>:<dbpassword>@ds257698.mlab.com:57698/node-auth

Enter fullscreen mode Exit fullscreen mode

So, the Mongo Server Address(MongoURI) should look like this. Don't try to connect on my MongoURI. It's just a dummy username & password. 😄😄

mongodb://test:hello1234@ds257698.mlab.com:57698/node-auth

Enter fullscreen mode Exit fullscreen mode

Now, you have the mongoURI you are ready to connect your node-auth app to the database. Please follow the below steps.


6. Configure User Model

Let's go and first create a config folder. This folder will keep the database connection information.

Create a file named: db.js in config

//FILENAME : db.js

const mongoose = require("mongoose");

// Replace this with your MONGOURI.
const MONGOURI = "mongodb://testuser:testpassword@ds257698.mlab.com:57698/node-auth";

const InitiateMongoServer = async () => {
  try {
    await mongoose.connect(MONGOURI, {
      useNewUrlParser: true
    });
    console.log("Connected to DB !!");
  } catch (e) {
    console.log(e);
    throw e;
  }
};

module.exports = InitiateMongoServer;

Enter fullscreen mode Exit fullscreen mode

Now, we are done the database connection. Let's create the User Model to save our registered users.

Go ahead and create a new folder named model. Inside the model folder, create a new file User.js.

We will be using mongoose to create UserSchema.

User.js


//FILENAME : User.js

const mongoose = require("mongoose");

const UserSchema = mongoose.Schema({
  username: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  },
  createdAt: {
    type: Date,
    default: Date.now()
  }
});

// export model user with UserSchema
module.exports = mongoose.model("user", UserSchema);

Enter fullscreen mode Exit fullscreen mode

Now, we are done with Database Connection, User Schema. So, let's go ahead and update our index.js to connect our API to the database.

index.js

const express = require("express");
const bodyParser = require("body-parser");
const InitiateMongoServer = require("./config/db");

// Initiate Mongo Server
InitiateMongoServer();

const app = express();

// PORT
const PORT = process.env.PORT || 4000;

// Middleware
app.use(bodyParser.json());

app.get("/", (req, res) => {
  res.json({ message: "API Working" });
});


app.listen(PORT, (req, res) => {
  console.log(`Server Started at PORT ${PORT}`);
});


Enter fullscreen mode Exit fullscreen mode

Congratulations 😄😄 , You have successfully connected your app to the MongoDB server.

Now, the next thing we have to do is make a /user/signup route to register a new user. We will see this in the next section.


7. User Signup

The Route for user registration will be '/user/signup'.

Create a folder named routes. In the 'routes' folder, create a file named user.js

routes/user.js


// Filename : user.js

const express = require("express");
const { check, validationResult} = require("express-validator/check");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const router = express.Router();

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

/**
 * @method - POST
 * @param - /signup
 * @description - User SignUp
 */

router.post(
    "/signup",
    [
        check("username", "Please Enter a Valid Username")
        .not()
        .isEmpty(),
        check("email", "Please enter a valid email").isEmail(),
        check("password", "Please enter a valid password").isLength({
            min: 6
        })
    ],
    async (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({
                errors: errors.array()
            });
        }

        const {
            username,
            email,
            password
        } = req.body;
        try {
            let user = await User.findOne({
                email
            });
            if (user) {
                return res.status(400).json({
                    msg: "User Already Exists"
                });
            }

            user = new User({
                username,
                email,
                password
            });

            const salt = await bcrypt.genSalt(10);
            user.password = await bcrypt.hash(password, salt);

            await user.save();

            const payload = {
                user: {
                    id: user.id
                }
            };

            jwt.sign(
                payload,
                "randomString", {
                    expiresIn: 10000
                },
                (err, token) => {
                    if (err) throw err;
                    res.status(200).json({
                        token
                    });
                }
            );
        } catch (err) {
            console.log(err.message);
            res.status(500).send("Error in Saving");
        }
    }
);

module.exports = router;

Enter fullscreen mode Exit fullscreen mode

Now, we have created the user registration in 'routes/user.js'. So, we need to import this in index.js to make it work.

So, the updated index file code should look like this.
index.js


const express = require("express");
const bodyParser = require("body-parser");
const user = require("./routes/user"); //new addition
const InitiateMongoServer = require("./config/db");

// Initiate Mongo Server
InitiateMongoServer();

const app = express();

// PORT
const PORT = process.env.PORT || 4000;

// Middleware
app.use(bodyParser.json());

app.get("/", (req, res) => {
  res.json({ message: "API Working" });
});


/**
 * Router Middleware
 * Router - /user/*
 * Method - *
 */
app.use("/user", user);

app.listen(PORT, (req, res) => {
  console.log(`Server Started at PORT ${PORT}`);
});

Enter fullscreen mode Exit fullscreen mode

Let's start the user registration using postman. A postman is a tool for API testing.

Alt Text


8. User Login

Now, it's time to implement the Login router which will be mounted on '/user/login'.

Here is the code snippet for login functionality. Add the below code snippet in user.js


router.post(
  "/login",
  [
    check("email", "Please enter a valid email").isEmail(),
    check("password", "Please enter a valid password").isLength({
      min: 6
    })
  ],
  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({
          message: "User Not Exist"
        });

      const isMatch = await bcrypt.compare(password, user.password);
      if (!isMatch)
        return res.status(400).json({
          message: "Incorrect Password !"
        });

      const payload = {
        user: {
          id: user.id
        }
      };

      jwt.sign(
        payload,
        "randomString",
        {
          expiresIn: 3600
        },
        (err, token) => {
          if (err) throw err;
          res.status(200).json({
            token
          });
        }
      );
    } catch (e) {
      console.error(e);
      res.status(500).json({
        message: "Server Error"
      });
    }
  }
);

Enter fullscreen mode Exit fullscreen mode

Alt Text

9. Get LoggedIn User

Now, your User Signup and User Login is working, and you are getting a token in return.

So, our next task will be to Retrieve the LoggedIn user using the token. Let's go and add this functionality.

The /user/me route will return your user if you pass the token in the header. In the file route.js, add the below code snippet.

/**
 * @method - GET
 * @description - Get LoggedIn User
 * @param - /user/me
 */


router.get("/me", auth, async (req, res) => {
  try {
    // request.user is getting fetched from Middleware after token authentication
    const user = await User.findById(req.user.id);
    res.json(user);
  } catch (e) {
    res.send({ message: "Error in Fetching user" });
  }
});
Enter fullscreen mode Exit fullscreen mode

As you can see, we added the auth middleware as a parameter in the /user/me GET route, so let's define auth function.

Go ahead and create a new folder named middleware. Inside this folder, create a file named auth.js

This auth middleware will be used to verify the token, retrieve user based on the token payload.

middleware/auth.js


const jwt = require("jsonwebtoken");

module.exports = function(req, res, next) {
  const token = req.header("token");
  if (!token) return res.status(401).json({ message: "Auth Error" });

  try {
    const decoded = jwt.verify(token, "randomString");
    req.user = decoded.user;
    next();
  } catch (e) {
    console.error(e);
    res.status(500).send({ message: "Invalid Token" });
  }
};
Enter fullscreen mode Exit fullscreen mode

Yayy !! You have successfully created an authentication API in nodejs. Now, You can go ahead and test the /user/me endpoint after logging in.

How to Test the application?

PostMan is required for Testing the API. If you don't have PostMan installed first, install it.

  1. First, register the user or login if you are already registered.

  2. From step 1, you will get a token. Copy that token and put in the header.

  3. Hit Submit

Here is a preview of testing.

Alt Text

10. Conclusion

In this CodeLab - 1, we covered authentication in nodejs using express, jsonwebtoken and MongoDB. We learned about how to write middleware.

Here is the link of full code for this CodeLab: https://github.com/dipakkr/node-auth.

Also, I would love to know what else you want to cover in the next CodeLabs.


I am glad you read till here, Please give some ❤️ ❤️ !!

If you face in problem in running/understanding this application, let me know in the comments. Don't forget to give your feedback. Getting feedback helps me improve.

Subscribe to my email newsletter and stay updated!

I write about new stuff almost daily. Please follow me on Twitter | Instagram

Latest comments (74)

Collapse
 
pratham_161 profile image
Prathamesh tanksale

Hii I'm ok with token part but i want to send all the user info except password along with the token. how can i do that?

Collapse
 
pratham_161 profile image
Prathamesh tanksale • Edited

hii I'm ok with the jwt part but now I want send all the user info except password from the server so how can I do that

Collapse
 
rboswellj profile image
rboswellj

I realize this is an older post, but this might help some people who stumbled on it, as it really is a very good guide. If you're stuck at the first signup tests you probably just need to remove some deprecated config flags in your mongo connection. You can remove the whole config, as all of those flags are now implied in later versions and explicitly calling them seems to cause problems. I'm referring to this in the mongoose.connect: {
useNewUrlParser: true
}

Just remove it and it should connect. Took me way too long to find that.

Collapse
 
shourya2002geek profile image
Shourya Gupta

woowwww

Collapse
 
shivain07 profile image
Shivain

json web token invalid how can i resolve this error /

JsonWebTokenError: invalid signature
   at C:\Users\shiva\Documents\mongotut\node_modules\jsonwebtoken\verify.js:133:19
   at getSecret (C:\Users\shiva\Documents\mongotut\node_modules\jsonwebtoken\verify.js:90:14)
   at Object.module.exports as verify
   at module.exports (C:\Users\shiva\Documents\mongotut\controllers\auth.js:10:21)
   at Layer.handle as handle_request
   at next (C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\route.js:137:13)
   at Route.dispatch (C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\route.js:112:3)
   at Layer.handle as handle_request
   at C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\index.js:281:22
   at Function.process_params (C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\index.js:335:12)
   at next (C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\index.js:275:10)
   at Function.handle (C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\index.js:174:3)
   at router (C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\index.js:47:12)
   at Layer.handle as handle_request
   at trim_prefix (C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\index.js:317:13)
   at C:\Users\shiva\Documents\mongotut\node_modules\express\lib\router\index.js:284:7

Collapse
 
mikeypenny profile image
Miguel Sandoval

You missed the name of the user file when you are creating the '/me' route, this supossed to be in user, also, if you are using a middleware you should explain to the rookies how to use this module, you are ecporting the middleware but didin't explain how to use it.

Collapse
 
shreyasbhaktharam profile image
shreyasb20

Great article! I was able to get the code up and running in no time :)

Collapse
 
mikeypenny profile image
Miguel Sandoval

Hey great post, how you do it to logout the user?

Collapse
 
florencemawusi profile image
FlorenceMawusi

Thanks a lot! I use Atlas instead of mLab.

Collapse
 
krishnay2000 profile image
Krishnaraj Yadav

TypeError: Cannot read property 'id' of null.
I am getting the above error while signup. Infact, the data is saving in mongoDB but it is returning error status 500.Below is where there is problem with the code i think so.Tried many solution but none is working Please help to resolve this.
const payload = {
user: {
id: user.id
}
};