Okay, so this is a rather different way of learning about JWTs. But I think, if done right it can teach more effectively than a long blog post filled with thousands of words. So, let's begin.
What are JWTs?
JWT or JSON Web Token is structured token format of encoding JSON data in a way that can be cryptographically verified.
JWT Structure
Example JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Notice the dots? If you "split" at dots you get 3 strings that make up the JWT.
So, JWT = headerString.payloadString.signatureString
(or signature, doesn't matter in this case)
What are these strings?
headerString = base64(header)
payloadString = base64(payload)
They are just base64 encodings for header & payload (header is a JSON object with metadata (type of JWT) & payload is another object with user-defined data with some standard keys).
Note: Here base64() implies base64URL(), assume them as functions that encode the object to base64 form, the later encodes it in URL-friendly manner. Similar assumptions ahead.
To keep things less repetitive ahead, let:
headerPayloadString = headerString.payloadString
We can form JWTs in different ways, for educational purpose lets look at a simple one (but not used IRL as much).
The signatureString/signature is discussed in following approach sections (sha256Signature, rsa256Signature).
The SHA256-HMAC Approach
SHA is a hashing algorithm. As mentioned this approach not used IRL as much given it's drawbacks discussed later.
Creating the JWT (signing)
sha256Signature = sha256(headerPayloadString , 'sharedSecret')
sha256JWT = headerPayloadString.sha256Signature
Verifying the JWT
- Split
headerPayloadString
&signatureString
(input signature) - Calculate signature as
sha256(headerPayloadString , 'sharedSecret')
- If both the input signature and calculated signature match, the JWT is valid.
Why do we not use this?
Notice how we need to use the same sharedSecret
or "key" for creating/signing & verifying? That means if we use an authentication server we have to share the same key between the server and the consumer/application. This leads to security concerns.
The RSA-SHA approach
RSA is public key encryption algorithm, consisting of 2 keys a private and public key (pair).
Creating the JWT (signing)
sha256XSignature = sha256(headerPayloadString)
We don't use a key here (non-HMAC way)
rsa256Signature = rsa256Encrypt(sha256XSignature, 'privateKey')
rsaJWT = headerPayloadString.rsa256Signature
Verifying the JWT
- Take the headerPayloadString, and hash everything with SHA-256
- Decrypt (rsa256Decrypt) the JWT using the public key, and obtain the rsa256Signature
- Compare the received JWT signature with the calculated one, if they match, the JWT is valid.
That Sums it Up
Voila! You now know the basic mechanics of JWTs. I recommended researching a bit more to supplement this post with. Hope, this helped clear some doubts.
Top comments (3)
Nice explanation, thanks for sharing. Expressing as functions is a smart way to explain the workings of jwt.
I would like to ammend that sha256 is widely used with the presharedkey. Whether that's useful depends on the usecase. If you have to validate something from one of your own servers, or from public servers than public key encryption (rsa, ecc) is used a lot. Indeed to mitigate the problems or giving away the secret as you stated.
When an application needs to validate input from its users, like in an api, the use of a psk in the form of an api-key is widely used. That api key is very often used to seed the sha256 algorithm (to not have to send the key readable over the wire) and as a means to validate the sender (because of the trust given to the knowing of the presharedkey) . Can it be done with public key infrastructure? Sure, but you can't expect every api-user to register a domain and host a public key and register this uri with your application. B2B solutions might adhere to this approach more but I haven't seen such complexity much in the wild compared to aforementioned methods.
Hope it helps and thanks again.
It does, and also, thanks for such a detailed comment!
nice job