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: Represents the encoding of the previous two sections and is encrypted using a secret key.
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. But what if John Doe were actually a regular user (with role “USER”) and manually changed his role? The result would be a valid token that allows unauthorized access to protected APIs.
This problem can be addressed in two ways:
- Avoid placing sensitive attributes like roles within the token.
- If unavoidable, ensure these attributes are validated against your app’s database.
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!