JSON web token(JWT) demystified

JSON web token(JWT) demystified

If you have ever dealt with authorized routes or resources in your web application, it is very likely that you have had to deal with JWT. You might have probably used an already available library to generate tokens ( Please continue doing so as they are tried and tested solutions ) but if you’re looking to understand them better and figure out how they work, may be this article can help you. In order to do so we will break down a token and figure out what are the different parts that make up a JWT and what information they carry.

Deconstructing JWT

Let us first generate a JWT with the following data and let the secret be “SUPER_SECRET_WORD”(please don’t use this in production. I beg you).

{ 
    name: 'testUser1', 
    email: 'testUser1@gmail.com' 
}

I’ll be using code snippet given below to generate the token

//jsonwebtoken is an npm package
const jwt = require('jsonwebtoken');

const user = { 
    name: 'testUser1', 
    email: 'testUser1@gmail.com' 
};

const token = jwt.sign(user, 'SUPER_SECRET_WORD');
console.log(token);

And we have ourselves a JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdFVzZXIxIiwiZW1haWwiOiJ0ZXN0VXNlcjFAZ21haWwuY29tIiwiaWF0IjoxNTkwNjgxOTUzf
Q.RFbiKd-yih_lQlb9fgFca0hPUHDxCtV-EEzvsHBFt1M

If you see the token above, you’ll notice that there are three parts to the token seperated by a dot(.). The first part of the token is the header, the second is the payload and the third one is the signature.

Header and Payload

//header
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

//payload
eyJuYW1lIjoidGVzdFVzZXIxIiwiZW1haWwiOiJ0ZXN0VXNlcjFAZ21haWwuY29tIiwiaWF0IjoxNTkwNjgxOTUzfQ

Header and payload are just base64url(URL safe version of base64 encoding) encoded version of some JSON data. Let us now decode and see what that data is.

//Function to decode base64url encoding to objects
const decodeData = ( data ) => {
  //base64url encoded string to ascii string -> stringified object
  const obj = Buffer.from(data,'base64').toString('ascii');

  //parsing the string to object
  return JSON.parse(obj);
}

const header = decodeData('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9');
console.log(header);

The output is as shown below

{ 
  alg: 'HS256', 
  typ: 'JWT' 
}

The header contains the algorithm and the type property. alg holds the information about what algorithm was used to sign the token. It is HS256 by default. There are other algorithms that you can specify but let’s talk about them some other time ;).

Moving on to payload section. Let us decode the payload and check what data it holds.

//calling the function defined above
const payload = decodeData( 'eyJuYW1lIjoidGVzdFVzZXIxIiwiZW1haWwiOiJ0ZXN0VXNlcjFAZ21haWwuY29tIiwiaWF0IjoxNTkwNjgxOTUzfQ'
);
console.log(payload);

And as a result we get…

{ 
  name: 'testUser1', 
  email: 'testUser1@gmail.com',
  iat: 1590681953 
}

What do we have here?… That’s the data that we originally used while issuing the token! But something else has been added to it. iat(Issued at) holds the time stamp of the moment when this token was originally issued/signed.

You might be thinking, since they are encoded, anyone can tamper with the data and you would be right about that if the token wasn’t signed with a secret. That’s where the third part of the token, signature comes into play.

Signature

//The signature
RFbiKd-yih_lQlb9fgFca0hPUHDxCtV-EEzvsHBFt1M

This is the most critical part of JWT. It is somewhat like a checksum but it combines the secret with the payload while generating the signature. Consider this, an authorization server generates the token and sends it to a client. The client, while making a request to an API server sends the token in the request header. The API server will need to verify if the token is valid. In order to do so it must have the shared secret, “SUPER_SECRET_WORD” in our example (Not the case if you’re using RS256 algorithm, you would have private and public key pair). The API server will now recalculate the signature with the payload and the secret with the algorithm specified in the header. Then it compare them to check whether they match. If someone has tampered the payload, they cannot possibly generate a valid signature without knowing the secret(keep your secret safe :p). The signatures won’t match and the API server now knows that the token is invalid and hence discards the request.


Thank you for reading! I hope you found this informative and helpful. If you noticed any error, please comment it down below, I’ll rectify them. Thank you for your time and have a great day!