Breaking Down JSON Web Tokens. From Pros and Cons To Building and Revoking
Breaking Down JSON Web Tokens. From Pros and Cons To Building and Revoking
Tokens
From pros and cons to building and
revoking
Anatomy of a JWT . . . . . . . . . . . . . . . . . . . . . . . . 34
The header . . . . . . . . . . . . . . . . . . . . . . . . . . 35
CONTENTS
The body . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Signature . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
What are JWTs
First things first. JSON Web Tokens, or JWTs, are pronounced ‘jot’,
not J-W-T. You’re welcome!
JWTs encapsulate arbitrary JSON in a standardized way, and
are useful to communicate between different parts of a software
system.
They are an IETF standard. The main RFC is 7519, but there are
others as well. RFC 7515, RFC 7516, RFC 7517, RFC 7518 and RFC
7520 all concern this technology in one way or another.
There are two kinds of JWTs: signed and encrypted.
Signed JWTs allow you to cryptographically verify the integrity
of the JWT. That means you can be assured the contents are
unchanged from when the signer created it. However, signed JWTs
do not protect the data carried from being seen; anyone who
possesses a JWT can see its content. You don’t want to put anything
in a JWT that should be a secret or that might leak information.
Encrypted JWTs, on the other hand, have a payload that cannot be
read by those who do not possess the decryption key. If you have
a payload that must be secret and both the creator and recipient of
the JWT support it, encrypted JWTs are a good solution.
In general, signed JWTs are far more common. Unless otherwise
noted, if this book uses the term JWT, it refers to a signed JWT.
JWTs are often used as stateless, portable tokens of identity. This
usage will be the focus of this book, but what does that actually
mean?
The combination of these attributes mean that JWTs are great for
transporting identity information to different services. One service
may authenticate the user and create a JWT for the client, and
then other services, which offer different functionality and data
depending on who the user is, can consume that JWT. This works
especially well for APIs and microservices, which have minimal
information about the user in their datastore. This is why many
auth servers, also known as identity providers, issue JWTs.
You can sign a JWT with either a symmetric or asymmetric algo-
rithm. Using a symmetric algorithm will be faster, but has signif-
icant security and operational ramifications. This is not unique to
JWTs, because a symmetric algorithm like HS256 requires a shared
secret. Therefore, when using a symmetric algorithm, any con-
sumer of a JWT can also create JWTs indistinguishable from those
created by an identity provider. Therefore, asymmetric solutions
are recommended, even though they are slower. If performance is
critical, make sure you benchmark your system to understand how
signing algorithm choice affects both creation and consumption of
JWTs.
Not all bearer tokens are JWTs and not all JWTs are bearer tokens,
but the use case is common enough that it is worth mentioning. A
bearer token is like a car key. If I have a car key, that gives me access
to the car. The key doesn’t care if I’m the owner, a friend or a thief.
What are JWTs 3
The combination of these attributes mean that JWTs are great for
transporting identity information to different services. One service
may authenticate the user and create a JWT for the client, and
then other services, which offer different functionality and data
depending on who the user is, can consume that JWT. This works
What are JWTs 5
have yet to run into a major language that didn’t have at least one
library for creating and parsing JWTs. And, because JWTs depend
on cryptographic operations and are often used as temporary
credentials, using a well known, well vetted open source library to
interact with JWTs is a good idea.
In the rest of this book, we’ll cover different aspects of JSON Web
Tokens and systems that use them.
Building a Secure Signed
JWT
JSON Web Tokens (JWTs) get a lot of hate online for being insecure.
Tom Ptacek, founder of Latacora⁵, a security consultancy, had this
to say about JWTs in 2017⁶:
Definitions
• creator: the system which creates the JWTs. In the world of
OAuth this is often called an Authorization Server or AS.
• consumer: a system which consumes a JWT. In the world of
OAuth this is often called the Resource Server or RS. These
consume a JWT to determine if they should allow access to a
Protected Resource such as an API.
• client: a system which retrieves a token from the creator,
holds it, and presents it to other systems like a consumer.
• claim: a piece of information asserted about the subject of the
JWT. Some are standardized, others are application specific.
Out of scope
This article will only be discussing signed JWTs. Signing of JSON
data structures is standardized⁸.
There are also standards for encrypting JSON data⁹ but signed
tokens are more common, so we’ll focus on them. Therefore, in this
article the term JWT refers to signed tokens, not encrypted ones.
⁸https://tools.ietf.org/html/rfc7515
⁹https://tools.ietf.org/html/rfc7516
Building a Secure Signed JWT 9
Security considerations
When you are working with JWTs in any capacity, be aware of
the footguns that are available to you (to, you know, let you shoot
yourself in the foot).
The first is that a signed JWT is like a postcard. Anyone who has
access to it can read it. Though this JWT string may look illegible,
it’s trivial to decode:
1 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJmdXNpb25h\
2 dXRoLmlvIiwiZXhwIjoxNTkwNzA4Mzg1LCJhdWQiOiIyMzhkNDc5My03M\
3 GRlLTQxODMtOTcwNy00OGVkOGVjZDE5ZDkiLCJzdWIiOiIxOTAxNmI3My\
4 0zZmZhLTRiMjYtODBkOC1hYTkyODc3Mzg2NzciLCJuYW1lIjoiRGFuIE1\
5 vb3JlIiwicm9sZXMiOlsiUkVUUklFVkVfVE9ET1MiXX0.8QfosnY2Zled\
6 xWajJJqFPdEvrtQtP_Y3g5Kqk8bvHjo
You can decode it using any number of online tools¹⁰, because it’s
just three base64 encoded strings joined by periods.
Keep any data that you wouldn’t want in the hands of someone else
outside of your JWT. When you sign and send a token, or when you
decode and receive it, you’re guaranteed the contents didn’t change.
But you’re not guaranteed the contents are unseen.
A corollary of that is that any information you do send should
avoid unintentional data leakage. This includes identifiers. If a JWT
includes a value like 123 for an id, that means anyone viewing it has
a pretty good idea that there is an entity with the id of 122. Use a
GUID or random string for identifiers to avoid this issue. Likewise,
because tokens are not encrypted, use TLS for transmitting them.
Don’t send JWTs using an HTTP method that may be cached or
logged. So don’t append the token to a GET request as a parameter.
If you must send it in a GET request, use an HTTP header. You can
¹⁰https://fusionauth.io/learn/expert-advice/dev-tools/jwt-debugger
Building a Secure Signed JWT 10
also use other HTTP methods such as POST, which sends the JWT
as a part of a request body. Sending the token value as part of a GET
URL might result in the JWT being stored in a proxy’s memory or
filesystem, a browser cache, or even in web server access logs.
If you are using OAuth, be careful with the Implicit grant, because
if not implemented correctly, it can send the JWT (the access token)
as a request parameter or fragment. While ignored by proxies, that
can be cached by browsers and accessed by any JavaScript running
on a page. For example, the Docusign esign REST API¹¹ delivers the
access token as a URL fragment. Oops.
Creating tokens
When you are creating a JWT, use a library. Don’t implement this
RFC yourself. There are lots of great libraries out there¹². Use one.
Set the typ claim of your JWT header to a known value. This
prevents one kind of token from being confused with one of a
different type.
Signature algorithms
When using JWTs, choose the correct signing algorithm. You have
two families of options, a symmetric algorithm like HMAC or an
asymmetric choice like RSA or elliptic curves (ECC). The "none"
algorithm, which doesn’t sign the JWT and allows anyone to
generate a token with any payload they want, should not be used
and any JWTs that use this signing algorithm (which actually
means they aren’t signed) should be rejected immediately.
¹¹https://developers.docusign.com/esign-rest-api/guides/authentication/oauth2-
implicit#step-1-obtain-the-access-token
¹²https://openid.net/developers/jwt/
Building a Secure Signed JWT 11
Performance
1 hmac sign
2 4.620000 0.008000 4.628000 ( 4.653274)
3 hmac verify
4 6.100000 0.032000 6.132000 ( 6.152018)
5 rsa sign
6 42.052000 0.048000 42.100000 ( 42.216739)
7 rsa verify
8 6.644000 0.012000 6.656000 ( 6.665588)
9 ecc sign
10 11.444000 0.004000 11.448000 ( 11.464170)
11 ecc verify
12 12.728000 0.008000 12.736000 ( 12.751313)
Operational concerns
Security ramifications
The shared secret required for options like HMAC has security im-
plications. The token consumer can create a JWT indistinguishable
from a token built by the creator, because both have access to the
algorithm and the shared secret. This means you’ll need to secure
both the creator and the consumer of your tokens equally.
By using public/private key cryptography to sign the tokens, the
issue of a shared secret is bypassed. Because of this, using an
asymmetric option allows a creator to provide JWTs to token
¹⁴https://tools.ietf.org/html/rfc7517
Building a Secure Signed JWT 13
consumers that are not trusted. No system lacking the private key
can generate a valid token.
Claims
Make sure you set your claims appropriately. The JWT specification
is clear:
Revocation
Because it is difficult to invalidate JWTs once issued–one of their
benefits is that they are stateless, which means holders don’t need
Building a Secure Signed JWT 14
to reach out to any server to verify they are valid–you should keep
their lifetime on the order of second, minutes or hours, rather than
days or months.
Short lifetimes mean that should a JWT be stolen, the token will
soon expire and no longer be accepted by the consumer.
But there are times, such as a data breach or a user logging out of
your application, when you’ll want to revoke tokens, either across
a system or on a more granular level.
You have a few choices here. These are in order of how much effort
implementation would require from the token consumer:
Keys
It’s important to use a long, random secret when you are using a
symmetric algorithm. Don’t choose a key that is in use in any other
system.
Longer keys or secrets are more secure, but take longer to generate
signatures. To find the appropriate tradeoff, make sure you bench-
mark the performance. The JWK RFC¹⁵ does specify minimum
lengths for various algorithms.
The minimum secret length for HMAC:
The minimum key length for ECC is not specified in the RFC. Please
consult the RFC for more specifics about other algorithms.
You should rotate your token signing keys regularly. Ideally you’d
set this up in an automated fashion.
Rotation renders all tokens signed with the old key invalid (unless
the consumer caches the keys), so plan accordingly. It’s best to allow
for a grace period equal to the lifetime of a JWT.
Holding tokens
Clients request and hold tokens, which then then present to con-
sumers to gain access to protected data or resources.
A client can be a browser, a mobile phone or anything else. A client
receives a token from a token creator (sometimes through a proxy
or a backend service that is usually part of your application).
Clients are then responsible for two things:
Consuming a JWT
Tokens must be examined as carefully as they are crafted. When
you are consuming a JWT, verify the JWT to ensure it was signed
correctly, and validate and sanitize the claims.
Just as with token creation, don’t roll your own implementation;
use existing libraries.
¹⁶https://gitlab.com/jimdigriz/oauth2-worker
¹⁷https://developer.android.com/training/articles/security-tips#StoringData
¹⁸https://developer.android.com/reference/android/content/SharedPreferences
¹⁹https://developer.apple.com/documentation/security/keychain_services
Building a Secure Signed JWT 17
First, verify that the JWT signature matches the content. Any
library should be able to do this, but ensure that the algorithm that
the token was signed with, based on the header, is the same used
to decode it.
In addition, verify the kid value in the header; make sure the key id
matches the key you are using the validate the signature. It’s worth
mentioning again here that any JWTs using the none algorithm
should be rejected immediately.
Once the signature checks out, validate the claims are as expected.
This includes any implementation specific registered claims set on
creation, as well as the issuer (iss) and the audience (aud) claims. A
consumer should know the issuer it expects, based on out of band
information such as documentation or deploy time configuration.
Checking the aud claim ensures the JWT is meant for you.
Other claims matter too. Make sure the typ claim, in the header, is as
expected. Check that the current time is within the JWT’s lifetime;
that is that ‘now’ is before the exp value and after the nbf value, if
present. If you’re concerned about clock skew, allow some leeway.
If any of these claims fail to match expected values, the consumer
should provide only a minimal error message to the client. Just as
authentication servers should not reveal whether a failed login was
due to an non-existent username or invalid password, you should
return the same error message and status code, 403 for example, for
any invalid token. This minimizes the information an attacker can
learn by generating JWTs and sending them to a consumer.
If you are going to use claims for further information processing,
make sure you sanitize those values. For instance, if you are going
to query a database based on a claim, use a parameterized query.
Building a Secure Signed JWT 18
In conclusion
JWTs are a flexible technology, and can be used in many ways.
We discussed a number of steps you can take, as either a creator,
client or consumer of tokens, to ensure your JWTs are as secure as
possible.
Pros and Cons of JWTs
This chapter provides an analysis of JWTs (JSON Web Tokens,
pronounced “jot”) from how they are used to pros and cons of using
JWTs in your application.
JWTs are becoming more and more ubiquitous. Customer identity
and access management (CIAM) providers everywhere are pushing
JWTs as the silver bullet for everything.
JWTs are pretty cool, but let’s talk about some of the downsides of
JWTs and other solutions you might consider.
One way to describe JWTs is that they are portable units of identity.
That means they contain identity information as JSON and can
be passed around to services and applications. Any service or
application can verify a JWT itself.
The service/application receiving a JWT doesn’t need to ask the
identity provider that generated the JWT if it is valid. Once a JWT
is verified, the service or application can use the data inside it to
take action on behalf of the user.
Here’s a diagram that illustrates how the identity provider creates
a JWT and how a service can use the JWT without calling back to
the identity provider: (yes that is a Palm Pilot in the diagram)
Pros and Cons of JWTs 20
JWT Example
When you contrast this with an opaque token, you’ll see why
so many developers are using JWTs. Opaque tokens are just a
large string of characters that don’t contain any data. A token
must be verified by asking the identity provider if it is still valid
and returning the user data the service needs. This is known as
introspection.
Here’s a diagram that illustrates how the identity provider is called
to verify the opaque token and fetch the user data:
the tokens inside the identity provider. JWTs on the other hand
don’t require any persistence or logic in the identity provider since
they are portable and standalone. Once a JWT has been issued, the
identity provider’s job is done.
There are a couple of things you should consider when deciding to
use JWTs. Let’s look at a few of the main ones.
Sessions as an alternative
Instead of using JWTs or opaque tokens, you always have the option
of using sessions. Sessions have been around for over two decades
and are proven technology. Sessions generally work through the
use of cookies and state that is stored on the server. The cookie
contains a string of characters that is the session id. The server
contains a large hash that keys off the session id and stores arbitrary
data safely server-side.
When a user logs in, the user object is stored in the session and
the server sends back a session cookie that contains the session id.
Each subsequent request to the server includes the session cookie.
Pros and Cons of JWTs 24
The server uses the session cookie to load the user object out of
the session Hash. The user object is then used to identify the user
making the request.
Here are two diagrams that illustrate this concept:
Login
Second request
Revoking JWTs
The Todo Backend in the diagram can use the JWT and the public
key to verify the JWT and then pull the user’s id (in this case the
subject) out of the JWT. The Todo Backend can then use the user’s
id to perform operations on that user’s data.
However, because the Todo Backend isn’t verifying the JWT with
the IdP, it has no idea if an administrator has logged into the IdP
and locked or deleted that user’s account.
What are some solutions to this issue?
Rotate keys
If the Todo Backend pulls in the keys from the IdP every time it
validates the signature of the JWT, then if the key is removed, the
JWT will not validate.
Assume a JWT has a key identifier (kid) of abcd. During normal
operation, the Todo Backend requests the list of public keys, and
that includes one with the kid of abcd. That key can then be used
to validate the signature of the JWT.
If the IdP removes the key abcd from the list, the JWT will no longer
have a valid signature.
This approach has two downsides:
• The Todo Backend must continually poll the IdP for a list of
valid keys. This obviates some of the distributed benefits of
JWTs.
• All JWTs signed by the removed key are rendered invalid.
This will affect many users.
Revoking JWTs & JWT Expiration 29
1 {
2 "event": {
3 "type": "jwt.refresh-token.revoke",
4 "applicationTimeToLiveInSeconds": {
5 "cc0567da-68a1-45f3-b15b-5a6228bb7146": 600
6 },
7 "userId": "00000000-0000-0000-0000-000000000001"
8 }
9 }
Here is how the JWTManager maintains the list of user ids whose
JWTs should be revoked.
This implementatin doesn’t depend on unique ids in the JWT, but
rather on the expiration time of the JWT and the time to live value
configured for the application which created the JWT. You could
also use the unique id of the JWT, often found in the jti claim, to
implement this without the datetime math.
Our implementation also starts a thread to clean up after itself so
we don’t run out of memory.
1 const JWTManager = {
2 revokedJWTs: {},
3
4 /**
5 * Checks if a JWT is valid. This assumes that the JWT \
6 contains a property named <code>exp</code> that is a
7 * NumericDate value defined in the JWT specification a\
8 nd a property named <code>sub</code> that is the user id \
9 the
10 * JWT belongs to.
²¹https://github.com/FusionAuth/fusionauth-node-client
Revoking JWTs & JWT Expiration 31
11 *
12 * @param {object} jwt The JWT object.
13 * @returns {boolean} True if the JWT is valid, false i\
14 f it isn't.
15 */
16 isValid: function(jwt) {
17 const expiration = JWTManager.revokedJWTs[jwt.sub];
18 return expiration === undefined || expiration === nul\
19 l || expiration < jwt.exp * 1000;
20 },
21
22 /**
23 * Revokes all JWTs for the user with the given id usin\
24 g the duration (in seconds).
25 *
26 * @param {string} userId The user id (usually a UUID a\
27 s a string).
28 * @param {Number} durationSeconds The duration of all \
29 JWTs in seconds.
30 */
31 revoke: function(userId, durationSeconds) {
32 JWTManager.revokedJWTs[userId] = Date.now() + (durati\
33 onSeconds * 1000);
34 },
35
36 /**
37 * Cleans up the cache to remove old user's that have e\
38 xpired.
39 * @private
40 */
41 _cleanUp: function() {
42 const now = Date.now();
43 Object.keys(JWTManager.revokedJWTs).forEach((item, in\
44 dex, _array) => {
45 const expiration = JWTManager.revokedJWTs[item];
Revoking JWTs & JWT Expiration 32
Our backend must ensure that it checks JWTs with the JWTMan-
ager on each API call. This becomes another part of the claims
validation process.
Conclusion
To answer the question that started this chapter, you can “sorta”
revoke JWTs.
Because they are self-contained, you need to build additional
functionality to ensure that the 5 to 10 minute window can be
effectively shut. This is one of the tradeoffs of using JWTs for
authorization purposes.
Anatomy of a JWT
In this chapter, you’ll learn more about what goes into a JSON
Web Token and how they are constructed. Here’s an example JWT,
freshly minted:
1 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY1ODg5MGQxO\
2 SJ9.eyJhdWQiOiI4NWEwMzg2Ny1kY2NmLTQ4ODItYWRkZS0xYTc5YWVlY\
3 zUwZGYiLCJleHAiOjE2NDQ4ODQxODUsImlhdCI6MTY0NDg4MDU4NSwiaX\
4 NzIjoiYWNtZS5jb20iLCJzdWIiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDA\
5 wMC0wMDAwMDAwMDAwMDEiLCJqdGkiOiIzZGQ2NDM0ZC03OWE5LTRkMTUt\
6 OThiNS03YjUxZGJiMmNkMzEiLCJhdXRoZW50aWNhdGlvblR5cGUiOiJQQ\
7 VNTV09SRCIsImVtYWlsIjoiYWRtaW5AZnVzaW9uYXV0aC5pbyIsImVtYW\
8 lsX3ZlcmlmaWVkIjp0cnVlLCJhcHBsaWNhdGlvbklkIjoiODVhMDM4Njc\
9 tZGNjZi00ODgyLWFkZGUtMWE3OWFlZWM1MGRmIiwicm9sZXMiOlsiY2Vv\
10 Il19.dee-Ke6RzR0G9avaLNRZf1GUCDfe8Zbk9L2c7yaqKME
This may look like a lot of gibberish, but as you learn more about
JWTs, it begins to make more sense.
There are a few types of JWTs, but I’ll focus on signed JWTs as they
are the most common. A signed JWT may also be called a JWS. It
has three parts, separated by periods.
There’s a header, which in the case of the JWT above, starts with
eyJhbGc. Then there is a body or payload, which above starts with
eyJhdWQ. Finally, there is a signature, which starts with dee-K in the
example JWT.
Let’s break this example JWT apart and dig a bit deeper.
Anatomy of a JWT 35
The header
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY1ODg5MGQxOSJ9
is the header of this JWT. The header contains metadata about a
token, including the key identifier, what algorithm was used to
sign in and other information.
If you run the above header through a base64 decoder:
1 echo 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY1ODg\
2 5MGQxOSJ9'|base64 -d
1 {"alg":"HS256","typ":"JWT","kid":"f58890d19"}%
HS256 indicates that the JWT was signed with a symmetric algo-
rithm, specifically HMAC using SHA-256.
The list of algorithms and implementation support level is below.
The kid value indicates what key was used to sign the JWT. For
a symmetric key the kid could be used to look up a value in a
secrets vault. For an asymmetric signing algorithm, this value lets
the consumer of a JWT look up the correct public key corresponding
to the private key which signed this JWT. Processing this value
correctly is critical to signature verification and the integrity of the
JWT payload.
Typically, you’ll offload most of the processing of header values to
a library. There are plenty of good open source JWT processing
libraries. You should understand the values, but probably won’t
have to implement the actual processing.
The body
The payload, or body, is where things get interesting. This section
contains the data that this JWT was created to transport. If the JWT,
for instance, represents a user authorized to access certain data or
functionality, the payload contains user data such as roles or other
authorization info.
Here’s the payload from the example JWT:
1 eyJhdWQiOiI4NWEwMzg2Ny1kY2NmLTQ4ODItYWRkZS0xYTc5YWVlYzUwZ\
2 GYiLCJleHAiOjE2NDQ4ODQxODUsImlhdCI6MTY0NDg4MDU4NSwiaXNzIj\
3 oiYWNtZS5jb20iLCJzdWIiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0\
4 wMDAwMDAwMDAwMDEiLCJqdGkiOiIzZGQ2NDM0ZC03OWE5LTRkMTUtOThi\
5 NS03YjUxZGJiMmNkMzEiLCJhdXRoZW50aWNhdGlvblR5cGUiOiJQQVNTV\
6 09SRCIsImVtYWlsIjoiYWRtaW5AZnVzaW9uYXV0aC5pbyIsImVtYWlsX3\
7 ZlcmlmaWVkIjp0cnVlLCJhcHBsaWNhdGlvbklkIjoiODVhMDM4NjctZGN\
8 jZi00ODgyLWFkZGUtMWE3OWFlZWM1MGRmIiwicm9sZXMiOlsiY2VvIl19
1 echo 'eyJhdWQiOiI4NWEwMzg2Ny1kY2NmLTQ4ODItYWRkZS0xYTc5YWV\
2 lYzUwZGYiLCJleHAiOjE2NDQ4ODQxODUsImlhdCI6MTY0NDg4MDU4NSwi\
3 aXNzIjoiYWNtZS5jb20iLCJzdWIiOiIwMDAwMDAwMC0wMDAwLTAwMDAtM\
4 DAwMC0wMDAwMDAwMDAwMDEiLCJqdGkiOiIzZGQ2NDM0ZC03OWE5LTRkMT\
5 UtOThiNS03YjUxZGJiMmNkMzEiLCJhdXRoZW50aWNhdGlvblR5cGUiOiJ\
6 QQVNTV09SRCIsImVtYWlsIjoiYWRtaW5AZnVzaW9uYXV0aC5pbyIsImVt\
7 YWlsX3ZlcmlmaWVkIjp0cnVlLCJhcHBsaWNhdGlvbklkIjoiODVhMDM4N\
8 jctZGNjZi00ODgyLWFkZGUtMWE3OWFlZWM1MGRmIiwicm9sZXMiOlsiY2\
9 VvIl19' |base64 -d
1 {
2 "aud": "85a03867-dccf-4882-adde-1a79aeec50df",
3 "exp": 1644884185,
4 "iat": 1644880585,
5 "iss": "acme.com",
6 "sub": "00000000-0000-0000-0000-000000000001",
7 "jti": "3dd6434d-79a9-4d15-98b5-7b51dbb2cd31",
8 "authenticationType": "PASSWORD",
9 "email": "[email protected]",
10 "email_verified": true,
11 "applicationId": "85a03867-dccf-4882-adde-1a79aeec50df",
12 "roles": [
13 "ceo"
14 ]
15 }
Note that the algorithm to create signed JWTs can remove base64
padding, so there may be missing = signs at the end of the JWT. You
may need to add that back in order to decode a JWT. This depends
on the length of the content. You can learn more about it here²².
As mentioned above, the payload is what your application cares
²²https://datatracker.ietf.org/doc/html/rfc7515#appendix-C
Anatomy of a JWT 39
about, so let’s take a look at this JSON more closely. Each of the
keys of the object are called “claims”.
Some claims are well known with meanings dictated by standards
bodies such as the IETF. You can view examples of such claims
here²³. These include the iss and aud claims from the example
token. Both of these have defined meanings when present in the
payload of a JWT.
There are other non-standard claims, such as authenticationType.
These claims may represent business domain or custom data. For
example, authenticationType is a proprietary claim used by Fusio-
nAuth to indicate the method of authentication, such as password,
refresh token or via a passwordless link.
You may add any claims you want to a JWT, including data useful
to downstream consumers of the JWT. As you can see from the
roles claim, claims don’t have to be simple JSON primitives. They
can be any data structure which can be represented in JSON.
Claims to verify
• nbf and exp. These claims determine the timeframe for which
the token is valid. The nbf claim can be useful if you are
issuing a token for future use. The exp claim, a time beyond
which the JWT is no longer valid, should always be set. Unlike
other claims, these have a defined value format: seconds since
the unix epoch.
should receive this JWT, but the code already has it. Nope, always
verify this claim.
Why?
Imagine a scenario where you have two different APIs. One is to
create and manage todos and the other is a billing API, used to
transfer money. Both APIs expect some users with a role of admin.
However, that role means vastly different things in terms of what
actions can be taken.
If both the todo and billing APIs don’t verify that any given JWT
was created for them, an attacker could take a JWT from the todo
API with the admin role and present it to the billing API.
This would be at best a bug and at worst an escalation of privilege
with negative ramifications for bank accounts.
Signature
The signature of a JWT is critical, because it guarantees the integrity
of the payload and the header. Verifying the signature must be the
first step that any consumer of a JWT performs. If the signature
doesn’t match, no further processing should take place.
While you can read the relevant portion of the specification²⁴ to
learn how the signature is generated, the high level overview is:
Limits
In the specifications, there are no hard limits on length of JWTs. In
practical terms, think about:
Storage
JWTs can be sent in HTTP headers, stored in cookies, and placed
in form parameters. In these scenarios, the storage dictates the
maximum JWT length.
For example, the typical storage limit for cookies in a browser
is typically 4096 bytes, including the name. The limit on HTTP
headers varies widely based on software components, but 8192
bytes seems to be a common value.
Consult the relevant specifications or other resources for limits
in your particular use case, but rest assured that JWTs have no
intrinsic size limits.
Performance penalty
Since JWTs can contain many different kinds of user information,
developers may be tempted to put too much in them. This can
Anatomy of a JWT 43
1 hmac sign
2 1.632396 0.011794 1.644190 ( 1.656177)
3 hmac verify
4 2.452983 0.015723 2.468706 ( 2.487930)
5 rsa sign
6 28.409793 0.117695 28.527488 ( 28.697615)
7 rsa verify
8 3.086154 0.011869 3.098023 ( 3.109780)
9 ecc sign
10 4.248960 0.017153 4.266113 ( 4.285231)
11 ecc verify
12 7.057758 0.027116 7.084874 ( 7.113594)
1 hmac sign
2 3.356960 0.018175 3.375135 ( 3.389963)
3 hmac verify
4 4.283810 0.018320 4.302130 ( 4.321095)
5 rsa sign
6 32.703723 0.172346 32.876069 ( 33.072665)
7 rsa verify
8 5.300321 0.027455 5.327776 ( 5.358079)
9 ecc sign
10 6.557596 0.032239 6.589835 ( 6.624320)
11 ecc verify
12 9.184033 0.035617 9.219650 ( 9.259225)
You can see that the total time increased for the longer JWT, but
typically not linearly. The increase in time taken ranges from about
20% for RSA signing to approximately 100% for HMAC signing.
Be mindful of additional time taken to transport longer JWT; this
can be tested and optimized in the same way you would with any
other API or HTML content.
Conclusion
Signed JWTs have a header, body, and signature.
Understanding all three of these components are critical to the
correct use of JWTs.
Conclusion
This book has covered a fair bit of ground on the topic of JSON
Web Tokens (JWTs). I hope you enjoyed learning more about this
powerful token format and have a better understanding of this
technical topic. At the end of the day, JWTs are another tool, and
the goal is not to use them everywhere, but to feel more comfortable
about when JWTs make sense and when they do not.
You also learned about revocation solutions and the implementa-
tion tradeoffs available for this vital function.
As a widely supported, understandable, flexible way to transport
information that can be cryptographically verified, JWTs deserve a
place in your developer toolbelt. I hope this book helped you decide
that they’ve earned it.