JWT Token
Overview
Authentication and authorization have been the subject of evolution from day one. There are many techniques to implement authentication and authorization.
JWT token is one such technique to implement authorization.
Prerequisite
Readers must know about HTTP authorization and spring security to best use the article.
Introduction
JWT stands for JSON Web Token. JSON web token is an open standard RFC759. JWT tokens are a popular technique for doing user authorization. Before we go into JWT, let's understand the challenge with web authorization. This will clear our perspective of why we need JWT.
HTTP Authorization Challenge
HTTP is a stateless protocol, i.e., the next request does not know about the previous request. Each request is expected to be self-contained and contains all the information needed to process it successfully. On the other side, we want an authorized user to continue interacting with the website without being authorized each time which means we want the server to remember who the user is and if the user is authenticated. It defies the principle of HTTP; be stateless.
HTTP | Authorization |
---|---|
Be stateless. No knowledge of the previous conversation should be used. | Be stateful. The server must know if a user is authorized to do the operation. |
There are multiple ways for a web application to remember the user. Below are the two most popular techniques based on tokens:
-
Session-based- Session-based authorization technique uses an HTTP session. Once a user is authenticated, a session identifier is assigned to the user, and the same is stored with the web server as key-value pairs.
SessionId User 1234ABCD BOB 9876ZYWX Alice All the subsequent requests from the same user carry the assigned session identifier, which gets matched at the web server for validity.
-
This solution works, but it has some drawbacks.
- It assumes that there is one single instance of the application where all the sessions are stored.
- It forces the server to maintain the state, violating the HTTP statelessness principle.
- User state is stored on the server.
- When multiple instances are sitting behind the load balancer, then the load balancer should be smart enough to route the request to the server where the session is stored.
- Crashing of a server will cause request failure.
- Most important model is not scalable. It restricts scalability. Think of the session token as a reference token, which refers to an actual user.
-
Token based - Token-based authorization leverages JSON Web Token (JWT). In the token-based authentication, the user data is encrypted into a JWT (JSON Web Token) with a secret and then sent back to the client. Client stores token information either in a cookie or local storage and sent as an "Authorization" header in the subsequent request. The server decrypts the token, validates the request, and sends the response to the client.
It has some advantages over session-based authorization.
- No state is stored on the server side.
- Improved scalability because any instance can process the request.
- Token is self-contained and contains all the required user information in encrypted form.
- Token is stored on the client side.
Think of JWT as a value token where a token carries user information rather than a reference key.
Getting Started with the JWT Token
We now have a fair understanding of what JWT is and what its advantages are. But what a JWT looks like? A typical JWT token looks like xxxx.yyyy.zzzz.
Sample JWT token
A JWT token consists of 3 parts separated by a "dot," which are:
- Header
- Payload
- Signature
Let's understand each part.
Header
The header consists of two pieces of information 1) the Type of token and 2) the Encryption algorithm. Sample decrypted header:
Payload
The payload section contains the claim. The claim contains user information with additional meta-data. User information can vary from system to system depending on the representation of a user to a system. The payload contains some reserved attributes that can not be used with the user payload. Some of the reserved claims are iss (issuer), exp (expiration time), sub (subject), and aud (audience).
Sample decrypted payload:
Signature
The last and most important part of the token is the signature. It ensures the authenticity of the token. We use our signature in many places in our daily life. Understand it with a simple example; You want to withdraw money from your bank account. The banking staff verifies your signature registered in the banking record against the one you put in the withdrawal form. Signatures are the way to ensure that you are who you claim to be.
In the same way, JSON tokens are stamped with signatures. Signatures are generated using an encoded header, payload, and a secret. For example, if you want to use the SHA256 algorithm, the signature will be created using the below formula.
The signature ensures the tempered tokens are not parsed, as the signature does not match the original payload.
To summarize JWT token is generated using the formula:
JWT tokens are using base64 encoded. This means you can very well decode it from https://jwt.io/.
Does it mean JWT is not secure? No, and there are reasons for it.
- All communications are expected to happen over HTTPS. This means everything transported over the network is encrypted.
- JWT security lies in the "secret" key. It uses a one-way encryption algorithm. Unless someone knows the secret, a token can not be validated.
Therefore, make sure not to keep any sensitive information in the payload.
Spring Boot JWT Authentication and Authorization
We will build a sample application using spring boot and security, which uses a JWT token for authorization.
We are going to build an e-commerce application with only two features:
- User should be able to log in using the /auth/login endpoint. This endpoint returns JWT to the user.
- A logged-in user should be able to access products using the /products endpoint if a valid JWT token is supplied; otherwise, it should return a forbidden response.
We will build only the web layer of the application, and the remaining layers will either be mocked or use hard-coded data. Let's code it!
- Add maven dependency
Two important dependencies here are spring-boot-starter-security and jjwt.
- Implement UserDetailsService
We are returning a hardcoded user with username "scaler" and password "scaler123" here, but it can come from the database using a repository.
- JWT token utility class. This is one of the key classes. It contains methods related to JWT tokens, like generating and validating tokens, etc.
Code explanation
- Line number 4 contains a secret used to sign the token.
- Line number 32 method createToken() generates JWT token with the information 1) issued time 2) expiry time and sign it is using secret and algorithm SHA256. The method will be invoked during login activity.
- Line number 52 method validateToken() checks validity of the token. It will be called during any other endpoint the user tries to invoke. In our case, the products endpoint.
These utility methods will be used during the authentication and authorization process.
- Spring security does not have inherent support for JWT-based authorization. Therefore we need to implement a custom filter and fit it with the spring security filter chain.
JWT Filter
Code explanation This filter has main authentication and authorization logic.
- Line number 14 extracts the token from HTTP request headers. The format of the header is "Bearer <JWT_TOKEN_STRING>".
- Line numbers 20-21, we extract the token string by stripping off "Bearer".
- Line numbers 40-50 does the job of actual authorization. It sets the username extracted from the token into spring security SecurityContextholder.
- Configure Spring security with the JWT filter and allow unsecured URLs.
Code explanation
- Line number 26 method configure(HttpSecurity http) does the following.
- Allow the /auth/login URL to be accessed without authorization because this is where the user will supply credentials to log in.
- Add jwtFilter before UsernamePasswordAuthenticationFilter in the Spring security filter chain.
Once configured filter below, the filter sequence will be executed.
- Rest Controller
On successful login, we return the JWT token to the user.
The code contains a few more classes. Full source code is available at GitHub location.
Run and test
Let's run the application and test with a different scenario.
- We need to log in using valid credentials. Open postman and log in using the POST /auth/login endpoint. Supply credentials in the request body.
The method should return the JWT token in the response after successful execution.
We can decrypt the token and see what is inside from JWT
Now that we have generated the JWT token. We will now invoke the /products endpoint with two scenarios.
- Invoke the /products endpoint without a JWT token. It should return HTTP status 403- Forbidden.
- Invoke the products endpoint with the JWT token. Should return response 200 OK with the list of products in the response.
Conclusion
- HTTP is a stateless protocol; therefore, building authorization is tricky.
- There are two popular techniques to build authorization 1) Using HTTP session and 2) Token based.
- HTTP session-based authorization is not scalable because the server has to maintain the state.
- HTTP tokens are reference tokens.
- JWT tokens are a popular way to implement token-based authorization.
- JWT tokens are value tokens, and each request is expected to carry to the token.
- JWT tokens are Base64 encrypted strings.
- JWT tokens improve scalability because tokens are stored on the client side, and each token carries all the required information to process the requests independently.