What is JWT?
JWT, which stands for JSON Web Token, is an open standard for securely sharing JSON data between parties. The data is encoded and digitally signed, which ensures its authenticity. JWT is widely used in API authentication and authorization workflows, as well as for data transfer between clients and servers.
In this article, we’ll explain how JWT works and explore its three-part structure. We’ll then discuss its use cases and review some benefits, challenges, and best practices for working with it.
How does JWT work?
Transmitting data across multiple systems can be risky, as this data can be intercepted and modified before it gets to the receiver. The sender must therefore take steps to ensure that the data is securely transmitted, and the receiver must be able to confirm that the data is from the expected sender and that it was not modified during transport.
JWT helps solve these challenges by providing an elegant, secure way of transferring statements (referred to as “claims”) about an entity (typically the user) between two parties. Claims in JWT are key-value pairs that are represented with either JWS (JSON Web Signature) or JWE (JSON Web Encryption). JWS and JWE are standards for digitally signing and encrypting JWTs, which helps ensure their authenticity and integrity.
The key difference between JWS and JWE is that JWS focuses on authenticity, which means anyone with the JWT can read the payload. JWE, on the other hand, ensures that only authorized parties can access the payload. A JWT is considered unsecured when it is not represented by either JWS or JWE.
What is the structure of a JWT?
A JWT has three sections: a header, a payload, and a signature. Each section is a Base64-encoded string, and the sections are separated by periods. A typical JWT looks like this, where the X’s represent the header, the Y’s represents the payload, and the Z’s represents the signature:
xxxxxx.yyyyyy.zzzzzz
A more explicitly written JWT would look something like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNjk3MjM5MDIyfQ.5CerSPBCrO_3WdiyPjR7HoWBOeXsuq2AcfplJeG7erc
Let’s review each of these sections in detail.
Header
The header contains information about the overall JWT, such as the main algorithm (alg
) used for signing and encryption (required), the media type of the JWT (typ
), and its content type (cty
). A decoded JWT header might look like this:
{ "alg": "HS256", "typ": "JWT" }
Payload
The payload consists of all of the data that’s being transmitted. This data is optional, and it is added as claims. There are three primary types of claims: registered claims, private claims, and public claims.
Registered claims
These are optional but pre-determined claims that are defined in the JWT specification and support interoperability. There are seven types of registered claims:
- Issuer (
iss
): The party that issued the JWT. - Subject (
sub
): The subject of the JWT or the entity this JWT carries information about. - Audience (
aud
): The recipient of the JWT. - Expiration time (
exp
): The time at which the JWT expires. - Not before(
nbf
): The time before which the JWT is invalid. - Issued at (
iat
): The time at which the JWT was issued. - JTW ID (
jti
): A string that acts as the unique identifier for this JWT.
Private claims
Private claims, which are used to represent the data being transported, are defined by the sender or receiver of the JWT and are therefore not registered in the JWT specification. They have specific use cases and must be structured to make sure they do not collide with registered claims. For example, you can include user profile information—such as email, name, and permissions—in your JWT as private claims.
Public claims
Public claims are not registered with the JWT specification, but they have been registered with the IANA (Internet Assigned Number Authority). The IANA has a JSON Web Token Claims Registry, where users can register their claims to prevent collisions with other claims that are used globally. In practice, however, most claims are either registered or private claims.
Signature
The signatures in JWT are generated using a cryptographic algorithm like HS256 (HMAC using SHA-256). The JWA (JSON Web Algorithm) standard defines a list of algorithms that can be used for signing a JWT.
The signature is created using the algorithm specified in the header, as well as the encoded header, encoded payload, and a secret. The signature is a one-way hash, which means it cannot be reversed and the original data cannot be retrieved from the generated signature.
The secret used in the signature is a shared secret between the party that issues the token and the party that is verifying the token. In some cases, such as when JWT is used for authorizing user login sessions by the same server, the issuer of the token is also the verifier of the token.
During verification, the recipient generates a new signature with the secret that was provided by the issuer. If this signature matches the signature that was received in the JWT, it means the JWT has not been altered.
What are JWT’s use cases?
JWT is widely used to manage user authentication and authorization in web applications. When a user logs in to an application, the server issues a JWT and sends it back to the client. This JWT can contain basic information about the user, as well as the user’s roles and permissions. The JWT is then stored on the client side, and it is sent along with subsequent requests for verification purposes. This is a stateless form of authentication and authorization, as the server does not need to maintain session state for each user.
JWT is also used to securely share data between applications and microservices. The data transmitted in a JWT can be encrypted to prevent third parties from reading it, and it can be signed to prove its authenticity. This makes it a very efficient mechanism for securely sharing data while maintaining trust.
Where can JWT be used?
JWT can be used alongside several other protocols or standards where it is important to maintain data integrity. Some of them include:
- OAuth 2.0: OAuth 2.0 is a widely used authorization framework that allows third-party applications to get consented access to a user’s data. JWTs can be used to issue and verify access and refresh tokens in OAuth 2.0.
- OpenID Connect (OIDC): OpenID Connect is an identity layer built on top of OAuth 2.0, and it uses JWT for identity, authentication, and authorization. In an OIDC context, an identity provider issues JWTs as identity tokens, which are used to represent identity information in a standardized format. Access tokens in OIDC are also JWTs.
- SAML (Security Assertion Markup Language): SAML is an XML-based standard for exchanging authentication and authorization data between parties. JWTs can be used within SAML assertions to carry user and attribute information, providing an alternative to XML-based data formats and enhancing compatibility with modern web applications.
- Federated identity and single sign-on (SSO): JWTs can be used to convey identity information and securely authenticate users across multiple applications or services in federated identity scenarios and single sign-on ecosystems. JWTs are often used for identity token exchange between identity providers and relying parties.
What are the benefits of working with JWT?
As discussed, the primary benefit of JWT is its ability to facilitate the secure transfer of data between two parties. However, there are several other benefits of JWT, such as:
- Statelessness: JWTs are stateless, which means that they contain all of the necessary information for authentication and authorization. This reduces the server’s need to maintain session state for each user, which is particularly advantageous in distributed and microservice-based architectures.
- Efficiency: JWTs are compact and lightweight, reducing the overhead of transmitting data with each request. For instance, all claims are purposely kept at three letters to further reduce their size. This efficiency is especially beneficial in scenarios with bandwidth and latency constraints.
- Interoperability: JWTs are based on widely adopted and standardized formats, making them compatible with various programming languages and platforms. This enables developers to implement JWT-based authentication and authorization across different services and systems.
- Cross-domain communication: JWTs can be used for securely sharing information between different parts of a web application or between services running on different domains. This eliminates the need for cookies or server-side sessions and simplifies cross-origin communication.
What are some challenges of working with JWT?
While JWT has a lot to offer when it comes to authentication and authorization, it can be challenging to work with. Some challenges of working with JWT include:
- Security risks: JWTs must be handled with care. Serious security breaches can occur if the token’s secret key is compromised or if security measures are not correctly implemented. For instance, if a JWT is not properly validated, an attacker could forge tokens or impersonate users.
- Size and efficiency: Although JWTs are designed to be compact, they can still become relatively large when they contain multiple claims. Transmitting large tokens in every request can increase network overhead and potentially affect performance.
- No invalidation mechanism: JWTs are stateless, so there’s no built-in mechanism to invalidate a token before it expires. If a token is compromised or if a user’s access rights change, you must wait for the token to naturally expire—or maintain an external mechanism for tracking and blacklisting tokens.
- Storage on the client: Storing JWTs securely on the client side can be challenging. For instance, JWTs can be vulnerable to cross-site scripting (XSS) attacks if they are stored in a way that allows malicious scripts to access the tokens.
- Limited central control: Ensuring consistent and secure handling of JWTs across all services in a distributed system can be challenging, especially when dealing with service-to-service communication.
What are some best practices for working with JWT?
Working with JWT is complex and carries high stakes. It is therefore important to follow the recommended industry best practices, such as:
- Keep payloads small: Minimize the amount of data stored in JWT payloads. For instance, avoid storing sensitive information in the payload, and consider limiting the number of claims to only essential data. When sending a JWT over HTTP headers, keep its size under 8 KB, as some servers do not accept request headers that are larger than 8 KB.
- Validate and verify: Always validate and verify the JWT before relying on the information it contains. This involves ensuring the token is not expired, validating the issuer and the algorithm that was used, and checking the signature to confirm the token’s authenticity.
- Use secure libraries: Utilize well-established libraries and frameworks for creating, parsing, and verifying JWTs. Avoid implementing JWT from scratch, as cryptography and security are very complex topics—even for experienced developers.
- Implement token expiry: Set a reasonable expiration time for JWTs. Shorter expiration times can enhance API security by reducing the window of opportunity for misuse if a token is compromised. If the JWT is an access token, consider pairing it with a long-lived refresh token.
Great article, you’ve covered a lot. Could you recommend any articles that delve into the encryption aspect of a JWT’s payload, particularly those using a public/private key-pair as the encryption algorithm?
Hi Mikael. Thank you!
The payload in a JWT is Base64 encoded. Private and public key pairs are not encryption algorithms but rather claims in a payload.
To learn more about all the algorithms used in the JWT spec, the JSON Web Algorithms (JWA) section of this JWT Handbook by Auth0 is a really good resource. It covers Base64 and cryptographic algorithms like HMAC.