Securing your Node js api with JSON Web Token
Mohamed Aymen Ourabi
Senior Frontend Engineer | React | Angular | Node Js | Typescript
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 https://localhost:3000/');
});
to check if everything is ok go to the command line and run this command
node index.js
open your browser on https://localhost:3000/
well everything looks fine !!
we can see also that the request is logged in our console thanks to morgan package
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
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
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
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 .
Cyber Security Specialist | Security Program Manager @Yogosha
6 年Great guide buddy ! keep it up !
Software Engineering Manager - Synchrony
6 年Just worked through this thanks for sharing!
Co-Founder and CTO | Web3 & Decentralized Applications, Strategic Product Development
6 年well i have been struggling a lot lately to decide where to actually store jwt cookies does not feel right and then local storage or session storage seem to be over visible to the end user ... there should be a good way to do this some other way