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:

23 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.

  6. |

    Hi,

    what is the best way to verify the token if i have multiple micro-service running on different machine?

    Thanks,

    • |

      It depends on what works best for you. But generally speaking, since API gateway is the common entry point for accessing your APIs, it is the most convenient place to add authentication, even if you have a separate service for authentication, you can write a middleware in API gateway which can call this authentication service before proxying it to the right service.
      Direct access to microservices should be blocked, all requests should go via the APi Gateway.

  7. |

    Why did you persist the secret key in the Global Config ? Isn’t that insane ? That way each user will have their auth based on the same friggin token! Is this for real? Can you elaborate ? I don’t think this tutorial was practical at all, but pray do correct me in case I’m terribly wrong! Thanks 🙂

    • |

      The secret key is only used for signing the data and verifying the token. The end result is a combination of encode{(header + data + timestamp), secretkey}. So even for the same user, the token generated will be different at different times just coz of the timestamp, let alone the data.

  8. |

    How do we go about using JWT in conjunction with Roles? How would we modify this to make use of JWT / Roles for access control?

    • |

      You can add a scopes as an array of permission and roles inside the JWT.
      And this can be validated against each endpoint in a middleware

  9. |

    So, If I am using jwt tokens to check authorisation then I have to keep the routes which are protected at a different place(after the middleware) and those which aren’t at a different place (before the middleware)?

  10. |

    You definitely shouldn’t show an authentication example storing the user’s raw password in db… This could mislead many beginner programmers to do the same instead of hashing the passwords with algorythms like bcrypt or another lower security but efficient encryption mechanism. When you have time, try to update yout tutorial with bcrypt and explain why programmers should use it =)

  11. |

    Why are we using req.decoded = decoded; ?

    • |

      We may need the data in our routes and hence attaching it to res object

Leave a Comment