Token based authentication in Node.js using JWT

January 11, 2017
by Rahil Shaikh

Authentication is a big part of any application. The way authentication is handled traditionally is by creating a user session on the server where we usually store the user’s information in memory or on disk. This is also known as server/session based authentication.

Web applications have come a long way in a past few year, we have the stateless web, hand-held devices came along, we have load balancer then came along the micro-services architecture. Storing session data on the server made our app less scalable and then sharing the session between multiple services or servers is also a problem. That is why Token Based Authentication 🙂

In this tutorial, we will learn to implement token based authentication in our node.js applications.

What is token based authentication?

Token-based authentication is state-less and session less, meaning when we authenticate the user we do not store any user information on the server. Instead, we generate a token signed by a private key and send it to the client. The way it works is as follows.

  • User makes a request to the server with username/password
  • The server verifies the user.
  • The server generates a signed token and provides it to the client.
  • The token may contain the user data.
  • The client stores the token and sends it along with every request.
  • The server verifies the token and processes the request.

Tokens can be sent to server in any way but the best practice tells us to send it in an HTTP header.

DOWNLOAD

Our Application

In this tutorial, we will see how we can easily add token based authentication using JSON web Tokens in Node.js.
We will build a few APIs using NodeJS and ExpressJS and see how we can protect/authenticate them using JWT’s
We will be using.

  • MongoDB as our DB
  • ExpressJS for routes
  • jsonwebtoken an npm module for managing tokens.

Getting started

Make sure you have node and npm installed. To begin with create a project folder and navigate into it and run npm init --yes. This will create a package.json for us.

Next, we will install the necessary tools for our application.

 npm i body-parser express jsonwebtoken mongoose --save

Our, package.json should look like this.

package.json
{
  "name": "jwt-auth",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "Rahil Shaikh",
  "license": "MIT",
  "dependencies": {
    "body-parser": "^1.15.2",
    "express": "^4.14.0",
    "jsonwebtoken": "^7.2.1",
    "mongoose": "^4.7.6"
  }
}

Folder Structure

We will be following the below folder structure

- config
--- config.js
- controllers
--- index.js
--- protected.js
--- user.js
- middlewares
--- verifyToken.js
- models
--- user.js
- index.js
- package.json

Creating our user model

We are using MongoDB for persistence and mongoose as our ORM. Make sure your machine has MongoDB installed and running.

./models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

module.exports = mongoose.model('User', new Schema({
    email: String,
    password: String
}));

Node Application

We will start in index.js by grabbing the required modules and setting up an express server.

./index.js
let express = require('express');
let app = express();
let bodyParser  = require('body-parser');
let mongoose    = require('mongoose');
global.config = require('./config/config');

let jwt    = require('jsonwebtoken');
let User   = require('./models/user');

mongoose.connect("mongodb://localhost/demo");
app.use(bodyParser.json());

app.get('/', function(req, res){
    res.send('hello world');
});

app.listen(3000, function(){
    console.log('App running on 3000');
});

Now, we can start the server by running node index.js. We should be seeing hello world printed on the screen on http://localhost:3000.

Adding unprotected routes

In this section, we will expose an API to signup users and also to authenticate them. In the authenticate API we will create a token for a successful login and send it to the client.

./controllers/index.js
var express = require('express');
var router = express.Router();

router.use('/user',require('./user'));

module.exports = router;
./controllers/user.js
let express = require('express');
let router = express.Router();
let jwt = require('jsonwebtoken');
let User = require('../models/user');

router.post('/signup', function(req, res){
    let user = new User({
        email: req.body.email,
        password: req.body.password
    });
    user.save(function(err, data){
        if(err){
            return res.json({error: true});
        }
        res.json({error:false});
    })
});

router.post('/authenticate', function(req, res){
    let data = {
        email: req.body.email,
        password: req.body.password
    };
    User.findOne(data).lean().exec(function(err, user){
        if(err){
            return res.json({error: true});
        }
        if(!user){
            return res.status(404).json({'message':'User not found!'});
        }
        console.log(user);
        let token = jwt.sign(user, global.config.jwt_secret, {
            expiresIn: 1440 // expires in 1 hour
        });
        res.json({error:false, token: token});
    })
});

module.exports = router;

Note. Here, for simplicity we are just encoding the entire user object. Idealy you should not include sensitive data such as password in your encoded token.

