JWT Authentication in Spring Boot Webflux

Jaiden Ashmore
5 min readApr 18, 2020

--

Spring Security has always been a hurdle for me to maintain my interest in my personal projects. Once I have to figure out how to authenticate my users I use to get frustrated or bored and would go back to relaxing and playing games. While working on personal project I wanted to add JWT authentication to the user service so that other services could authenticate a request independently. To do this, I dug into the internals of Spring Security to learn how it worked and thought it would be a good topic to write about.

By the end of this article we will have a Spring Webflux service that can create accounts, login to these accounts and make authenticated requests as this user using JWT. There will be a lot of shortcuts, e.g. a Map of users instead of an actual database but those aspects can easily be extrapolated from this article.

Initial project

Spring Initializr was used with the following configurations:

From here I created a really basic REST controller for doing signup, login and getting a user’s details. You can see the start of this service at the spring-boot-webflux-jwt-authentication-example#4d149ded77bec3da9ad269e2feb30e81d10d774e commit.

Basic User Controller

Hooking in Spring Security

Spring Security provides the tools to easily authenticate and authorise user’s access to your application. Simply put it is just a sequence of filters that run before the Controller of your application to apply security logic, such as authenticating a user or blocking access completely. These filters are performed in a distinct order defined by the SecurityWebFiltersOrder, for example, the process of authenticating the user (confirming that you are a valid user in the system) needs to be done before authorisation (confirming whether you have access to this resource).

These filters are set up via the ServerHttpSecurity which allows you to configure which filters you want to have. The following code gist is a simple security filter chain that allows all requests to pass into our controllers and disables some features that aren’t useful for our purely REST API.

Security Configuration that permits all requests

If you dig into the Spring Security code, you can see that the ServerHttpSecurity#build() method is used to apply these filters to the SecurityWebFilterChain. I would recommend digging through this to get a stronger understanding of the internals.

If we analyse how basic authentication works we can figure out the three main components that will be applicable for our custom JWT Authentication:

  • ServerAuthenticationConverter: this is used to convert the request to the authentication object. For example, in basic authentication it would read the Authorization header of the HTTP request and extract the username and password into an Authentication object for further processing.
  • ReactiveAuthenticationManager: this is used to determine if the provided Authentication can be authenticated. For example, it could take the username and password and check that it exists in the database and is the correct credentials.
  • AuthenticationWebFilter: filter used to take the request and apply all of the logic above. If the Authentication object can be authenticated, it will be added to the ReactiveSecurityContextHolder for usage by the subsequent AuthorizationWebFilter which will determine whether they have access to the resource.

Creating a JWT on successful login

The first thing we will do is update the login endpoint to generate a JWT on a successful login. For simplicity, we will use store the JWT in a cookie but there are better approaches from a security perspective. Take a look at The Ultimate Guide to handling JWT on frontend clients for a real in-depth explanation on this topic.

We will use RSA, an asymmetric key algorithm, that will generate a private key for signing a new token and a public key very verify the token. This will allow us to share the public key to other microservices so that they can do their own authentication without needing to call this user service.

I am going to use JJWT as the JWT library but any other equivalent library would do the job.

implementation("io.jsonwebtoken:jjwt-api:0.11.1")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.1")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.1")

We can now create a basic JwtSigner class that can be used to generate a keypair on startup and allow it to create JWT tokens.

JwtSigner that has the responsibility to create and validate tokens

Now when we successfully login we can use this JwtSigner to add a cookie to the response.

Authenticating the JWT

Currently the user will be able to include the JWT token for each request due to the additional cookie but the current code doesn’t do anything about validating this. The next step is to hook into the Spring Security to make sure we authorise our protected requests against the JWT token.

We can apply this authentication by adding a AuthenticationWebFilterinto our filter chain. To get this to work we will need to tell the filter how to get the JWT token out of the request via a custom ServerAuthenticationConvert.

What this function does it looks to see if there is any cookie with the name X-Auth and generates the Authentication object from it. I have used the JWT value as both the username and password as it stores both of these details.

The next step is to implement a ReactiveAuthenticationManager which will be used to take this extracted Authentication and check that it is a valid user. This will need to parse the JWT and make sure that it is valid and not expired.

This uses the JwtSigner that was created before to take the JWT and check that it is valid. The JJWT library handles cases like the token expiring so if the method does not throw a JwtException it is a valid token.

Now we need to build our AuthenticationWebFilter and add it to our chain.

Now when we run the application, any request that does not have a valid JWT token will be rejected with a 401 response.

Obtaining the user in our Controller

The final step for this application is to get the Authentication object so we can use the current logged in user within our Controller. As Webflux does not allow for ThreadLocals it is stored in the context of the Mono instead as a SecurityContextobject. The ReactiveSecurityContextHolder can be used to easily obtain this context but it would be nicer if this was automatically injected into the function signature of our REST endpoint.

To do this we can include the Principal into our method signature which is the UsernamePasswordAuthenticationToken that we can had created before.

Getting the currently logged in user

What about testing?

I hate these blog posts that show you a technique and then don’t show how you can write some tests for this. As unit tests should be easy to do yourself, I will show you an example of an integration test that signs a user up, logs it in and then gets its owner user details.

What’s next?

While this goes over the basics of JWT Authentication, there is a lot missing in this solution. For example:

  1. How do we allow refreshing of the JWT token before it expires?
  2. How do other services authenticate the JWT?
  3. As the token is being generated on startup, how would this work for a multi-node system?

I am hoping to have follow up blog posts around this topics as I solve these in my own application.

If you are interested in the final state of the application, take a look at this repo’s commit.

--

--