OVO Tech Blog
OVO Tech Blog

Our journey navigating the technosphere

Share


Tags


Json Web Tokens

What is a JWT?

JWT stands for JSON Web Token and is defined in RFC7517 as:

   JSON Web Token (JWT) is a compact, URL-safe means of representing
   claims to be transferred between two parties.  The claims in a JWT
   are encoded as a JSON object that is used as the payload of a JSON
   Web Signature (JWS) structure or as the plaintext of a JSON Web
   Encryption (JWE) structure, enabling the claims to be digitally
   signed or integrity protected with a Message Authentication Code
   (MAC) and/or encrypted.

Mostly you will see JWTs used in OAuth 2 frameworks, within which there are different types of tokens; Refresh Tokens, Access Tokens, Identity Tokens and JWT be used can represent them all.

JWT and OAuth are often confused with each other, OAUTH is an authorization framework that enables applications to obtain limited access to user accounts, this access is provided using tokens, these tokens can be JWT, but they do not have to be.

Why use JWTs

The big win for users of JWTs is that you do not have to go back to the issuer, on every request, to validate the contents of a token or to find out more about the subject. Imagine the following scenario:

If the JWT (Access/Bearer Token) is included in the request to the service, it could just interrogate the token for a 'backend-service' > 'create-items' role, and if this role is present process the request, otherwise reject it would reject it with an unauthorized response.

This is opposed to approaches where a session token is passed to the service and it has to hit the issuing service with the session token to obtain information on the user (subject).

Obviously the service cannot just trust any old JWT token it receives, it must be verified (verification is covered at the end if you make it that far).

Structure

So what is actually in a JWT?

A JWT is made up of:

Let's look at these in a bit more detail

Here is an example header from a JWT Bearer (Access) token generated using a locally running instance of Keycloak:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "n_TdQEebp0Y3TiG9vTUA8QaG73jj-zATdFrto0dimNg"
}

As per RFC7517 (detailed above) the JWT can be signed as a JWS (JSON web Signature), which is what Keycloak does, in this case the header contains information on how the token was signed and what it is.

Payload

Here is an example payload from a JWT Bearer (Access) token generated using a locally running instance of Keycloak:

{
  "jti": "316fec1f-6d70-406a-bca3-b95ddd9546e3",
  "exp": 1525435773,
  "nbf": 0,
  "iat": 1525435713,
  "iss": "http://localhost:8080/auth/realms/master",
  "aud": "my-product",
  "sub": "2bdf4147-fb94-471b-827f-634ab1899b73",
  "typ": "Bearer",
  "azp": "my-product",
  "auth_time": 0,
  "session_state": "c2229bb7-5e52-483a-80c2-c2a990ed75eb",
  "acr": "1",
  "allowed-origins": [],
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    },
    "backend-service": {
      "roles": [
        "create-items",
        "delete-items"
      ]
    }
  },
  "name": "Steve Fleetwood",
  "preferred_username": "steve.fleetwood",
  "given_name": "Steve",
  "family_name": "Fleetwood",
  "email": "my.email@ovoenergy.com"
}

The JSON object is made up of the claims contained in the token, there are different types of claims within the payload:

Registered Claims

These are essentially reserved claims in OAUTH, but are still optional:

Private claims

These are claims that are agreed between issuer and consumers of the tokens.

In the example from Keycloak you can see various private claims, a couple of interesting ones being:

Other pretty self explanatory private claims in the Keycloak example are name, preferred_username, given_name , family_name, email

Public claims

Public claims are again claims that are determined by the issuer, but they must be named in such a way to prevent collisions with other public claims.

What does a JWT look like

The above examples are what the parts of a JWT token look like when they have been parsed, the format of actual token when it is passed around in requests, stored in cookies, etc is explained below.

JWS - JSON Web Signature

When a JWT is issued as a JWS it looks like this:

eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJuX1RkUUVlYnAwWTNUaUc5dlRVQThRYUc3M2pqLXpBVGRGcnRvMGRpbU5nIn0.eyJqdGkiOiJhYmRhMDJhNi1lM2IzLTRhYTQtOTJiYy04ZTY0MDM1YjRkMDMiLCJleHAiOjE1MjU0Mzc1MTMsIm5iZiI6MCwiaWF0IjoxNTI1NDM1NzEzLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoibXktcHJvZHVjdCIsInN1YiI6IjJiZGY0MTQ3LWZiOTQtNDcxYi04MjdmLTYzNGFiMTg5OWI3MyIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJteS1wcm9kdWN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiYzIyMjliYjctNWU1Mi00ODNhLTgwYzItYzJhOTkwZWQ3NWViIiwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfSwibXktcHJvZHVjdCI6eyJyb2xlcyI6WyJjcmVhdGUtaXRlbXMiLCJkZWxldGUtaXRlbXMiXX19fQ.kZxN2l55dUI--U-6rXAs3T110hHHylPRKV-a_9V1jwMwPCG-FfYNEfuC4ro9fPb51r5WO2lTetkCSHDh1mJeUIW4kpvYAhWkjfx0IHgupPDQrZyAe7zy8piyJqPk4Mhp6h1qRCUXH68I4qwoWu5IE8XaK1znU6jcvs20Dtu1Dl81xrp9Wqp5Xj658fuNj4r0guPxGnskjmrHPJt_0e_7lzs3wA51qVadUeW7KMusP1IBdMlpuk0LxTK5bz5QoeumwGDym31LsQ617JTE3RTY2x3XtPNHwjoYiQfsatfXGXziayOch8AsoQ37-orDvr_oj_rn3RAydNvmwrI6qcBLxQ

It is massive, but is made up of the header, payload and signature parts, Base64 encoded and separated by a ., basically aaaaaaaaaa.bbbbbbbb.cccccccc

Signature

As in the Keycloak example above, the signature is a hashed value, generated from the specified algorithm and secret, applied to the encoded Header and Payload, it looks like:

✻ Signature bsIJ3Wle9_n5VK0_fWK9VwyxQUR2MpUDjOBIQBc-Rvv71cF2qdDVw9PIFKkFdPPKkNGrsfMjpMal_2IeW9UtUmnfV7b6O0q2GLtk5GV7jsBDZaMG4nO1XhBhU0wsRT3soORxap3HqSDeX93VLOOeAq4XCx0koW6oUpDCEQcu-b8Ns-nin6CiZyI2S_pgP8dvH2jncvBl_OBT2j9BxjPYIQadmPyE4xief4UUyz_PA_Iw48P21tVviOaq-H8_uZSZ3FoWAtD4Ygz8XUalylXe5RntB_jYU4vcz5KCBLvcVJVQX2cqRSP7ulYJ5q3vDgaAgGkG4A7oPulGeHiGNukxWw

Note - As more claims are added to a JWT the bigger the JWS gets, so care should be taken if storing these in cookies, etc where the is a size limit.

JWE - JSON Web Encryption

Whereas JWS tokens are Base64 encoded, meaning the contents can be read by anyone, JWE tokens are encrypted with the intention that they can contain sensitive values in the claims, that only the intended receiver can decrypt and read.

I am going to skip how this encryption in implemented for now, as is a blog post in it's own right, and this post is dry enough as it is :)

Verification

Before trusting the contents of a JWT, some steps to ensure that the token has been issued by the correct party, is still valid and hasn't been modified could be:

That is a lot of stuff to check, but luckily there are libraries in most languages that can do this for you, see https://jwt.io/ which has a nice list of them.

View Comments