10 minutes read

Node.js and Bcrypt Authentication with MongoDB

Summary:Bcrypt is a simple hashing library for Node.js. This tutorial focus on Node.js and Bcrypt authentication using MongoDB for secure login and signup.

There are multiple libraries for authentication in Node.js like Passport and Bcrypt. You need to choose one depending on the size, type and complexity of the application.

It is a well-known fact that complexity increases security risk because there are high chances that we will miss some minor details.

Bcrypt is a simple way for authentication in Node.js. So, if you want to implement only the username and password authentication method, it is a wise decision to go with Bcrypt.

What is Bcrypt?

Bcrypt is a password hashing function which encrypts your password.

We do not store password as plain text in the database,  it is a critical security risk. The benefits of hashing passwords is that even if any data breach occurs, the hacker will again need to brute force the hash to regain the password which is again a hard task.

You can read more about why we should not store password as plain text.

In Node.js we use Bcrypt.js to encrypt passwords before saving it into the database. The reason is that it is not secure to save the password as plain text in your database.

Flow of Authentication

Here is the simple flow of authentication in Node.js using Bcrypt.

To Register a new user.

  • The server gets a post request at /register route with username and password in the request body.
  • Encrypt the password using Bcrypt.
  • Save the username and encrypted password in the database.

To login users:

  • The server gets post request on /login route with the username and plain text password.
  • Use Bcrypt to verify if saved password (encrypted) and password in request body matches after decryption.
  • If they match then either create a session or send JWT token depending on the application type.

How to use Bcrypt with Node.js Authentication

Here comes the actual coding part.

In index.js file:

const express = require("express"),
  app = express(),
  mongoose = require("mongoose"),
  bcrypt = require("bcrypt"),
  bodyParser = require("body-parser");

const saltRounds = 10;

app.use(bodyParser.urlencoded({ extended: false }));

mongoose.connect("mongodb://localhost/node_bcrypt", {
  useNewUrlParser: true,
  useUnifiedTopology: true,

var userSchema = new mongoose.Schema({
  username: String,
  password: String,
  joined: { type: Date, default: Date.now },

const User = mongoose.model("user", userSchema);

app.get("/", (req, res) => {
  res.send("Welcome to authentication tutorial");

app.post("/register", async (req, res) => {
  try {
    const hashedPwd = await bcrypt.hash(req.body.password, saltRounds);
    const insertResult = await User.create({
      username: req.body.username,
      password: hashedPwd,
  } catch (error) {
    res.status(500).send("Internal Server error Occured");

app.post("/login", async (req, res) => {
  try {
    const user = await User.findOne({ username: req.body.username });
    if (user) {
      const cmp = await bcrypt.compare(req.body.password, user.password);
      if (cmp) {
        //   ..... further code to maintain authentication like jwt or sessions
        res.send("Auth Successful");
      } else {
        res.send("Wrong username or password.");
    } else {
      res.send("Wrong username or password.");
  } catch (error) {
    res.status(500).send("Internal Server error Occured");

app.listen(3001, () => {
  console.log("Server started at port 3001");

You can arrange the above logic into your folder structure for modularity. Here is the explanation of the code.

We have three routes in this code.

  • First one is / root route which is a dummy route.
  • The second post route is /register route which takes username and password in request body. It add's new user to the database.
  • The third post route is /login route which takes username and password in the request body to authenticate the user.

Salt rounds that we define on the 7th line means cost factor, it affects the time to calculate a single hash.

You can read more about salt rounds here.

In the /register route, we generate the hash for the password and store it with username in the database.

In the login route, we fetch the hashed password from the database and compare it with the plain password passed by request body.

If both the password matches then we do next actions to maintain sessions or generate a JWT token depending on our need.

Now, start the server and you can test the code is working fine.

Open ImageNode.js Bcrypt authentication register route working fine
Open ImageNode.js Bcrypt authentication login route working fine