JWT: A Deep Dive into JSON Web Tokens

·

11 min read

JWT: A Deep Dive into JSON Web Tokens

JSON Web Tokens (JWT) have become a popular method for securely transmitting information between parties as a JSON object. This compact and self-contained way of representing claims is widely used in authentication and information exchange scenarios.

Understanding the Need for JWT

Before we delve into the intricacies of JWT, let's understand the problem it solves.

Traditional web applications relied heavily on session cookies to manage user authentication. In this model, the server stores session data, and a unique session ID is sent to the client in a cookie. Subsequent requests include this cookie, and the server looks up the session to verify the user's identity. While this worked for many years, it introduced several challenges:

  • Scalability: As applications grew, managing sessions across multiple servers became complex.

  • Security: Session data stored on the server was vulnerable to attacks, and cookies could be stolen or manipulated.

  • Statefulness: The server had to maintain the state for each user, increasing resource consumption.

JWT emerged as a solution to these problems by offering a stateless, secure, and efficient way to manage user authentication and authorization.

Authentication and Authorization

Authentication is the process of verifying the identity of a user, device, or system. It typically involves the presentation of credentials, such as a username and password, to prove one's identity.

Authorization is the process of granting or denying access to specific resources or actions based on the user's privileges, roles, or permissions. It ensures that authenticated users can only access resources or perform actions they are explicitly permitted to access.

To perform Authentication, there are certainly two most popular ways on the web.

Stateless vs. Stateful Authentication

  • Using Cookies (Stateful Authentication**)** :

    The server maintains session information for each user, requiring it to remember user data across multiple requests. Examples include session cookies and server-side sessions. The traditional approach is Cookie-based server-side sessions. Here, the applications use sessions and cookies to store the user and perform authentication and authorization.

    The process begins with a user filling out their username and password and submitting to a server. As soon as the request reaches the server, the server is going to perform the authentication to make sure that the user is correct. If authentication is successful, the server is going to store the ID of the user inside the session, which is stored inside the server memory. This unique ID maps to the part of the memory, where the user info is stored. This unique ID is sent back to the client (browser), which is then stored inside a cookie. So now the browser always has the session ID in the form of a cookie and every time the client makes a request, it sends this cookie along with the request to the server. The server then checks in the server memory whether there is any ID that corresponds to the id that it received from the user. If so (authorized), the server responds back to the client.

  • Using JWT's (Stateless Authentication) :

    The server doesn't store session information. Each request contains all necessary data for authentication and authorization. JWT is a prime example of stateless authentication. Here, it uses JWT, to do the authorization/authentication.

    It works very similarly in the beginning, where the user makes a post request to the server with login credentials. But instead of storing the user info inside the session memory, what happens is server creates json web token (JWT). Here the server actually encodes and serialises the sign with its own secret key, So the server knows if you tamper it. Then it sends back the JWT to the client.

    Notice the main difference here, nothing is stored on the server, and that's why this type of authentication is stateless. The JWT that the client received will have all the information in it. This JWT is then stored in a local storage on the client side. And everytime the client makes the request to the server, this JWT is sent along with request. The server verifies the JWT signature and sents back the response to the client.

What is JWT?

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.

JWT (JSON Web Token) is an open standard (RFC 7519) for securely transmitting information between two parties as a JSON object. It consists of three parts separated by dots:

  1. Header: Contains metadata about the token, such as the signing algorithm.

  2. Payload: Contains claims about the user and other data.

  3. Signature: Ensures the integrity of the token.

Structure of JWT

A JWT is composed of three parts separated by dots (.), which are:

  1. Header: Contains metadata about the token, such as the signing algorithm.

  2. Payload: Contains claims about the user and other data.

  3. Signature: Ensures the integrity of the token.

1. Header

The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.

Example:

{
  "alg": "HS256",
  "typ": "JWT"
}

This JSON is then Base64Url encoded to form the first part of the JWT.

2. Payload

The payload contains the claims. Claims are statements about an entity (typically, the user) and additional data. There are three types of claims: registered, public, and private claims.

  • Registered claims: These are a set of predefined claims which are not mandatory but recommended, to provide a set of useful, interoperable claims. Some of them are: iss (issuer), exp (expiration time), sub (subject), aud (audience), etc.

  • Public claims: These are custom claims defined by the JWT standard but are not mandatory. They can convey non-sensitive information. These can be defined at will by those using JWTs. However, to avoid collisions, they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision-resistant namespace.

  • Private claims: These are the custom claims created to share information between parties that agree on using them and are neither registered nor public.

  • These are custom claims created by the parties involved in the token exchange. Examples are "role" of a user (admin/user).

Example:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

This JSON is then Base64Url encoded to form the second part of the JWT.

3. Signature

To create the signature part, you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that. It is used to verify that the user has not tampered the token.

For example, if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way.

The three parts of a JWT, when combined, form a string in the following format: {header}.{payload}.{signature}. The three parts of JWT are separated by periods. The recipient of the token can decode the header and payload, then base64 encodes the decoded part and hash it with a particular algorithm and verifies whether the signature part of the JWT part is the same as the present hashed value. When you won't get the same value then it indicates that JWT is tampered.

