When I started learning about JSON Web Tokens, there were some things that were straightforward to understand — and some concepts that felt like “hidden secrets” of JWT lore. 🧙♂️ This article aims to demystify how to sign and validate JSON Web Tokens, with little need for security or cryptography knowledge.
Note: This article is a companion to my post on Authorization and Authentication for Everyone. I recommend that you read that post first.
JWTs are — in general terms — reasonably approachable. Information about them is readily available from many sources, chiefly covering:
- JSON Web Tokens (or JWT) are a compact, URL-safe way to transfer pieces of data between two parties (such as an authorization server and an application).
- The JWT format is defined by IETF specification RFC 7519 and is composed of three segments (a header, a payload, and a crypto segment.
- JWTs are signed with a key when they are generated and then validated with a key upon receipt so we can verify that they haven’t been modified in transit.
There’s a meme about being introduced to a concept and then there are steps missing while you’re expected to be able to get to the end result without knowing the steps in between. The meme demonstrates “How to draw an owl”:
To me, not being able to find approachable information about how to sign and validate JSON Web Tokens felt like the missing steps to draw the owl.
Most of the resources I dug up took me deep down the rabbit hole — until my head was swimming with mind-melting jargon, Alices and Bobs (placeholder names used in cryptography examples), and complex maths. Instead of learning simpler, incremental steps to aid in my understanding, it felt like I was being given ten more complex, complete owl drawings.
Why is this? Why do most JWT resources simply say “and then sign and validate the JSON Web Tokens” and leave it at that?
Think of it this way: for human beings to be effective or skilled with a tool, we don’t need to know the intricacies of the tool’s components.
🚙 When you get into your car to drive to the grocery store, do you need to know how internal combustion works? You don’t.
🔋 When you charge your laptop, do you need to know what chemical reactions take place in a lithium ion battery to create the flow of electrons in a circuit to store and produce energy? Nope!
We can still be good drivers or great computer programmers without intimate knowledge of these things. We trust that the manufacturers have used their expertise, specifications and standards, and due diligence to make useful tools for us to be more effective at the jobs we need those tools for.
This is why you don’t need to know the exact process for signing and validating JWT in order to effectively use them for authenticating and authorizing your applications and APIs.
Okay, but you’re reading this because you still want to know the missing steps to draw the owl. While you generally should not sign and validate tokens yourself (seriously, leave this to the experts — identity providers and Identity-as-a-Service platforms!), knowing how it works can be helpful in making you feel more comfortable with using JWTs. 😌
I covered the anatomy of JWT in depth in the previous blog article on authentication and authorization, but let’s do a very brief recap.
JSON Web Tokens are composed of three URL-safe string segments concatenated with periods
The first segment is the header segment:
The header segment is a JSON object containing a signing algorithm and token type. It is
base64Url encoded (byte data represented as text that is URL and filename safe).
Decoded, it looks something like this:
The second segment is the payload segment:
This is a JSON object containing data claims, which are statements about the user and the authentication event. This is the information that the JWT is conveying from one entity to another. Data claims might look something like this:
"name": "John Doe",
This is also
The final segment is the crypto segment, or signature. JWTs are signed so they can’t be modified in transit. When an authorization server issues a token, it signs it using a key.
When the client receives the ID token, the client validates the signature using a key as well. (If an asymmetric signing algorithm was used, different keys are used to sign and validate; if this is case, only the authorization server holds the ability to sign tokens.)
This is where the missing steps of the
owl JWT signing / validation process are. Let’s go through it step-by-step.
For this article, I’m going to assume use of an RS256 signing algorithm. RS256 is an RSA Digital Signature Algorithm with SHA-256. RS256 is an Asymmetric Key Cryptography algorithm, which uses a pair of keys: a public key and a private key to encrypt and decrypt. In this case, the private key is used by the token issuer (authorization server), and the public key is used by the application receiving the token in order to validate it.
If this feels jargon-laden and confusing right out of the gate, don’t worry! Let’s break it down into smaller, distinct steps.
Let’s explore what the crypto segment actually is.
First we take the first two segments of the JWT (the header and the payload). In practice, that looks something like this:
In other words, this is the
base64Url encoded header and the
base64Url encoded payload, concatenated with a
This is what we call the signing input.
We then hash the signing input using the SHA-256 hashing algorithm. Hashing converts one value into a different value. A hash function uses a mathematical algorithm to generate a new value from an existing one.
- Hashing is irreversible. Once we have hashed an input, we cannot get back to the original input again.
- Hashing will always produce the same output for the same input.
- No two different hashing inputs will produce the same output.
At this point, we have a hash of the header and payload segments — which we could compare to other hashes, but cannot reverse to return to the original signing input.
Next we encrypt the hashed signing input. Unlike hashing, encryption is reversible: we can decrypt encrypted results (called ciphertext) to decipher the original input (plaintext).
Note: I mentioned earlier that this example uses RSA, which is an asymmetric signing algorithm and uses one key to encrypt tokens, and another key to decrypt them. To learn more about this topic in an approachable, practical way, check out Public Key Cryptography by Computerphile on YouTube.
This final output (the hashed, encrypted, encoded header and payload) is the crypto segment — or signature — of the JWT.
There you have it. That’s the signature of a JSON Web Token. 🎉
Okay, we can start to understand how the token was signed — but that’s only half of the story! The entire point of signing the token is so that whoever receives the token can validate that this JWT contains data that hasn’t been tampered with.
How do applications validate JWTs?
First, let’s look at what information is available to the application that receives the token. We have access to the JWT itself: the header, the payload, and the signature (aka, the crypto segment). We also have access to a public key, which — as per its moniker — is freely available to the world.
Note: How are public keys made available? JSON Web Key (JWK) RFC 7517 defines a specification for JSON representation of a cryptographic key. If you use an authorization server platform, the public key will be provided — and often helpfully encapsulated in an SDK or library that handles JWT validation for you. You can read more in this blog article: Navigating RS256 and JWK.
Remember that the signature is the encrypted, hashed header and payload. Hashing is irreversible, but encryption can be decrypted (in this case, with the public key). Validation of the JWT is about getting to a point where we can effectively compare what we received to what we expect.
The application can decode the header and payload to get some information. Recall that these two segments are
base64Url encoded to make them URL safe. There is nothing cryptographically magical or secure about decoding these segments; in fact, you can do so with a simple online base64 decoding tool. Once they’re decoded, we can easily read the information in them. For instance, we could decode the header segment to see what algorithm the JWT says it was signed with.
On the other hand, the signature‘s purpose is to validate that the information in the other segments is the same information that the authorization server sent — and that it hasn’t been changed along the way.
From the decoded header, we can see:
Our received token says that the algorithm is RS256. Our application should be configured to expect the algorithm that our authorization server (token issuer) uses. When we read the algorithm in the JWT’s header, we should verify that it matches our configured expectation. If it doesn’t match, we should reject the token outright.
If the algorithm in the token matches our expectation of RS256, we know we need to generate the SHA-256 hash of the header and payload segments. This will reproduce the signing input hash (again). Remember that hashing is irreversible, but the same input will always produce the same output. If we generate the hash from the header and payload we received, it should match the header+payload hash that is encrypted in the signature.
Therefore, we will hash the concatenated,
base64Url encoded header and payload. Now we have the signing input hash freshly calculated on the application side.
The hashed signing input is also in the signature, but it’s been encrypted by the authorization server (token issuer) with a private key. The application has the public key, so we can decrypt the signature. Once this is done, we have access to the original hash: the one generated by the authorization server when the token was first generated.
Now we can compare the decrypted hash to the calculated hash. If they are the same, then we’ve verified that the data in the JWT header and payload segments has not been modified between the time the authorization server created the token and the time our application received it. 🎊
Verifying Token Claims
Once we’ve validated the signature, there’s more to verifying the JSON Web Token’s data. Claims in the payload segment should also be validated, because they contain information about the token issuer, token expiration, intended audience for the token, information binding the token to the authorization request, and more.
This data gives the receiving application vital details that the signature validation alone does not. For instance, examination of claims can reveal that a technically valid token was actually intended for a different application or user, has expired, came from an issuer that the application has no affiliation with, etc.
We’ve now covered signing JWT and validating JWT signatures.
My hope is that you feel confident your understanding of JWT signatures and validation has a few more steps filled in now:
It’s still important to reiterate that, as a developer, you most likely will never need to implement these processes yourself. And in fact, unless your engineering focus is security and identity, you probably shouldn’t.
Identity platforms like Auth0, Okta, Ping Identity, and more do all of this for you: issuing and signing tokens on the authorization server side, and providing SDKs and libraries for validation and token management on the application or API side.
As I mentioned at the beginning, this article is a companion and supplement to Authorization and Authentication for Everyone, which is a much more comprehensive resource on the history of OAuth, OpenID Connect, authorization servers, JSON Web Tokens, API authorization, delegation with scopes, and more. Please check it out if you’d like to go further into identity topics.
In an effort to create a digestible introduction to signing and validating JWTs that is widely approachable (and is the introduction I wished I’d had), I have simplified topics that are incredibly rich and complex.
🧠 To learn much more, try starting with some of the following resources:
- Public Key Cryptography
- What Are Encryption Keys and How Do They Work?
- The RSA Encryption Algorithm – Part 1
- The RSA Encryption Algorithm – Part 2
- Learn Identity
If you’d like to chat, I’m available on Twitter at @KimMaida, and I also speak at conferences and events — at least, I did before COVID-19. I hope to see you sometime, and thank you so much for reading!