We will also have to modify our main index.js to add routes exposed by our controllers to the app.

...
....
.....
app.use(require('./controllers'));

app.listen(3000, function(){
    console.log('App running on 3000');
});

Run the app. We will use postman to test our APIs. Let's signup first.

Sign Up

Sign Up

Here, we have signed up a user with email as rahil[at]ciphertrick.com and password as password. Now, let's try to authenticate with the same credentials.

authenticate

authenticate

If you see above, on a successful authentication the API sends us a token. This is the token we will need to further validate the user. We can accept the token in the header, in the body or as a url param. Also, remember the token has the data which we have signed using our secret key.

Note. In a real application passwords must not be stored as a plain text instead should be hased with a proper alogorithm depending upon your requirements.

Creating a middleware to verify Token

Let's add an express middleware that will verify the token for us.

./middlewares/verifyToken.js
var jwt = require('jsonwebtoken');

module.exports = function(req,res,next) {
  var token = req.body.token || req.query.token || req.headers['x-access-token'];
    if (token) {
    // verifies secret and checks exp
        jwt.verify(token, global.config.jwt_secret, function(err, decoded) {
            if (err) { //failed verification.
                return res.json({"error": true});
            }
            req.decoded = decoded;
            next(); //no error, proceed
        });
    } else {
        // forbidden without token
        return res.status(403).send({
            "error": true
        });
    }
}

Protecting routes

Now that we have our middleware ready to use, let's see how we can protect our routes. For this, we will create an API as /protected, which will simply verify the token and send the decoded result.

./controllers/protected.js
let express = require('express');
let router = express.Router();

router.get('/', function(req, res){
    res.json(req.decoded);
});


module.exports = router;

Modify your index.js file within controllers to look like this.

./controllers/index.js
var express = require('express');
var router = express.Router();
let verifyToken = require('../middlewares/verifyToken');

router.use('/user',require('./user'));
router.use('/protected', verifyToken, require('./protected'));

module.exports = router;

Note that we are using the verifyToken middleware to protect our APIs. Once again restart your server and test the API using postman. Remember if we hit the API without the token it will result in a 403 forbidden error.

jwt verification

jwt verification

Conclusion

We have seen how we can add token-based authentication to our node.js application using jsonwebtoken. This was just a simple use-case to help get an understanding on how token based authentication works. Token based authentication scales well and makes it easier to manage cross devices authentication.

Further Links

  1. Full stack web development course
  2. jsonwebtoken

About

Engineer. Blogger. Thinker. Loves programming and working with emerging tech. We can also talk on Football, Gaming, World Politics, Monetary Systems.

Free PDF

Subscribe and get AngularJS Tips PDF. We never spam!
First Name:
Email:

12 comments

  1. |

    But what does it mean router.use ‘protected’? Is this for get-request or for a post-request?

    • |

      We are just attaching the routes defined in the protected.js file to our express router.

  2. Dlazzy
    |

    Hi,

    This is a really good article but when you say :
    “We will also have to modify our main index.js to add routes exposed by our controllers to the app.”
    I think this is one the most important part but there is not example.
    I writted this on index.js :
    app.post(‘/signup’, function(req, res){
    res.send(‘sign up’);
    });

    app.post(‘/authenticate’, function(req, res){
    res.send(‘authenticate’);
    });

    And wow it doesn’t works !
    I can’t find a working tutorial for local authentication with or without token…
    Will you help me ?

    Thanks !

    • Rahil Shaikh
      |

      Okay, so I guess there is a confusion here. When I say “main index.js” it refers to the index file in the root(outer) directory. A sample code is provided just below that line, I’m putting it here again.

      app.use(require('./controllers'));

      This picks up the index.js file inside the controllers directory.

      And as for the the index.js file inside the controllers directory is should look like this.

      var express = require('express');
      var router = express.Router();
      router.use('/user',require('./user'));
      module.exports = router;
      • dlazzy
        |

        Well thank you very I’ve just confuse myself, I will try it later !

    • Rahil Shaikh
      |

      What do you mean?

  3. |

    Hi Rahil, can you provide this tutorial with mysql example ? i have no idea how to implement it. thank you

  4. |

    Hi Rahil, good Article. But here what are the contents in the config file, what should be secret key. Is it hardcoded from our side

  5. |

    Thanks a lot bro! It’s really good articles.

Leave a Comment