Introduction To JWT
An introduction to JWT, complete with a simple web application playground
Introduction
From the introduction at
jwt.io
:
“JSON Web Token (JWT) is an open standard
(RFC 7519)
that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.”
So what is it really and how can you benefit from them?
This is the first post in a series of posts about JWTs. In case you are unfamiliar with JWT (JSON Web Tokens) you can think of them as credentials you can use whenever a proxy or stand-in need to represent a user of any kind; human, another microservice or another system altogether. Besides the token itself, they can contain additional information that is cryptographically verifiable, meaning the information is secure against tampering, whether it is authorization claims, service request data or any other information. In the next post I will explore the case of user-less interaction between microservices and how to establish trust between them.
The code for this article can be found here
Overview of JWT
Something or other
[overview images here]
Structure of a JWT
Let's backtrack a little and look at the structure of a JWT. Later we will look at how each part of the structure can be used.
The three parts of JWTs are:
- Header
- Payload
- Signature
When looking at a JWT, it usually looks something like this header.payload.signature with the 3 parts Base64 encoded and strung together with a . in between the parts. It would look something like this:
eyJraWQiOiJiN2RhMmI3MC1iMzA0LTRjMjYtYTY3Mi1hMDAyMzY5MTc1MTQiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJKd3RJbnRyb2R1Y3Rpb25sIiwic3ViIjoiY2xhZXNqb25zc29uIiwibmFtZSI6IkNsYWVzIEpvbnNzb24iLCJjYW5DcmVhdGVKV1QiOnRydWUsImlhdCI6MTQ2Njc5NjgyMiwiZXhwIjo0NjIyNDcwNDIyfQ.fjmTCp_6SLbn46optYs3-JSs1CqZ5bIT0NVyjsPm4G4i-fiOZyrkiEnjxKIey_lrfbJfgLrTzEjgK54t4AyZcZeH0VqyCzWjsHWlSQToRsWDGkD9vqq134JMsQjpZa8gYAIrpv7XBQO6Ap8Q_HhWSnHYlco6EFOO86ZExDedPhI
which would look like this when parsed:
{
"header": {
"alg": "RS256",
"kid": "b7da2b70-b304-4c26-a672-a00236917514"
},
"payload": {
"canCreateJWT": true,
"exp": 4622470422,
"iat": 1466796822,
"iss": "JwtIntroductionl",
"name": "Claes Jonsson",
"sub": "claesjonsson"
},
"signature": "fjmTCp_6SLbn46optYs3-JSs1CqZ5bIT0NVyjsPm4G4i-fiOZyrkiEnjxKIey_lrfbJfgLrTzEjgK54t4AyZcZeH0VqyCzWjsHWlSQToRsWDGkD9vqq134JMsQjpZa8gYAIrpv7XBQO6Ap8Q_HhWSnHYlco6EFOO86ZExDedPhI"
}
JWT Header
The header commonly contains the token type and the hashing algorithm used:
{
"typ": "JWT",
"alg": "RSA256"
}
This would also be the place to add custom headers; for instance, assuming the recipient of the JWT also expects to receive JWTs from other clients, adding a unique client id kid to the header, the recipient could use that id look up the correct public key to verify the signature with.
JWT Payload
The payload will contain claims, which are statements about something, often a user, and additional data and metadata. There are three types of claims:
- Reserved claims which are predefined claims, such as exp for expiration and sub for subject.
- Public claims can be anything. To avoid name collisions they should be defined in the IANA JSON Web Token Registry
- Private claims which are used to exchange agreed information
{
"canCreateJWT": true,
"exp": 4622470422,
"iat": 1466796822,
"iss": "JwtIntroductionl",
"name": "Claes Jonsson",
"sub": "claesjonsson"
}
JWT Signature
The signature is the result of base64 encoding the header, add a dot and concatenating the base64 encoded payload:
RSA256 (
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
privateKey
)
The receiver uses the signature to verify that the header and payload has not been tampered with.
Signing and/or encryption?
So, should we sign the JWT and/or encrypt the JWT?
Signing should always be done using either a pre-shared key (HMAC encryption) or using each clients unique private key, whose public counter part has to be explicitly known by the receiving microservice (RSA encryption). When using RSA encryption, the receiving microservice knows which public key to use for validation by checking the JWT standard kid (short for Key ID) header and looking up the clients public key.
Encrypting can also be done using a shared key or by using the receiving microservice public key, which of course has to be known by the sending microservice, and decrypted using the receivers private key. However, since communication between sender and receiver should always be protected by TLS, encrypting the JWT may be more complication than needed.
For maximum security, it is of course possible to encrypt JWT tokens, as well as signing them, and if you follow Jeff Bezos’ Mandate (originally “leaked” here ) that all service interfaces must be designed to be externalizable (which I very strongly recommend), it makes sense to do so, in order to protect the integrity of the service by signing, and the privacy of the client by encryption. An argument could be made that communicating over TSL is enough to ensure privacy, but encrypting the JWT is an added option to keep in mind for the security and privacy conscious. Here in the EU, with the specter of GDPR looming, some might consider encrypting JWTs a requirement.
Publicly accessible APIs
For APIs that are accessed publicly it is often a good idea to encrypt the JWT, although in some cases it may be more security than needed, especially with TSL only access. Signing is never optional, or we could seriously jeopardize the integrity of the service.
Private only APIs
For APIs that are not accessed publicly there is probably no need to encrypt the JWT. Here too signing should always be used, or little trust can be established, even with the use of JWTs.
See JWT in action
Requirements
In order to follow along with the examples in this tutorial, the following must be installed
The easiest way for the casual Docker user on Windows or Mac is to install the
Docker Toolbox
, but if you are more serious about Docker (and I would argue that you absolutely should be, but that's for another post), then install the packages for your respective platform. Start at the
Docker Store
(don't worry it's the free community edition)
Get the code for the playground
Clone the code for this tutorial:
git clone https://git.claesjonsson.net/shared/tutorials/jwt-introduction.git
Fire up the playground
- Spin up the containers running our simple JWT API
docker-compose up -d
- Run a shell in the client container. This is the container from which you will be calling the API, it has my favorite HTTP client installed HTTPie
docker exec -it httpjwtintro bash
You will be greeted with a prompt similar to this:
bash-4.3$
Now you are ready to explore JWTs, using the built in
HTTPie
client, which is a little friendlier than curl.
Try it out for yourself
To see an example of a JWT token, try the endpoint /get-example-jwt
$ http "http://jwtservice1/get-example-jwt"
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Tue, 05 Sep 2017 13:27:28 GMT
Transfer-Encoding: chunked
{
"jwt": "eyJraWQiOiJiN2RhMmI3MC1iMzA0LTRjMjYtYTY3Mi1hMDAyMzY5MTc1MTQiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJKd3RJbnRyb2R1Y3Rpb25sIiwic3ViIjoiY2xhZXNqb25zc29uIiwibmFtZSI6IkNsYWVzIEpvbnNzb24iLCJjYW5DcmVhdGVKV1QiOnRydWUsImlhdCI6MTQ2Njc5NjgyMiwiZXhwIjo0NjIyNDcwNDIyfQ.fjmTCp_6SLbn46optYs3-JSs1CqZ5bIT0NVyjsPm4G4i-fiOZyrkiEnjxKIey_lrfbJfgLrTzEjgK54t4AyZcZeH0VqyCzWjsHWlSQToRsWDGkD9vqq134JMsQjpZa8gYAIrpv7XBQO6Ap8Q_HhWSnHYlco6EFOO86ZExDedPhI",
"status": "SUCCESS"
}
To parse the example JWT:
$ http "http://jwtservice1/parse-jwt?jwt=eyJraWQiOiJiN2RhMmI3MC1iMzA0LTRjMjYtYTY3Mi1hMDAyMzY5MTc1MTQiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJKd3RJbnRyb2R1Y3Rpb25sIiwic3ViIjoiY2xhZXNqb25zc29uIiwibmFtZSI6IkNsYWVzIEpvbnNzb24iLCJjYW5DcmVhdGVKV1QiOnRydWUsImlhdCI6MTQ2Njc5NjgyMiwiZXhwIjo0NjIyNDcwNDIyfQ.fjmTCp_6SLbn46optYs3-JSs1CqZ5bIT0NVyjsPm4G4i-fiOZyrkiEnjxKIey_lrfbJfgLrTzEjgK54t4AyZcZeH0VqyCzWjsHWlSQToRsWDGkD9vqq134JMsQjpZa8gYAIrpv7XBQO6Ap8Q_HhWSnHYlco6EFOO86ZExDedPhI"
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Date: Tue, 05 Sep 2017 13:33:37 GMT
Transfer-Encoding: chunked
{
"jwsClaims": {
"body": {
"canCreateJWT": true,
"exp": 4622470422,
"iat": 1466796822,
"iss": "JwtIntroductionl",
"name": "Claes Jonsson",
"sub": "claesjonsson"
},
"header": {
"alg": "RS256",
"kid": "b7da2b70-b304-4c26-a672-a00236917514"
},
"signature": "fjmTCp_6SLbn46optYs3-JSs1CqZ5bIT0NVyjsPm4G4i-fiOZyrkiEnjxKIey_lrfbJfgLrTzEjgK54t4AyZcZeH0VqyCzWjsHWlSQToRsWDGkD9vqq134JMsQjpZa8gYAIrpv7XBQO6Ap8Q_HhWSnHYlco6EFOO86ZExDedPhI"
},
"status": "SUCCESS"
}
Client side vs Server side Sessions
TODO Write about client side vs server side sessions (JWTs can be used for client side - stored as cookie or in local storage) This includes a discussion on stateless services, and the benefit for scalability and durability, and the potentially sever security issues when using JWT for ‘stateless’ sessions. Also point to the blog post about Security Issues with JWT.
TODO Write about session state JWTs as cookies vs as special HTTP headers


Share this post
Twitter
Google+
Facebook
Reddit
LinkedIn
StumbleUpon
Email