· engineering  · 8 min read

Decentralize trust with Json Web Tokens

Exploring JWTs. What they are, where they're used, and what needs to be considered in their implementation.

Exploring JWTs. What they are, where they're used, and what needs to be considered in their implementation.

Have you ever wondered, when you arrive at the airport, how interesting a passport is?

It’s a trusted document, whose authenticity can often be difficult to verify by those it’s presented to.
The entire value of the passport is based on trust in the issuing authority, and not the bearer.

When computers talk, they face a similar dilemma.
How can we be sure a client claiming access to our resources is truly authorized?

In some cases it’s simple, we might be able to ask for credentials, or expect them to provide a session cookie, but these rely on the system being able to authenticate the user. What do we do in a system where we cannot authorize users ourselves, for example when using microservices?

To avoid constantly verifying identities in computer communication, we need a similar process to passports. We need a system that acts like a government, verifying your identity (authentication) and issuing a trustworthy passport (token). This passport can then be presented to further systems who will trust it and grant or deny access to resources (authorization) accordingly.

This process exists in various forms, including the implementation we’ll look at in this article, Json Web Tokens (JWT).

Anatomy of a JWT

Json Web Tokens (JWTs) are a technique for authorization between parties, and they work independently of authentication (confirming user identity). They provide a secure way to establish a user’s access rights to resources without needing to maintain any session state on a server. First proposed in December 2010, they took a while to gain traction, but the last decade has seen their usage increase dramatically.

At its simplest, a JWT is just three strings, each base64url encoded and joined with a period.

The first string is the ‘header’.
This contains information on which algorithm was used to create the signature, the most common of which are HS256 and RS256.
HS256 is symmetrical; the same key used to create the signature is also needed to verify it, whereas RS256 uses an asymmetric RSA key, with a public and private part.
Of the two, RSA encryption should be preferred, as recipient systems are able to validate the token without being able to issue it.

Next comes the ‘payload’.
The payload can contain custom fields, for example message payload or user details, but will also include some standard fields.
iat (issued at), exp (expiration time) and nbf (not before) can be used to verify whether the token is yet/still valid, whereas iss (issuer), sub (subject) and aud (audience) can be used to help decide whether to grant/deny access to resources. The audfield in particular is important, as it can be used to determine whether the token issued is intended for use with the recipient.

Finally, we have the ‘signature’.
This is the value of a string formed from the header and payload, each base64url encoded and joined with a period, encrypted using the method defined in the header, and the shared (or private) key.
It is the encryption of the header and payload together which prevents tampering with either and gives a JWT its security.

At this point, it is worth mentioning, that although a JWT looks meaningless, it is due to the base64 url encoding and is NOT encrypted.
The content of your payload and header are visible to anyone who wants to look.
Always be sure to use https, and do not put any confidential information in your payload (unless it is encrypted separately beforehand).

Notable use cases

JWTs are used extensively throughout the web and beyond for authorization. Whether it’s a mobile app talking to an api, a desktop app communicating with peers, or a web user doing some shopping, wherever there’s a need for decentralized trust, wherever a user needs to access information from a system that itself is unable to verify their identity, JWTs are usually present.

They provide the underlying security in OpenID connect - the technology behind single sign on - wherever you see a button inviting you to “login with google” - there’s JWTs behind the scenes.

Another great use case for JWTs is in microservices. Not only when communicating form client to server, but also when communicating between services - JWT can help you establish a “zero trust architecture”, requiring all requests to be authorized, even if you ought to be able to trust their source.
An example of this would be requiring payloads of messages in a queue, or inter-services requests, to be in a signed JWT format.

As an aid to scalability JWTs are also a great choice. Their stateless, self contained manner, allows them to be verified by any server, without it needing to look the user up in a database. They do away with the need for sticky sessions, tables, or session caches, and the authorization code can be separated from the rest of the logic on an infrastructure level (and cached based on the header value), further aiding scalability.

JWT in AWS

