Tuesday, June 23 2020

How to build Secure Authentication App using JWT in Node.JS

In this tutorial, we are going to discuss about How to build Secure Authentication App using JWT in Node.JS and Express JS. JWT means JSON Web Token is a object that is safe approach to transfer data between two parties.

how-to-build-secure-authentication-app-using-jwt-in-node-js

Authentication Workflow with JWT

  1. When server receives a request from user end and JWT will encrypted that information and associate with JWT token.
  2. Client store the token in their machine for accessing data next time.
  3. Next request, client sets token in header using Authorization with Bearer XXXX XXXX XXXX 
  4. In server, verify that token once succeeded and returns the result. If that mismatch returns the message with Authorization failed.

Click here to read about Authentication App with OAuth2.0 and Node.js

Prerequisites

  1. Express – is a popular framework which is a most common framework used in node.js
  2. JSON Web Token(JWT) – It is used to generating token for only authorized persons can able to access the application
  3. Bcrypt – It is used to hash the password into the protected string before saving it to the database.
  4. Body-parser – It is used to parse the JSON data, plain text or a whole object.
  5. CORS – It allows enabling CORS with multiple options. It is available through the npm registry.
  6. Mongoose – It allows you to interact with MongoDB database.
  7. ENV – It allows you to create environment file for keeping configuration variables accessing at any where.
  8. Validator – It helps to validate the user inputs with mongoose.
  9. Postman – for testing API.

Install Packages

npm init

npm install express jsonwebtoken bcryptjs body-parser cors mongoose env-cmd --save

npm install nodemon --save-dev

After the installation and create following files and folders in your project directory.

touch app.js

// Root Directory
mkdir middleware
mkdir models
mkdir routes
// Authnetication
cd middleware
touch auth.js
// Database
cd models
touch db.js
touch user.model.js
//Routing
cd routes
touch user.route.js

Project Setup

jwt-authentication-app-directory-structure

Setup ENV variables

Create a .env file inside root directory with following contents

PORT=4000
JWT_KEY=JwtAuthenticationApp2020
MONGODB_URI=mongodb://localhost:27017/bg_mongo

Define Mongoose Schema

Before create a schema we have to make an connection with mongodb in /models/db.js file with following contents.

// MongoDB connection
const mongoose = require('mongoose');

mongoose.Promise = global.Promise;
mongoose.connect(process.env.MONGODB_URI, {useUnifiedTopology: true, useNewUrlParser: true, useCreateIndex: true});
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error'));

Create schema for users table in /models/user.model.js file with following contents

const mongoose = require('mongoose');
const validator = require('validator');

const Schema = mongoose.Schema;

const userSchema = new Schema({
    name: {
        type: String,
        required: true,
        trim: true,
        max: 65
    },
    email: {
        type: String,
        required: true,
        trim: true,
        unique: true,
        lowercase: true,
        max: 65,
        validator: value => {
            if (!validator.isEmail(value)) {
                throw new Error({ error: 'Invalid Email address'  });
            }
        }
    },
    password: {
        type: String,
        required: true,
        trim: true,
        minlength: 8
    }
});

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

In app.js file with following contents

const express = require('express');
const bodyParser = require('body-parser');
const cors = require("cors");
const router = require('./routes/user.route');

require('dotenv').config();
require('./models/db');
const port = process.env.PORT;
const app = express();

var corsOptions = {
    origin: "http://localhost:4000"
};
  
app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}));
app.use('/', router);

app.listen(port, () => {
    console.log(`Server running on port ${port}`)
})

In /routes/user.route.js file with following contents

const router = require('express').Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const userModel = require('../models/user.model');
const auth = require('../middleware/auth');

Once included the packages, we makes below REST API calls.

HTTP /POST – user register

router.post('/user/register', (req, res, next) => {
    bcrypt.hash(req.body.password, 10).then((hash) => {
        const user = new userModel({
            name: req.body.name,
            email: req.body.email,
            password: hash
        });

        user.save().then((response) => {
            res.status(201).json({message: "User successfully created!", result: response});
        }).catch(error => {
            res.status(500).json({error: error});
        })
    });
});

After that, Run nodemon app.js in the terminal. Goto Postman check register URL http://localhost:4000/user/register through /POST method.

signup-using-jwt-authentication-token-with-nodejs

HTTP /POST – user login

router.post('/user/login', (req, res, next) => {
    let getUser;
    userModel.findOne({ 
        email: req.body.email 
    }).then(user => {
        if (!user) {
            return res.status(401).json({message: "Authentication failed"});
        }
        getUser = user;
        return bcrypt.compare(req.body.password, user.password);
    }).then(response => {
        if (!response) {
            return res.status(401).json({message: "Authentication failed"});
        }
        let jwtToken = jwt.sign(
            {
                email: getUser.email,userId: getUser._id,
            }, 
            process.env.JWT_KEY, 
            {
                expiresIn: "1h"
            }
        );
        res.status(200).json({
            token: jwtToken,
            expiresIn: 3600,
            data: getUser
        });
    }).catch(error => {
        return res.status(401).json({message: "Authentication failed"});
    })
});

After that, Run nodemon app.js in the terminal. Goto Postman check login URL http://localhost:4000/user/login through /POST method.

login-using-jwt-authentication-with-node-js

After logged we make an API for profile page.

Before making this API we need to create authentication function to verify that connection using in /middleware/auth.js file with following contents

const jwt = require('jsonwebtoken');
const userModel = require('../models/user.model');

const auth = (req, res, next) => {
    const authHeader = req.headers.authorization;
    if (authHeader) {
        const token = authHeader.split(' ')[1];
        data = jwt.verify(token, process.env.JWT_KEY);
    
        userModel.findOne({
            _id: data.userId,
        }).then(user => {
            if (!user) {
                res.status(401).json({message: "Authentication failed"});
            }
            next();
        }).catch(error => {
            return res.status(401).json({message: "Authentication failed"});
        });
    } else {
        return res.status(401).json({message: "Authentication missing"});
    }
};

module.exports = auth;

Update the below codes in /routes/user.route.js file for user profile page.

router.get('/user/me/:id', auth, (req, res, next) => {
    userModel.findById(req.params.id, (error, data) => {
        if (error) {
            return next(error);
        } else {
            res.status(200).json({result: data});
        }
    });
});

After that, Run nodemon app.js in the terminal. Goto Postman check this profile URL http://localhost:4000/user/me/{_id} through /GET method.

jwt-authentication-token-verify-node-js

Thanks for reading this tutorial. Please share this post with others.


Click here to Get Full Source Code from Github

Read more Node.JS Articles

Share Your Thoughts

phpexpertise

I’m Blogger and Programming Blog, Tutorials, PHP, MySQL, jQuery, Laravel, Wordpress and Codeigniter