Spring Security : Authentication Lifecycle & JWT
Any developper having worked on authentication process, session management, or any security subject, once wondered : “how does my application know that I am still authenticated?”, or “Why am I being logged out after some time ?”.
Regarding the frameworks and the developers’ choices, these questions may be answered differently. In this article, I will detail how it works in the context of a REST API secured with Spring Security (the main concepts of which are discussed in this article).
1. Security Context : a critical object
Requests targeting the endpoints of a Spring Security secured REST API may have to provide a proof of authentication. In order to check if a user is authenticated, Spring Security relies on the content of its Security Context, which should be populated with and Authentication object.
The common procedure when a request targets a secured endpoint and there is no authenticated user is to redirect the user to a login form.
Now, let’s consider there is an Authentication object in the Security Context. The user may then target any endpoint, according to their authorizations. Well, at least until they choose to log out… It is indeed logical to give the user the option to put their session to an end, so their account will not be used by another person. In that case, the Security Context content is cleaned.
2. Spring Security session management
Other situations may lead to the Security Context being erased. The said situations depend on the Spring Security session management policy choosed by the developper. Here is an example of session management configuration on line 64 :
Four distinct configuration are available, each having a different impact on the Security Context lifecycle.
- SessionCreationPolicy.ALWAYS : the application is then said stateful, which means its state is saved with each request. It implies that the Security Context is also saved by Spring Security, so its content may be used over multiple requests, without prompting the user for their credentials each time.
- SessionCreationPolicy.IF_REQUIRED : default option. The session is only saved when it is necessary. After some time reading the official documentation, I am not closer to know what necessary means for Spring Security… sorry about that.
- SessionCreationPolicy.NEVER : Spring Security does not save any session, but may use some already existing session, which has been saved by other means. It is indeed to be noted that we are only talking about the Spring Security session configuration : any other framework used by the API may follow its own session policy.
- SessionCreationPolicy.STATELESS : Spring Security does not save any session, nor use any otherwise existing session. This policy implies that the Security Context is erased after each request, and that an authentication proof must then be provided with each incoming request targeting endpoints protected with the authenticated() or hasAuthority() methods.
We will not detail each policy scenario here, I will focus on the case of an API configured with the Stateless policy, which may be considered as the ‘most secured’ since the authentication expires with each request.
The next question is : how to avoid prompting the user for their credentials with each request when using such a policy ?
3. Using a JWT
A JWT (Json Web Token) is a Json objet containing keys and values, which is usually crypted into a String. A JWT is typically used to store a username, a list of authorizations, and an expiration date. If such an object is attached to a request, we may use it to generate an Authentication if the expiration date is valid.
In such a scenario, the authentication and the request processing unfold according to the following main steps :
Well then, we must answer two critical questions : where does the JWT come from, and how to attach it to a request ?
A. Creation of the JWT from Authentication object
When a user is signing in the application from a login page, Spring Security uses the prompted credentials to build an Authentication object. This object :
- is stored in the Security Context until the request processing is complete.
- is used to build a JWT according to the following steps :
The JWT is then returned attached to the request answer.
B. Attaching the JWT to each request
Once the JWT is generated by the API and obtained from the login request answer, one must send it back to Spring Security with each request, so the user identity may be verified. The JWT is typically sent as a request header, which by convention :
- is named “Authorization”
- starts with the String “Bearer ”, to which is concatenated the encrypted JWT
The connexion + JWT creation steps are summed up in the following scheme :
We now know the authentication, JWT generation, and Security Context reading processes used with Spring Security.
We still have the reading/validating of the JWT processes and the Authentication object generation from the JWT process on our plate.
C. Reading/validating the JWT
Reading the JWT should include a decryption step : no developper is forced to encrypt the JWT, but not doing it is…well, frowned upon. Then, checking its validity is a process focused on 4 main steps :
- is the object a JWT indeed ?
- is the JWT not empty ?
- is the JWT fitting the format expectations ?
- is the JWT not expired ? It is to be noted here that the developper is in charge of setting the expiration date of the JWT during the generation process. It is common to set a 1h lifetime, so the user does not have to login too frequently, but is still loged out after a short period of time.
The reading and the validation processes of the JWT are summed up in the following scheme :
D. Populating the Security Context from the JWT content
If Spring Security was summed up in only one concept, it would be “filter chain”. When an incoming request reach a Spring Security secured API, it is crunched by multiple filters before finally being allowed to reach the targeted endpoint. Some of the filters are put automatically in place by Spring Security, others may be customized by the developper.
Fetching the JWT from the request, reading and validating it, and transferring the data between the JWT and the Security Context are tasks operated by such a customized filter, which implements the Spring Security provided OncePerRequestFilter interface. This filter actions are summed up in the following scheme :
And that is how Spring Security knows that multiple consecutive requests come from the same authentified user, without repeatedly prompting for credentials, and without having to store authentication information between requests.
4. To sum up
If we only consider the Authentication filter and the JWT related processes (the other filters are omitted for clarity purposes), an incoming request targeting a Spring Security secured API endpoint goes through the following steps :
- an incoming request lands on the API.
- a filter checks wether a JWT is attached to the request.
- the Security Context is populated with the Authentication object generated from the JWT content, or is left empty.
- if the targeted endpoint is secured, the Security Context is searched for an Authentication object.
- if a required Authentication cannot be obtained, the user is redirected to a login form.
- if the login request is successful, a valid JWT is sent back. This JWT must be attached to each following incoming request as an authentication token.
Sources
Contact
- Need for complementary informations ? Need help to concretely implement the JWT authentication ? => write to louis.jeannejulien@gmail.com
Liens externes
Wonder who is Ouidou ? Do not hesitate to contact us at contact@ouidou.fr or pay a visit to our website on https://ouidou.fr