JWT is an open standard for representing claims securely between two parties.

JWT website does a good job of explaining the concepts involved. This document tries to provide a practical explanation of how Setu does JWT.

When a web request is made to your service by Setu, the Authorization header is sent. This header looks like this

Authorization: Bearer <JWT_TOKEN>

The JWT TOKEN is of the format

<JWT_HEADER>.<JWT_PAYLOAD>.<SIGNATURE>

For example the header might look like this

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJaWEpoYkNJc0ltbGhkQ0k2TVRVeE5qSXpPVEF5TWl3aWFuUiIsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiNjljY2U3ZjMtOGRoZGMzNTEzZjdhIn0.ZP_V-4mM1ca8A-qZWr9R2lHLPPtEdyyLv97ENOf5iAc

This means that

JWT_HEADER: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
JWT_PAYLOAD: eyJhdWQiOiJaWEpoYkNJc0ltbGhkQ0k2TVRVeE5qSXpPVEF5TWl3aWFuUiIsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiNjljY2U3ZjMtOGRoZGMzNTEzZjdhIn0
SIGNATURE: ZP_V-4mM1ca8A-qZWr9R2lHLPPtEdyyLv97ENOf5iAc

JWT Header

This is a base64 encoded value.

For example:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

becomes

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

when decoded.

The header contains the algorithm used to calculate the SIGNAGTURE. In the above example it is HS256 (HMAC with SHA-256).

JWT payload

The JWT_PAYLOAD too a base64 encoded value.

For example:

eyJhdWQiOiJaWEpoYkNJc0ltbGhkQ0k2TVRVeE5qSXpPVEF5TWl3aWFuUiIsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiNjljY2U3ZjMtOGRoZGMzNTEzZjdhIn0

becomes

{"aud":"ZXJhbCIsImlhdCI6MTUxNjIzOTAyMiwianR","iat":1516239022,"jti":"69cce7f3-8dhdc3513f7a"}

when decoded.

Here

  • aud: This is known as the audience claim in JWT. You can think of it as the API key. This should be shared with Setu if Setu is making calls to your API. Setu would share this value, if you make calls to Setu APIs.
  • iat: This is the time at which the request was issued, in milliseconds since the UNIX epoch. Requests older than 120000 milliseconds (2 minutes) are considered stale, and hence must be rejected.
  • jti: Is the unique ID per request.

JWT signature

Using the algorithm specified in the header, the signature is constructed in the following way

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

The secret is a key shared between Setu and you. This signature can be used to verify if the claims being made in the payload are valid and you can accept of reject the incoming request based on the validity of the signature.

For example:

Input string: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJaWEpoYkNJc0ltbGhkQ0k2TVRVeE5qSXpPVEF5TWl3aWFuUiIsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiNjljY2U3ZjMtOGRoZGMzNTEzZjdhIn0
Secret: BBzKUWAZeAS2tBsk0FtJT4ep
HA256 Signaure: ZP_V-4mM1ca8A-qZWr9R2lHLPPtEdyyLv97ENOf5iAc

You can try this on your command line by running the following command

echo -n "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJaWEpoYkNJc0ltbGhkQ0k2TVRVeE5qSXpPVEF5TWl3aWFuUiIsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiNjljY2U3ZjMtOGRoZGMzNTEzZjdhIn0" | openssl dgst -sha256 -hmac BBzKUWAZeAS2tBsk0FtJT4ep -binary|openssl base64 -e -A | sed s/\+/-/ | sed -E s/=+$//

How we arrived at this bash command is explained here

Sample code

Practically, you never need to worry about the encoding/decoding of JWT. A lot of third party libraries exist that can do this for you easily in most programming languages.

For example here is some python code to generate a token.


import jwt
import datetime
api_key = "ZXJhbCIsImlhdCI6MTUxNjIzOTAyMiwianR"
secret = "BBzKUWAZeAS2tBsk0FtJT4ep"
payload = {
    "aud": api_key,
    "iat": datetime.datetime.utcnow(),
    "jti":"9012adddbcc-87hdc3513f7b"
}
token = jwt.encode(payload, secret, algorithm="HS256")

Here is how you can decode the token and verify the aud claim.


import jwt
api_key = "ZXJhbCIsImlhdCI6MTUxNjIzOTAyMiwianR1"
secret = "BBzKUWAZeAS2tBsk0FtJT4ep"
try:
   jwt.decode(token, secret, audience=api_key)
   print("verified")
   # verified claim
except jwt.PyJWTError:
    print("unverified")
    # unverified claim

You can reject a request if it meets any of the criteria.

  1. The time since token generated is more than 2 minutes.
  2. When the aud claim is not verified
  3. When the signature is invalid.

Else you can accept this request as legitimate and proceed to take further action.