How JWT Works?

  1. Token Creation:

    • The server generates a JWT containing necessary claims (e.g., user ID, roles, expiration time).

    • The token is signed using a secret key or a public/private key pair.

  2. Token Transmission:

    • The server sends the JWT to the client.

    • The client stores the token (usually in local storage or a cookie).

  3. Token Verification:

    • Subsequent requests include the JWT.

    • The server verifies the token's signature and claims.

    • If valid, the server grants access to protected resources.

  4. User logs in: When the user logs in using their credentials, the server verifies the credentials and, if valid, creates a JWT and sends it back to the user.

  5. User stores the token: The user stores this token (usually in local storage or cookies).

  6. User makes a request: When the user wants to access a protected route or resource, the user agent sends the JWT, typically in the Authorization header using the Bearer schema.

  7. Server verifies the token: The server verifies the token's signature and the claims (e.g., expiration date, issuer) to ensure the token is valid.

  8. Server responds: If the token is valid, the server responds with the requested resource. If not, it responds with an error.

JWT vs. Cookies

FeatureJWTCookies
StorageClient-sideServer-side
SecurityHigh (signed and can be encrypted)Moderate (can be stolen, manipulated)
StatefulnessStatelessStateful
DataCustom claimsSession ID
Use CasesAuthentication, authorization, information exchangeSession management, user preferences

Why should we use JWTs?

Let's say there is a company that has two servers, the company is very large that they need two different servers to handle all of its users coming to the server and they have a load balancer to distribute the traffic to different servers.

Now let's say a client is accessing server A for a while, and for some reason, that server got really busy and shifts the client to server B. But the user session is not stored on server B, it's only on server A and so the user has to login back.

The solution to this is to introduce a single Cache Service for all the instances. This is a typical use case of the Redis Cache Service. The drawback to this solution is that, there will be now a single point of failure for the application.

But with JWT the user is stored at client side, so no matter how many different servers you have and no matter how many different application load balancers or anything that you have, you can always authenticate with any of those servers as long as all the servers have the same secret key. This JWT is really useful when your application has microservices.

Use cases of JWTs over Cookies

  • Statelessness:

Web applications can be built to be stateless, meaning they don't store user data on the server. JWTs contain all necessary information, allowing the server to verify and trust incoming requests without the need for server-side sessions.

  • Scalability:

JWTs are designed for distributed systems and microservices architecture. They facilitate secure communication between different components without the need for constant centralized authentication.

  • Single Sign-On (SSO):

JWTs are a crucial component of Single Sign-On systems, allowing users to log in once and access multiple applications seamlessly without re-entering credentials.

  • Cross-Origin Resource Sharing (CORS):

JWTs enable secure cross-origin requests. They can be included in HTTP headers, allowing web applications hosted on different domains to interact securely. Whereas cookies are subject browser's same-origin policy, which makes cross-origin requests more challenging.

Use Cases of JWT

  1. Authentication and Authorization: JWT is widely used for Authentication and Authorization. When the user successfully logs in using their credentials, a JWT is returned. Subsequent requests will include this token, allowing the user to access routes, services, and resources that are permitted with that token.

  2. Information Exchange: JWTs are a good way of securely transmitting information between parties. Since JWTs can be signed, you can be sure the senders are who they say they are. Additionally, the structure of a JWT allows you to verify that the content hasn't been tampered with.

  3. API Authentication: Protecting API endpoints.

  4. Single Sign-On (SSO): Enabling users to access multiple applications with a single login.

Advantages of JWT

  • Compact: Because of their small size, JWTs can be sent through a URL, POST parameter, or inside an HTTP header, and they are transmitted quickly.

  • Self-contained: The payload contains all the required information about the user, avoiding the need to query the database more than once.

  • Secure: JWTs can be signed using a secret or a public/private key pair.

  • Statelessness: Improves scalability and reduces server load.

  • Security: Provides strong security guarantees through signing and encryption.

  • Decoupling: Separates authentication from session management.

  • Versatility: Can be used in various applications and environments.

  • Efficiency: Compact and lightweight.

Disadvantages of JWT

  • Token Size: Can become large with extensive claims, affecting performance.

  • Token Expiration: Requires careful management of expiration times.

  • Security Risks: If the secret key is compromised, tokens can be forged.

  • Complexity: Implementing JWT requires careful consideration of security and performance.

Best Practices for using JWT

  • Use strong algorithms for signing and encryption.

  • Set appropriate expiration times for tokens.

  • Store tokens securely on the client side.

  • Validate claims carefully on the server.

  • Consider using JWT with additional security measures like HTTPS.

Conclusion

JWTs provide a robust and efficient way to handle authentication and information exchange in modern web applications. Their compact size, self-contained nature, and security features make them an excellent choice for developers looking to implement secure and scalable authentication mechanisms. Understanding the structure and working of JWTs is crucial for leveraging their full potential in your applications.

Did you find this article valuable?

Support Aanchal's blog by becoming a sponsor. Any amount is appreciated!