Have you ever noticed that some websites have a private section that requires a username and password to access? You might also have noticed that, once logged in, you’re often not asked to re-enter your username and password for subsequent visits. In stateless web applications, this behavior is achievable by using a JWT token. JWT, which stands for JSON Web Token, is a data exchange standard defined by RFC7519 in 2015.

In simple terms, a JWT token allows a user to communicate their identity to a REST application without needing to enter a username and password each time.

Structure of the Token

A JWT token consists of three main components:

  • Header: Contains information about the type of token and the signing algorithm used.
  • Payload: Contains all the information exchanged between the user and the application.
  • Signature: The signature of the previous two sections, computed using a secret key. It lets the server verify that the token has not been altered.

JWT Token: An Example

To see what a JWT looks like, you can visit jwt.io. Here’s an example of a token structure:

  • Header
{
  "alg": "HS256",
  "typ": "JWT"
}
  • Payload
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}
  • Signature
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

The result is:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

You can distinguish the three components, as they’re separated by a “.”

Customizing the JWT Token

JWT tokens can be customized using Claims. Claims are a map of values representing information useful for the application. These are placed within the Payload and contribute to the token’s overall size.

Here’s an example of a customized Payload:

{
  "sub": "johndoe@test.com",
  "firstName": "John",
  "lastName": "Doe",
  "iat": 1728830222,
  "exp": 1728866222
}

The resulting token will look like:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huZG9lQHRlc3QuY29tIiwiZmlyc3ROYW1lIjoiSm9obiIsImxhc3ROYW1lIjoiRG9lIiwiaWF0IjoxNzI4ODMwMjIyLCJleHAiOjE3Mjg4NjYyMjJ9.OYqR8KbYa0n6-Zrx1SQKXZAeX4a2iO6xZKrkC2_wLwQ

What Not to Do!

Now that we understand what a JWT token is and how it can be customized, let’s look at a common security mistake. Imagine you have APIs protected by JWT, accessible only to users with a specific role. Here’s an example of a JWT Payload for an admin user:

{
  "sub": "johndoe@test.com",
  "firstName": "John",
  "lastName": "Doe",
  "role": "ADMIN",
  "iat": 1728830222,
  "exp": 1728866222
}

With this token, our application identifies “John Doe” and grants access to admin-only APIs.

The token is signed, so nobody can change its content without the server noticing: if John Doe had the role “USER” and manually changed it to “ADMIN”, the signature would no longer match and the token would be rejected (as long as the server actually verifies it).

The real mistake is a different one: treating the content of the token as always current. The signature guarantees that the data has not been altered, not that it is still true. If John Doe’s ADMIN role is revoked, the token issued earlier keeps declaring him an administrator until it expires.

For this reason, it is a good idea to:

  1. Use short expiration times, so that outdated data is short-lived.
  2. For the most sensitive operations, check the user’s role and status against your app’s database instead of relying on the token alone.
  3. Never put confidential information in the Payload: it is encoded, not encrypted, and anyone can read it.

Conclusion

JWT tokens are a popular standard for authorizations in web applications. For further details, refer to this link. Have you used JWT tokens in your applications? Let us know in the comments!