Json Web Tokens are used extensively in AWS.
A notable use case is the managed authorization service cognito, which uses JWT when federating identity with other SSO providers, and in the token issued to the user. This JWT token can be used directly in various places, for example with the apiGateway cognito authorizer.

The information contained in a cognito JWT can also be used as the basis of calculating a SigV4 signature for use with other AWS services, for example as pre-signed-urls for cloudfront and s3. SigV4 is a method Amazon uses to sign requests, allowing secure, time limited, yet anonymous, requests to be made with a single url parameter (the signature). More about sigV4 here.

JWT forms the basis for the authentication process when establishing an IAM Identity providers - which allows us the bearer of a token from a third parts to assume an IAM role. The privileges of this role can be determined based on the claims made in the JWT when authenticating.

Elsewhere in AWS JWT is a great solution for authorization. Due to it’s stateless manner, understandable implementation and customizable payloads, it is ideal for implementing authorization, for example in cloudfront (using edge functions) or api gateway with authorization lambdas. These methods can pre-authorize traffic, separating the concerns of authorization and business logic. This doesn’t just improve code reusability and maintainability, but can have a significant benefit when it comes to scaling applications.

Drawbacks

Like all technologies, JWTs have their drawbacks. Though they’re relatively easy to describe and understand, the concrete implementations of them can be error prone or contain serious flaws (for example, for a while certain libraries would accept an algorithm type of “none” - leading to absolutely no validation of the signature).

But leaving aside the potential for error during their implementation, and misunderstandings around their unencrypted plain text nature (the token payload is fully legible to any recipient), there are some issues inherent in the design of JWTs that are important to know about.

The stateless nature of Json Web Tokens, that makes them so useful and ubiquitous in the first place, is also the source of their main disadvantages, JWTs can inherently be read and replayed by anyone until their expiration date, and cannot be revoked.

So it is essential that any communication with JWT is done securely, and that they are treated securely by the browser.

When issuing JWTs think carefully about how long they should be valid for.
A common pattern is for a trust provider to issue two tokens, a short lived token for accessing resources, and a longer lived token which can be used to refresh itself and the short lived tokens. This pattern helps to mitigate against replay attacks by reducing the time frame in which they can occur.

Alternative techniques include creating a signed request from a token, as AWS does with SigV4 (see above), or by including custom fields in the token payload that make them single use.

Of course it goes without saying that the security of your JWT relies heavily on the encryption method chosen, and the security applied to the keys used to sign it. Keep your keys safe, rotate them, and don’t chose your encryption method based on how easy it is to implement!

Conclusion

When used correctly, JWT is one of the best techniques currently available to create trust in a decentralized computing landscape.

With attention to detail, consideration for the potential drawbacks, awareness of it’s plain text like nature, and use of alternatives where necessary, it is possible to create a secure, zero trust architecture that is easy to maintain and implement and scales well. JWT as a standard is still developing, and the future is likely to hold further development, around encryption methods, as well as facilitating new use cases, for example secure data transfer.

JWTs can be easily read by anybody, and thus debugging them is quite easy, I highly recommend a visit to jwt.io fof further info on JWTs, a list of reliable libraries, and an excellent interactive debugger.

The full specification of Json Web Tokens is currently defined in the RFC 7519 standard.

James Babington

About James Babington

A cloud architect and engineer with a wealth of experience across AWS, web development, and security, James enjoys writing about the technical challenges and solutions he's encountered, but most of all he loves it when a plan comes together and it all just works.

Comments

No comments yet. Be the first to comment!

Leave a Comment

Check this box if you don't want your comment to be displayed publicly.

Back to Blog

Related Posts

View All Posts »
Using Eventbridge Scheduler

Using Eventbridge Scheduler

EventBridge Scheduler offers one-off events, flexible scheduling, and direct integration capabilities that will simplify and even replace existing time-delayed solutions and Lambda functions.

Solving our CMS Downtime with NGINX

Solving our CMS Downtime with NGINX

How we used NGINX's reverse proxy features to entirely mitigate errors caused by downtime of our headless CMS effectively, whilst always serving the most recent content.