loading...
Cover image for Securing your Node js api with JSON Web Token

Securing your Node js api with JSON Web Token

medaymentn profile image medaymenTN ・6 min read

Introduction

Nowadays REST ( Representational state transfer) has become the most used style in web architecture due to their simple syntax and flexibility . REST allows users to make their apps extensible, the fact that a client from different frontend platforms can perform requests to the server using http protocol in a simple way and exchange data which could be encoded in JSON or XML format.

Now with all the features that comes up with Restful architecture it still have some problems specially with their security .

From the many security approaches that are used to secure Restful api's is token based authentication

So what is token based authentication ?

let's make this easy :))

The general concept behind a token-based authentication system is simple.

Allow users to use their username and password in order to obtain a token which allows them to access a specific resource without using every time their natural credentials.

Once their token has been obtained, the user can use that token to access a specific resource in a server for a time period to the remote site.

How it works ??

well the process of using jwt is composed of 6 steps

1- authenticate using credentials

2- once authentication is granted the server generate a random string which contains the json web token

3- return the token to the client side

4- storing the token in the client side

5- sending the token with every single http request from the client to the server

6- the server check whether the token is valid or not and grant access to the specified resource

What we are going to build ?

well in this article we are going to build an API with Node js and Express.js and we will test it with postman so let's get started :))

first let's take a look at our project structure

-/configurations
             ->/config.js

-package.json

-index.js

now that our project is dived and ready to go let's install our packages.

open your command line under your project directory and write this command

npm install --save  express body-parser morgan jsonwebtoken 

so let's explain the packages that we have installed

Express : the famous node js framework

body-parser: allow us to get the data from the body of the requests

morgan : logs the requests in the console

jsonwebtoken : the package that allows us to generate jwt and build our middleware to check whether the token is valid or not .

Now let's go the config.js

config.js

well accually this file is used to setup some configurations that most users need to do in order to better organise their projects.

They can setup configuration for databases or for other purposes , in our case we are going to use this file to setup our secret which will be used when creating our jwt so the file should look like this

module.exports = {

    secret : "heymynameismohamedaymen"
}

Now let's go to our index.js which is the most important file in our app .

index.js

const express = require('express'),
bodyParser = require('body-parser'),
morgan      = require('morgan'),
jwt    = require('jsonwebtoken'),
config = require('./configurations/config'),
app = express(); 

//set secret
app.set('Secret', config.secret);

// use morgan to log requests to the console
app.use(morgan('dev'));

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

// parse application/json
app.use(bodyParser.json());

app.listen(3000,()=>{

 console.log('server is running on port 3000') 

});
app.get('/', function(req, res) {
    res.send('Hello world  app is running on http://localhost:3000/');
});

to check if everything is ok go to the command line and run this command


node index.js 

open your browser on http://localhost:3000/

alt text

well everything looks fine !!

we can see also that the request is logged in our console thanks to morgan package

alt text

Setting up the authentication system

Now comes the best part of our app :)) .

Well in this app we are not really going to work with real models which are stored in database but we are going to setup a static login and password to check whether the user exists or not ,because what we want to focus on in this article is the usage of JWT, so simply you can change the code that we are about to write .

so in our index.js let's create the authenticate route

here we choose aymen as a username and 123 as a password

app.post('/authenticate',(req,res)=>{

    if(req.body.username==="aymen"){

        if(req.body.password===123){
             //if eveything is okey let's create our token 

        const payload = {

            check:  true

          };

          var token = jwt.sign(payload, app.get('Secret'), {
                expiresIn: 1440 // expires in 24 hours

          });


          res.json({
            message: 'authentication done ',
            token: token
          });

        }else{
            res.json({message:"please check your password !"})
        }

    }else{

        res.json({message:"user not found !"})

    }

})

now that the route is build we can get back our token .. so let's make a test with postman

alt text

now that we have the token, as a client we have first to store that token somehow and there are many tools to do that , for exemple if we are using our browsers we can use localstorage or if we are using android to create a mobile app we can use sharedpreferences

Setting up the middleware

For the moment we have our token and we can make http requests to the server , but we must also build our middleware that will handle every http request , search for the token and check if it's valide or not .

But before creating the middleware we must create the routes that will be protected with it , so in our index.js the protected routes should look like this

const  ProtectedRoutes = express.Router(); 

app.use('/api', ProtectedRoutes);


Now every route under /api will be a protected route by the middleware and to get access to the resource that uses /api as a parent route we have to provide the right token for it .

To send the token to the server along side with some data we usually store the token in the header of every request after that the middleware will handle the http request and extract the token from the header .

so in our index.js let's write the code that will do that

ProtectedRoutes.use((req, res, next) =>{


    // check header for the token
    var token = req.headers['access-token'];

    // decode token
    if (token) {

      // verifies secret and checks if the token is expired
      jwt.verify(token, app.get('Secret'), (err, decoded) =>{      
        if (err) {
          return res.json({ message: 'invalid token' });    
        } else {
          // if everything is good, save to request for use in other routes
          req.decoded = decoded;    
          next();
        }
      });

    } else {

      // if there is no token  

      res.send({ 

          message: 'No token provided.' 
      });

    }
  });

To check if our middleware works or not we are going to create another route in which we are going to return an array of products and we are going to set this route under /api using ProtectedRoutes, after that we will make a GET request to get the data back .

first let's create the route

ProtectedRoutes.get('/getAllProducts',(req,res)=>{
 let products = [
     {
         id: 1,
         name:"cheese"
     },
     {
        id: 2,
        name:"carottes"
    }
 ]

 res.json(products)

})

now let's try to get the list of products without providing the token and see what happens

alt text

this time the middleware did not return the data because we did not provide the token or in other word the api did not recognize us and thought that we are the bad guys that want to grab some informations .

so now using postman we are going to put the token in the header of our request and let's perform another request

alt text

Now everthing looks great we get the data back ;)).

Conclusion

In this exemple we had a great look at the usage of JWT and their importance for the security of the Restful Api's , note that this is not the only approach for the security of node js apps but there are a lot of tools that could be very usefull .

we hope that this look has given a better understunding about how the tokens are created , how the routes are protected and how all of this if managed inside a node js app .

you can find the project in the link below :

(https://github.com/medaymenTN/JWTNodeJS)

Posted on by:

Discussion

markdown guide
 

Great tutorial :)

I guess it's also interesting how you invalidate JWT on logouts. Do you prefer any method? For example refresh tokens or blacklist tables?

 

Personally, I'm storing a unique hash in my database associated with the JSON web token's unique ID (you could also just store this in memory but as my application is still in the development stage it gets restarted often).
That way, when the user logs out, I can just remove their hash from the database and thus invalidate their session.

It's cleaner because then I don't have to worry about wiping old keys from blacklist tables - and I don't have to deal with refreshing the tokens.

 

Isn't the point of using web tokens that you don't need database access to users for every request?

I was going to say that the article could benefit from more exploration of why this is a good approach.

Good point, although this does minimize the amount of requests that you need to make to the database.

 

well to be honest i'v never used blacklist tables ,i usually use refresh tokens.
check out this link below you may find a good strategy to deal with JWT on logout, there is a lot of infomations that could be very useful
(stackoverflow.com/questions/219786...)

 

Jwt.io has a great visual debug tool for these type of tokens!

 

I'm just getting started with building an app that would be using JWT for auth and trust me this has been really helpful. Gracias.

 

happy to hear that sirr . De Nada :))

 

Just a quick correction, 1440 refers to 1440 seconds, which is 24 minutes, not 24 hours.