Making a JWT From Scratch

JSON Web Token (JWT) is a fairly recent standard that is quickly gaining popularity in a number of areas. JWTs allow developers to sign a set of claims and pass that information around, including to and from client code, without worrying about that information being altered maliciously.

However, as with most things crypto, there are a couple of issues to be aware of when using JWTs that can slip past some people. My firm belief is that with a little more understanding of how everything works it is much easier for developers to make the correct decisions on how to handle security issues. Knowledge is power.

What is a JWT?

When you see a JWT, it appears to simply be a relatively long opaque string.

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiTWljaGFlbCBEYXZpcyJ9.oFBF-b8ZMxFg5IMocsJJHnH2-jJCxM9qIN9_lwFNIq0"

However, it is important to realize that this string isn’t opaque. The JWT itself contains all of the claims embedded and directly readable. This means that one should never include sensitive information in a JWT that is sent to the client or made available externally in any way.

There are three parts to a JWT, delimited by periods.

  • Header
  • Claims
  • Signature

The header and claims are simply base64 encoded JSON maps, which is the JSON in JSON Web Token.

Header

The header is usually fairly sparse. It contains information on the type of token as well as the algorithm used.

{
    “alg”: “HS256”,
    “typ”: “JWT”
}

It is important to note that you should not attempt to verify the token based on the algorithm in the header. There is an attack on JWTs that allows malicious users to fake a token by altering the algorithm.

For the most part, it is best to ignore the information in the header. You should already know how the JWT is encoded and only attempt to verify it in a manner that you expect.

Claims

The claims are the real informational meat of the token, providing all sorts of information. After the token has been verified, this information can be used for all sorts of purposes. Anything can be included in the claims that the signing body deems appropriate.

{
    “name”: “Michael Davis”
}

Signature

The signature portion is the signing output obtained by running the base64 encoded claims and header through the algorithm dictated in the header. You don’t need to base64 decode it.

"so2p4iIB7OIszQh2lPSAIs7ExEJQP4s2KEyrMHgTriI"

Generating a JWT

Now that we know the three parts of the JWT, lets go ahead with generating one from scratch. My examples are going to use Python, but the general idea is applicable to any language.

Header

import base64  
import json


header_dict = {  
    “alg”: “HS256”,
    “typ”: “JWT”
}

# Step 1: Coerce the dictionary to a JSON string.
json_string = json.dumps(header_dict)

# Step 2: Base64 encode the JSON string.
base64_string = base64.b64encode(json_string)

# Step 3: Strip padding from the Base64 string
header = base64_string.rstrip('=')  

This gives us the first piece of the puzzle.

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"

Claims

The claims are similar to the header, altering the input for the claims you want to sign instead of the header values.

import base64  
import json

claims_dict = {  
    “alg”: “HS256”,
    “typ”: “JWT”
}

# Step 1: Coerce the dictionary to a JSON string.
json_string = json.dumps(claims_dict)

# Step 2: Base64 encode the JSON string.
base64_string = base64.b64encode(json_string)

# Step 3: Strip padding from the Base64 string
claims = base64_string.rstrip('=')  

Our second piece of the puzzle

"eyJuYW1lIjoiTWljaGFlbCBEYXZpc3MifQ"

Signature

The signature is the final piece of the puzzle that allows other parties to verify and trust the claims. There are a number of different algorithms you can use to sign your JWT that allow for different pros and cons.

The big question you need to answer is if you are going to use a symmetric or asymmetric key. An asymmetric key is required if you want to allow others to verify the authenticity of your JWTs by providing a public key without divulging the private key used for signing.

We are going to go ahead with a symmetric HMAC key using SHA256, which is often the default in JWT libraries.

We are going to use a secret of “key”.

import hashlib  
import hmac


signing_input = header + '.' + claims

signature = hmac.new('key', signing_input, hashlib.sha256).digest()  

This gives us the final piece.

"oFBF-b8ZMxFg5IMocsJJHnH2-jJCxM9qIN9_lwFNIq0"

It is as simple as concatenating these three pieces together, delimited with periods.

jwt = "%s.%s.%s" % (header, claims, signature)  

This gives us the final JWT.

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiTWljaGFlbCBEYXZpcyJ9.oFBF-b8ZMxFg5IMocsJJHnH2-jJCxM9qIN9_lwFNIq0"

Congratulations! Constructing a JWT from scratch is just that easy.

Using a Library

As easy as creating a JWT from scratch is, it does usually help to use a library to keep from having to re-implement everything every time. It just so happens that I wrote just such a library which makes signing and verifying JWTs a snap.

from jose import jwt

claims_dict = {  
    “alg”: “HS256”,
    “typ”: “JWT”
}

token = jwt.encode(claims_dict, 'key')  

It makes JWT verification just as easy.