dcsimg
 

Securely Store User Passwords Using Bcrypt Hashing

Monday Nov 26th 2018 by Rob Gravelle

In a previous article, we were introduced to the JSON Web Token (JWT) open standard (RFC 7519). In today's tutorial, Rob Gravelle shows us how to create a route that accepts a user's credentials and stores them securely in a MongoDB database using bcrypt hashing.

In the Integrate JSON Web Token Authentication into Single-page Apps article, we were introduced to the JSON Web Token (JWT) open standard (RFC 7519), that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. Having set up a Node.js project in MS Visual Source Code, in today's tutorial, we'll be creating a route that accepts a user's credentials and stores them securely in a MongoDB database using bcrypt hashing.

Configuring up the MongoDB database

MongoDB offers both an Enterprise and free Community version of its database. The latter will suffice for our purposes. Its available for download on the MongoDB site.

Open the VS Code project from the last tutorial and add the following 2 lines to the server.js file:

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/jwtauth', { useNewUrlParser: true });

The express web framework, which we installed in the last tutorial, includes body-parser middleware that parses incoming request bodies before our handlers. We will be using two body parsers:

  1. bodyParser.urlencoded([options]): returns middleware that only parses urlencoded bodies and only looks at requests where the Content-Type header matches the type option.
  2. bodyParser.json([options]): returns middleware that only parses json and only looks at requests where the Content-Type header matches the type option. This parser accepts any Unicode encoding of the body.

Here's the code that tells the express app to use the above body parsers:

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

Creating the User Model

The user model defines the schema for the User collection and exports the model for other modules to use. Create a new folder in the project root called "models". Inside it, create a file named "user.model.js".

Add the following code to the user model:

const mongoose = require('mongoose');

const user = mongoose.Schema({
   _id: mongoose.Schema.Types.ObjectId,
   email: {type: String, required: true},
   password: {type: String, required: true}
});

module.exports = mongoose.model('User', user);

Defining the User Route

We'll be using the npm bcrypt module to hash the password so that we aren't storing the plain password. Bcrypt is a really good hashing algorithm to use because, in addition to incorporating a salt to protect against certain types of cyber attacks, bcrypt is an adaptive function. In a nutshell, the iteration count can be increased to make it slower, making it resistant to brute-force attacks even with increasing computation power.

Here's the installation command:

npm install bcrypt -- save

Now we'll create sign up page for our application. For that, we'll define a post route.

Again in the project root, create a folder named "routes" and add a file called "user.route.js" to it.

Here's the code to put in the user route file. After the code, I'll explain what it does.

const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const User = require('../models/user.model');
const jwt = require('jsonwebtoken');

router.post('/signup', function(req, res) {
   bcrypt.hash(req.body.password, 10, function(err, hash){
      if(err) {
         return res.status(500).json({
            error: err
         });
      }
      else {
         const user = new User({
            _id: new  mongoose.Types.ObjectId(),
            email: req.body.email,
            password: hash    
         });
         user.save().then(function(result) {
            console.log(result);
            res.status(200).json({
               success: 'New user has been created'
            });
         }).catch(error => {
            res.status(500).json({
               error: err
            });
         });
      }
   });
});

module.exports = router;

The post() method above takes the password field of the incoming request and hashes it. Then, a new user it created and added to the MongoDB database. If something should go wrong along the way, the function returns the error within a json formatted response.

Testing our User Route

It would be nice to know if the user route works as it should - in fact, it's kind of essential. There are two basic approaches we could take: A) we could create a test page that submits the user info via a POST action and then capture the returned data. B) An easier way is to use Postman. It's a great tool for testing web services because it was specifically developed for sending HTTP requests in a simple and quick way!

In VS Code, go to the Terminal and issue the npm start command. You'll see the "Server is running on Port 3000" message in the console.

Here's how to send a post request from Postman:

  1. Select "POST" from the request type list.
  2. Enter "http://localhost:3000/user/signup" in the URL field.

In the Body tab:

  1. Select the "raw" radio button.
  2. Choose "JSON(application/json)" from the data format list.
  3. Enter the following in the request body textarea:
{
	"email": "rob@robgravelle.com",
	"password": "mysupersecurepassword"
}

After clicking the SEND button, you should see the success message appear below the request form:

Back in VS Code, you'll see a print out of the user, including his bcrypted password.

You should also see the same data in the MongoDB users collection. Here's a screenshot of the user document using Navicat for MongoDB's excellent JSON view:

All of the files from today's article are up on GitHub.

Conclusion

In today's article, we elaborated on the SPA that we set up in the last tutorial by creating a route that accepts a user's credentials and stores them securely in a MongoDB database using bcrypt hashing. Next time, we'll use our user's credentials to log into the site.



Rob Gravelle

Rob Gravelle resides in Ottawa, Canada. His design company has built web applications for numerous businesses and government agencies. Email him.

Rob's alter-ego, "Blackjacques", is an accomplished guitar player, who has released several CDs and cover songs. His band, Ivory Knight, was rated as one of Canada's top hard rock and metal groups by Brave Words magazine (issue #92).

Home
Mobile Site | Full Site