Most of the web applications these days have some RESTful web application to share information and these are the main data source for your front-end application. An application built using any front-end framework or library like React, Angular, Ember or Vue needs one RESTful web application to share the information.
While these days the web applications are built in this fashion security on REST is also becoming critical. You cannot just have a REST service with no security so anyone can pull the information from your server.
When I have started building RESTful web application the security is only concerns I have. I have tried most of the available options like Basic Auth, OAuth1, OAuth2 and etc. I found the JWT works best in most of the scenarios and this article is to talk about those specific scenarios and best practices to secure your RESTful web application.
JWT stands for JSON Web Tokens which simply means there will be some JSON data, you encode it using Base64
, sign it using RSA-SHA256
, HMAC-SHA512
or etc and then give it to the user. It also has a built-in expiry of the shared token.
A simple JWT token looks like this
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiZW1haWwiOiJhc2hva0BpbXB1bHNpdmV3ZWIuY29tIiwiZmlyc3RfbmFtZSI6IkFzaG9rIiwibGFzdF9uYW1lIjoiVmlzaHdha2FybWEiLCJnZW5lcmF0ZWRfYXQiOjE1MjQ3MDU0NzA0MzAsImlhdCI6MTUyNDcwNTQ3MH0.7E_9-Lpv8zfAJaOK_3be4w5Nwg_O68LwIe4Z8h2MBZU
Did you see that two little dots separating the values, these are the separators in JWT to identify different parts of the token, these parts are
Header
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Data
eyJpZCI6MSwiZW1haWwiOiJhc2hva0BpbXB1bHNpdmV3ZWIuY29tIiwiZmlyc3RfbmFtZSI6IkFzaG9rIiwibGFzdF9uYW1lIjoiVmlzaHdha2FybWEiLCJnZW5lcmF0ZWRfYXQiOjE1MjQ3MDU0NzA0MzAsImlhdCI6MTUyNDcwNTQ3MH0
Signature
7E_9-Lpv8zfAJaOK_3be4w5Nwg_O68LwIe4Z8h2MBZU
The very first part of JWT is a header which is also a simple JSON data contains information about how it is signed and its type
{"alg":"HMAC256","type":"jwt"}
The second part of the JWT is actual Data of the token (the JSON object you have encoded and signed, the payload) along with iat
he token issuing time and exp
when the token will expire.
{
“id”: 1,
“email”: “some@domain.com”,
“name”: “Some One”,
“exp”: 153456789123,
“iat”: 1575878759412
}
And the third and last part of the JWT is the actual signature of the first two parts. Simply it takes the first part and the second part, concatenate and encode it using Base64
and sign it using a secret key.
The above-described token can be an Access Token or a Refresh Token in the schema and the process to generate them is actually the same, the only difference between Access and Refresh Tokens that Refresh Tokens have a longer lifetime.
Take a scenario where the shared Access Token is expired and the user sends some request with the expired token, the server will reject the request and user has to login again.
To prevent this scenario JWT has built it Refresh Tokens which can be sent to the user when they logged in. A user can generate the new Access Token by sending their Refresh Token to the server.
The JWT seems quite a solution to secure your RESTful web apps, here are the process to implement the same in NodeJS using KoaJS.
PS: The same process can be used to implement into ExpressJS as well after changing some syntaxes.
/**
* jsonwebtoken
*
* @url https://www.npmjs.com/package/jsonwebtoken
*/
import jwt from 'jsonwebtoken';
/**
* es6-promisify
*
* @url https://www.npmjs.com/package/es6-promisify
*/
import toPromise from 'es6-promisify';
/**
* Session
*
* A session class to set or verify the tokens
*/
class Session{
constructor(){
/**
* secret
*
* Your secret for the JWT
* @type String
*/
this.secret = "yourapplicationsecret";
/**
* jwt
*
* Promisify the jwt methods sign and verify
*/
this.jwt = {
sign: toPromise(jwt.sign, jwt),
verify: toPromise(jwt.verify, jwt)
};
/**
* protecting other methods from direct uses
*/
return {
verify: this.verify.bind(this),
set: this.set.bind(this)
}
}
/**
* verify
*
* Verify the existing token and return the payload
* @param {string} token
*/
async verify(token){
try{
const payload = await this.jwt.verify(token, this.secret);
if(payload) return payload;
} catch (err){
throw({message: "Invalid token", name: 'Authorization Error'});
}
}
/**
* set
*
* Set the session for payload (containing basic user details) and return the signed JWT token
* @param {object} payload
*/
async set(payload){
try{
payload.time = Date.time();
return await this.jwt.sign(payload, this.secret);
} catch (err){
console.log(err);
}
}
}
export default Session;
I have used jsonwebtoken
to generate and sign the tokens for the payload (JSON data) and es6-promisify
to convert sign and verify methods of jsonwebtoken
into a Promise
. Feel free to use the above as a Session utility in your web application.
import Session from './session';
const session = new Session();
// generate the token
const token = await session.set({...});
import Session from './session';
const session = new Session();
// returns the payload
const payload = await session.verify(token );
A good way to send the token from front-end is using request headers below is the example using JavaScript fetch
API
var options = {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + <your token>
}
}
fetch('/auth/log', options)
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.done();
This will send the token to your server into headers.
Session implementation using JWT there are important key aspects to take in practice for better performance.
When you set your payload only your database id after inserting the token into the database, your application needs to make another database call to get the user details, which will be an extra IO operation into the application flow. You should set your payload with the basic user details like id
, username
, first_name
, last_name
etc to use the same after the JWT verification.
If you look carefully at the generated token of the same payload, the middle part will remain same and can be brute forced to decode the signature. It is a good practice to add a timestamp value in your payload so that the middle part of the token also get changed every time.
Make sure you are sending and receiving data using secure connection where everything gets encrypted on the network to prevent the network interceptors to modify or update any data.
If you think to add HTTPS to your application in cost you extra, look into the LetsEncrypt project backed by Linux Foundation and supported by many tech giants in the community.
Hope now you understand the JWT and will be able to implement the same into the next RESTful web application let me know your thoughts and experience in the comments also don’t forget to share among your other developer friends. Also an applaud doesn’t hurt anybody so hit the button :)
Google Develover Expert — WebTechnologies and Angular | Principal Architect at Naukri.com | Entrepreneur | TechEnthusiast | Speaker