Spring WebFlux and OAuth 2.0

Richard Jones
6 min readJan 24, 2021
Photo by James Harrison on Unsplash

The Spring ecosystem provides extensive support for integrating with OAuth 2.0 complaint providers. In this tutorial we will cover how to create a reactive Spring Boot application that uses a third party provider’s OAuth 2.0 for authentication, and how to use the access token issued to further request protected resources.

The tutorial assumes you are familiar with:

  • Spring / Spring Boot
  • Project Reactor
  • OAuth 2.0

Spring Security comes preconfigured with common OAuth 2.0 providers for Google, Github, Facebook, and Okta. Their configurations can be found in the spring-security CommonOAuth2Provider class. By providing these configurations, it is incredibly easy for you to integrate with those providers, in-fact all you need to do is configure the client-id and client-secret for the provider:

Google OAuth 2.0 Configuration (Gist)

There are many other popular OAuth 2.0 providers whose configuration isn’t included by default. One such example is Reddit, which is the provider this tutorial will focus on.

Register an Application with Reddit

To start, let’s register our application with Reddit. For this you will need an account, if you haven’t already got one go ahead and sign up. Reddit is an outlier on the internet in that you don’t need to enter an email to create an account, you can just click “continue” and it will bypass it.

Once you’ve logged into your account, navigate to https://www.reddit.com/prefs/apps/ and scroll to the bottom. Click on the are you a developer? create an app... button.

Make sure that web app is checked, and specify the redirect uri as http://localhost:8080/login/oauth2/code/reddit (more on this below). You are free to update the other fields as you see fit.

Creating a web app on Reddit

Once the app is created, you will be able to obtain the client ID and secret, note these down as they will be required later. In my case, the client ID is SVDNR1kmiOVDGg and secret is BpQxZT5msviUM1GCGvIhu055E_psFQ:

Created web app on Reddit, with client ID and secret highlighted

Generating the Spring Boot Application

Secondly, we need to bootstrap our Spring Boot project. Lets navigate over to Spring Initializr and add the following dependencies:

  • Spring Reactive Web
  • OAuth2 Client

Alternatively, you can click here for a pre-configured initializr template.

Spring Initializr with Reactive Web and OAuth2 client dependencies
Spring Initializr Template

Once the project is downloaded, open it in your IDE of choice.

Bootstrapping the service

To use WebFlux, we need to enable it in our service:

Configuration to Enable WebFlux (Gist / Github)

While here, we will also add a simple Hello World endpoint:

Simple Hello World Controller (Gist / Github)

If we tried to hit this endpoint right now we would be redirected to a login page, this is because Spring Security is on the class path and Spring Boot is automatically configuring the service to require authentication (although there are no configured users).

Enabling OAuth 2.0

As we want to use Reddit to authenticate, we need to customise the the security configuration to enable OAuth 2.0 for all requests:

Enabling OAuth 2.0 Login (Gist / Github)

If you tried to run the service now, it wouldn’t even start due to IllegalArgumentException: clientRegistrationRepository cannot be null. This is because we haven’t configured any of the OAuth 2.0 clients.

The configuration for OAuth 2.0 clients is split into two main categories:

  • spring.security.oauth2.client.registration— configuration relating to how the OAuth2 client will create the authentication request
  • spring.security.oauth2.client.provider— configuration relating to the OAuth2 server

In the Google example mentioned above, all that was required was to specify a couple of the registration properties. As this is a new provider, it requires both the registration and provider to be configured:

Reddit OAuth 2.0 Configuration (Gist / Github)

Note: you will need to provide your own clientId and clientSecret values with the one’s you definitely noted down earlier…

Spring has some great documentation, so if you’re not sure what one of those properties is I will direct you their respective classes:

Although, I would like to highlight the spring.security.oauth2.client.registration.reddit.redirctUri: http://localhost:8080/login/oauth2/code/reddit property. This is an endpoint in our service, but we haven’t created any controller to handle this request. Spring’s DefaultOAuth2AuthorizationRequestResolver is being used to intercept and handle the response from Reddit. More information on this can be found here.

Testing Authentication

Start the service in the usual way (e.g. ./mvn spring-boot:run), and navigate to http://localhost:8080/. As Spring Security is configured to require authentication for all requests, and OAuth 2.0 login is enable you should be automatically redirected to Reddit where you will be asked to allow access to the app you created. Upon accepting, you we be redirect back to http://localhost:8080 where we are now authorised to get a response from our HelloController endpoint.

Downstream Authentication

As we now have our authentication working, we have the ability to obtain the access token issued by Reddit. We can use this access token to interact with the Reddit API.

Spring’s WebClient has built-in support for invoking downstream services using the access token that was issued, it just requires some configuration:

Configuring a WebClient with an OAuth 2.0 filter (Gist / Github)

Lets break down what the two methods are doing:

  • redditWebClient — Instantiates a WebClient bean, that is configured to use https://oauth.reddit.com as the base URL for all requests. Furthermore, it is configuring that WebClient with a ServerOAuth2AuthorizedClientExchangeFilterFunction that uses the Reddit OAuth2 configuration as a default. This filter will be applied before any requests are made, and will add the Authorization HTTP header to all requests.
  • authorizedClientManager — Instantiates a ReactiveOAuth2AuthorizedClientManager, that is used by the ServerOAuth2AuthorizedClientExchangeFilterFunction mentioned above. The ReactiveOAuth2AuthorizedClientManager allows Spring obtain the access token used in requests to the Reddit API.

The last thing we need to do, is to update the HelloController to use our configured WebClient to make a request to the Reddit API.

RestController that uses the configured Reddit OAuth 2.0 configured WebClient (Gist / Github)

In the above example, the WebClient is used to invoke the https://oauth.reddit.com/api/v1/me endpoint that is protected by the identity scope (that is configured in our application.yml). It then parses the JSON response into a map, and retrieves the name attribute.

Restarting our service, and navigating to http://localhost:8080 will trigger the authentication flow again. Once accepted, you will see the updated response with your username.

But.. I’m not using WebClient

There may be a use case where you need to obtain the access token yourself, this is possible to do using the auto configured ReactiveOAuth2AuthorizedClientService bean. This is the same bean that we are using in the ReactiveOAuth2AuthorizedClientManager bean we created above.

In the below example, we will replicate what the ReactiveOAuth2AuthorizedClientManager is doing under the hood, to achieve the same outcome:

RestController that manually retrieves the downstream access token (Gist / Github)

In this case, we are required to obtain the SecurityContext and use the name retrieved from the Authentication object held my Spring Security to retrieve the access token from the ReactiveOAuth2AuthorizedClientManager. We can then use this value to populate the Authorization header when we make our request.

--

--