Dan Imhoff

Developer and Photographer

31 December 2015
Introducing JWT: JSON Web Tokens

JSON Web Tokens are a somewhat new (May 2015) open standard for authenticating someone or something between two services without having them log in or dealing with sessions. They are best used in a microservices environment with a web application front-end.

The encoded token consists of three parts: the header, which contains meta information, the payload, which contains the identifying data of the user, and the signature, which is a cryptographic hash of the payload using a shared secret between the two services. No part of the token is encrypted, so by base64 decoding the payload, anyone can view it, but altering it in an attempt to forge a malicious token would result in an invalid payload/signature combo, and the service would reject the token.

Typical Flow

Typical Usage of JWTs

There are libraries for JWTs in almost every language. To demonstrate usage, I will be using Python and PyJWT. You can find a library for your language on JWT.io.

Step 1 should be already implemented. Your web application should have a user system that allows users to log in.

Step 2 may come as a surprise. A dedicated authentication server is not required to issue and verify the tokens. Any server that shares the secret key can issue and verify tokens. Typically, tokens are issued by your web application after users are successfully logged in.

token = jwt.encode({'uid': user.id}, 'foxtrot')
print(token)  # b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjF9.UiYGJGxrTO5tshcrkncONYqq7aIeLb6atZPDuLyG0Ng'

In this example, {'uid': user.id} is our payload and foxtrot is our secret key, never revealed to the user. PyJWT takes care of the rest, generating an encoded (not encrypted) JWT for the user. JWTs should be stored client-side so the front-end can make requests to the various microservices directly.

In step 3, the front-end makes a request with the user’s JWT. This is pretty simple. Just include the JWT in the request where the resource server is expecting it. Typically, the HTTP Authorization header works great, but JWTs are URL-safe, so if you’d like to send it as a URL parameter, you certainly can. It is common practice to use the Bearer scheme in the Authorization header, like so:

Authorization: Bearer <token>

Step 4 is important, and libraries vary in their implementations. Be sure the JWT signature is valid before doing anything with the payload.

auth_header = headers['Authorization']
token = auth_header[7:]  # Strip off 'Bearer '

try:
    payload = jwt.decode(token, 'foxtrot')
except jwt.InvalidTokenError:
    abort(401)  # Token is invalid, so the user is not authorized
else:
    user_id = payload['uid']
    # TODO: Do something with user_id!

That’s it. Once the token is verified and decoded, the user’s identifying information is used to scope resources to that user.

Advanced Usage

In the JWT specification, several registered claims are outlined that you can use in your JWTs. In the code above, we used a custom claim, uid, which is totally fine.

Libraries often provide additional support for these registered claims, such as automatically verifying the exp (Expiration Time) claim to see if a token has expired. More useful claims are iss (Issuer), which identifies which entity issued the token, iat (Issued At), which identifies when the token was issued. For exp, iat, and other date/time claims, use Unix timestamps. Here’s an example of token generation with custom and registered claims:

token = jwt.encode({'uid': 1, 'iss': 'my-app', 'exp': 1451638951}, 'foxtrot')
print(token)  # b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsImlzcyI6Im15LWFwcCIsImV4cCI6MTQ1MTYzODk1MX0.WXKc-CKRrj05SNmYqkTMVXKY9aUMu_2a7saXwotUcgQ'

— Dan Imhoff, 2015