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.
- The time since token generated is more than 2 minutes.
- When the
aud
claim is not verified - When the signature is invalid.
Else you can accept this request as legitimate and proceed to take further action.