From stateful to stateless RESTful security using Spring and JWTs – Part 3 (token-based authentication)

Last time we implemented a basic, but fully functional stateful authentication solution using Spring Security. We will now gradually move to a stateless solution.

My objective is to go through the reasoning that will eventually lead us to get rid of that Session object. This journey towards “statelessness” will therefore be split split in two: in this part we will see how we can replace sessions with tokens, and in the next part we’ll integrate JWTs to generate decent, secure token values.

So take a deep breath and start coding! (or point your browser to this chapter’s code on GitHub. It’s all good to me!)

Related posts

Expanding on that authentication manager

As a warming up exercise, let’s start by replacing that in-memory authentication manager with an implementation that’s closer to the real thing.

Spring Security provides out-of-the-box managers on which you can plug your LDAP or JDBC databases. However you might decide to use another support for storing your users’ credentials, in which case you need to tell Security how and where to find a given user’s profile data, based on the username provided by a user at authentication. One approach to do that is to extend UserDetailsService:

There is one method to implement, loadUserByUsername(). Its purpose is to check if such user exists and consequently load his/her profile. It then either raises a UsernameNotFoundException if that user does not exist, or returns a UserDetails object which allows Spring Security to know who it is dealing with.

So this is where one must normally query the supporting database to check for the user’s existence and to load its details.
For the sake of this tutorial I just hard-coded a test user’s details, but in a real situation one would probably use a DAO or a Repository instance, provided by means of the class’ constructor.

If the user is valid, Spring Security will then take over and compare the password from the loaded UserDetails with the one typed by the user in the login screen. Well, to be precise, it actually compares a hash of the provided password with the password from the database, which is obviously also stored as a hash (unless you want to do the same mistake as these guys). This is why I use an instance of a PasswordEncoder in my implementation: to make as if the password loaded from the database is hashed.

Our custom UserDetails service therefore requires a PasswordEncoder instance. It must also be exposed as a bean so we can use it in our security configuration class:

No surprises here.

We now tell Spring Security to use that UserDetailsService implementation instead of our previous in-memory one. For that, we need to go back to our SecurityConfiguration class:

As seen above, the inMemoryAuthentication() call on that authenticationManagerBuilder instance is replaced with a usersDetailsService() call that passes our custom UserDetailsService implementation.

We also specify that we must use a PasswordEncoder instance to handle hashed passwords.
This must be the same encoder instance, or at least the same encoder configuration that is used to hash our passwords (normally done in the database, but for this tutorial they are hashed in our MyUserDetailsService class…).

With this taken care of, let’s now examine how we can bring in the tokens.

Tokens of recognition

What normally happens when a user logs in? A framework such a Spring Security will start by searching in the users’ database whether there is a user matching the provided username. If that’s the case, the username’s hashed password is returned so that the framework can compare it with the hashed version of the provided password. If passwords match then the user is authenticated and Spring Security can store that user’s authentication state… where exactly? In a session, on the back end server side!

What we want to achieve is remembering who is logged in, but not in sessions. So the obvious question is: if we don’t store it on the server side, how can we remember if a user is authenticated? One possible answer is to store it on the client side. This means we have to replace the part where a Session object is created and a JSESSIONID sent as a cookie, and instead use some sort of text (a token) that identifies the user.
Technically, we could do that by implementing two custom Spring filters, one for authentication and one for authorization.

The authentication filter could look like this:

After Spring finds the user by his/her username and verifies that the passwords match, it calls the successfulAuthentication() method to store the user as logged in. We override that method (from UsernamePasswordAuthenticationFilter) in order to provide a token instead of a Session object. The token we return in this example is pretty dumb of course, but it is there to merely illustrate my point. In a real situation a string of text would be somehow generated to securely identify who the user is. We will later explore how this special token can be built.

When the logged user then queries the API he/she will send that token back in the request header. Our custom authorization filter is responsible for taking that token and re-identify the logged user from it:

Spring Security’s context is initialized with the details of the user matching that token. In this tutorial, we match the “success!” token with a user whose username is… “user”. Originality strikes again.
By doing that, Spring Security is now able to provide a Principal object from which our application will be able, for example, to retrieve who is querying the API, by using principal.getName().

We now modify the SecurityConfiguration class to enable those new filters. Additionally we get rid of the “form” login and the logout, which we don’t need in the context of a token-based solution:

Not quite there yet

At this point we have the core of the idea right:

  • At authentication, the back end server does not create a session and sends back a token.
  • When the user queries a protected API endpoint, the authorization filter finds who the logged user is by deducing it from the token.

Nice… but is not quite secure, is it?
We still need to find a way to generate tokens that carry a certain amount of information about the logged user, such as what level of authorization he as (is he administrator or simple user?). These tokens should be secure and tamper-proof, otherwise one could simply build a token using a text editor and make the server believe he is an authenticated user with admin rights! Also, we should make sure that the generated token is different every time the user logs in: this is important to prevent someone from recognizing a certain user by examining the token! Last but not least, it would be better to be able to verify who emitted this token.

Next time, we’ll see how JWTs can help us solving all this.

Until then,

Cheers!

 

Leave a Reply

Your email address will not be published. Required fields are marked *