JSON web tokens (JWTs) are an open standard for securely transmitting data as a JSON object between parties in a compact and self-contained format.
Knowledge of JWTs is important because most modern systems and tools use them for secure, efficient and scalable authorization. Knowing about JWTs will also help you understand how third-party integrations with other software work.
In this article, you will learn what a JWT is, including its benefits, structure and potential risks and the common mistakes with it during implementation.
The following definition is from 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 or ECDSA.
A JWT was designed to be used to send compact self-contained data over the internet, but it’s popularly used for authorization. A JWT is signed with a private key that can be used to verify if the token is valid (has not been tampered with).
As they are Base64 encoded, JWTs are compact and can be attached as part of an HTTP request.
Using JWTs for authorization is primarily intended to avoid a deadlock situation where the user is authenticated in one server but cannot be authorized in another server because the other server does not have access to the previous server session. Traditionally, session management was handled using cookies that servers send to browsers. These cookies are sent to the server whenever any request is made, then the server uses the cookie to restore the session of that user. In this era of distributed computing and microservices, using cookies to manage sessions will result in problems such as the following:
Users need to sign in too frequently because their requests are routed to different servers.
A user is locked to the particular server on which they started their session, which defeats the purpose of distributed computing.
The diagram above illustrates how a simple distributed system works. A client makes a request to the system, and the reverse proxy determines which server handles the request.
With JWTs, all the information needed to maintain a session is stored on the client, making JWTs stateless. So, if the user is routed to another server, the user only needs to present the token, which has the required information needed to continue the session.
JWTs have many applications. The following are just some examples of what they can do.
Application Authorization: JWTs can be used as part of your authentication flow. When you login with your email and password, the system checks if you are a registered user. The process of identifying that you are a registered user is called authentication. Once you are authenticated by the system, the system generates a JWT that the client will use for subsequent requests when trying to access a resource in the system.
Personal Access Tokens (PATs): PATs are tokens which a user generates from an application dashboard. These tokens can then be used by multiple clients to gain access to the target service. JWTs can be used for the implementation of PATs.
OAuth: OAuth is usually used when you want to allow an application you are logged in to access your information in another application. In this scenario the application that contains the resource generates a JWT for the other application to use when trying to access the resource.
This section highlights some significant benefits of using JWTs, which have contributed to their popularity.
JWTs are verifiable and trusted because they are digitally signed, which means they are secure. JWTs can be signed with a secret using the HMAC 256 algorithm or signed using a public and private key pair using ECDSA or RSA. You can find out more about signing algorithms here.
When any part of the JWT is modified, it renders the token invalid because the secret key used to sign the modified data won’t match the signature of the original data.
JWTs can also be encrypted to provide secrecy between parties.
The data contained in a JWT Token is JSON data. The JSON data is then encoded using Base64 encoding. This makes the token compact and small enough to be transmitted through URL parameters, HTTP headers or POST parameters. The token can still exceed the maximum length that can be transferred over HTTP when too many claims are contained within, so it’s also up to you to make sure that the token is compact.
JWTs are very flexible because they allow for the declaration of custom claims. Custom claims enable you to assign granular roles to a particular user based on the level of control the user needs or has earned.
JSON is very popular in different areas of computing; for example, most REST APIs respond with JSON, which is usually used for configuration files like package.json in NPM. As JSON is widely used, many tools and programming languages have very good support for it, which makes it easier to work with. JWTs’ compact nature also makes them easy to process for clients such as browsers and resource-constrained mobile devices.
JWTs are used for authorizing users and granting or restricting access to resources.
You can think of a token as an ID card. New hires for a company usually meet HR personnel to identify and onboard them on their first day. The identification and onboarding process is analogous to the authentication process you go through when you log in or register to a web service.
After the authentication (identification and onboarding) process, new employees will most likely be issued an ID card that will likely contain the following information: company name, company signature, employee name, employee role, employee number, employee photo and expiry date (valid until a specific date). Once issued an ID card, the employee can access offices that their role permits them to access. If their ID card bears the role of CEO, they’ll probably have access to all departments in the company, but if it bears “software engineer,” they’ll be granted access only to the software engineering department. They continue to have access until the validity period of their ID card has expired.
The ID card analogy applies to JWTs. Once you authenticate, a JWT, which contains information about who you are (including your roles and privileges) will be created for you. You will need to present your token anytime you need access to any resource.
Having an ID card is also beneficial when a company has a new branch. The new branch might not have data about you, but they can use your ID card to grant you the right access. The above point applies to JWTs too. If a new server, feature or service is added to the larger system, you will be authorized successfully because all the information needed for authorization is encoded in the JWT.
The major difference between an ID card and a JWT is in the topic of signature. An ID card can easily be faked, but a JWT can’t be faked because the data is signed and a signature is generated based on the content of the data and the secret or private/public key used to sign it. The signature makes sure that the token being sent was not manipulated.
A JWT is made up of three parts separated by dots — the header, payload, and signature — which looks like
The header of a JWT is a Base64URL-encoded JSON object that contains
alg refers to the type of algorithm used for signing.
typ refers to the type of JWT.
The code below shows what a decoded JWT header looks like:
The payload is often referred to as the body. The payload consists of claims, which are statements about a user that are added as part of the payload during the creation of the token. These claims are represented as properties in the JSON payload. Information such as user roles, username or email are examples of some information that can be set as claims. These claims are used to identify a user and what they are authorized to do. There are different types of claims: registered, public and private.
These are a set of predefined (registered in IANA JSON Web Token Registry) claims that are recommended to provide a set of useful, interoperable claims. Some of these include
aud (Audience), and
sub (Subject). You can learn more about registered claims here.
These are claims that are defined at will but should be defined as a URL to prevent collisions using namespaces. Public claims can also be defined in the IANA Web Token Registry.
Private claims are custom claims agreed upon by parties involved to share information. There are claims that are neither registered nor public.
The code block below shows an example of a JWS payload including claims:
A decoded payload can look like the above.
The signature is a very important component of the token as it is used to verify that the integrity of the token is maintained. If the header or payload is tampered with, then the existing signature won’t match the signature generated based on the altered header or payload.
At the left-hand side, you can see the encoded JWT. Three items in the image have been highlighted:
The red part of the token is the Base64URL-encoded form of the header at the right side. The purple part is the Base64URL-encoded form of the payload. Finally, at the bottom right corner, you should see the function used to create the token.
Even though JWTs can be a quite secure approach to authorization, there are some potential risks to using them.
If you fail to verify the signature, then the data being read might be compromised. This is one of the risks of using JWTs. A hacker could potentially gain access to an admin role by tampering with the JWT payload. This is why it’s necessary to always verify a JWT before carrying out further operations.
None algorithm is one of the major ways hackers exploit systems that rely on JWTs. A user can alter their token and change the algorithm in the header to
none, thereby bypassing token verification against the secret or private or public key used to sign it.
You can mitigate this threat by treating all tokens with
none algorithms as invalid. Some libraries and frameworks already help to protect against this.
The following are examples of common mistakes when implementing JWT.
Using weak token secrets: Weak tokens can easily be guessed or might be obtained through a brute-force approach.
Using a long expiry time: While a token is still active (has not expired), it can always be used for authorization. Creating tokens with a long expiration time is risky because if a third-party obtains the token, they can always be authorized as long as the token is active.
Using JWTs as session cookies: JWTs can function completely independently of cookies. There is no need to use JWTs as cookies to maintain sessions.
Not using appropriate storage: Where JWTs should be stored has been a topic of debate. Storing JWTs in local storage is not the best option as they can be easily accessed by third parties. Recommended storage locations include session storage that clears once the browser or tab is closed and encrypted storage in other client devices such as mobile and desktop.
Not validating JWTs: JWTs should be verified to ensure that they can be trusted; applications will provide a JSON Web Key Set (jwks) endpoint to verify that the JWT can be trusted.
Sharing tokens over HTTP: HTTPS is recommended over HTTP because the communication is secured by TLS. HTTP is prone to MITM (man in the middle) attacks. A hacker can easily get your token when sniffing your network traffic. Once the hacker has gotten the token, they can always impersonate you until the expiry time of the token.
In this article, you learned what a JWT is, how it is structured and its benefits. You also learned about some of the potential risks associated with JWTs and common mistakes people make while using them.
Remoteler is a platform that provides a very easy and secure way to access your infrastructure, such as your servers, Kubernetes cluster, Applications (with JWT Support!), databases and a lot more, in a variety of ways.