How to setup reactive client with OAuth in Spring Boot

WebClient with OAuth

I started a new project with WebFlux and wanted to access API with OAuth2 authorization. The official documentation for setting up OAuth2 with WebFlux was helpful, but not quite covered my case. Later, I found Baeldung’s article – Spring Security OAuth Login with WebFlux. I tried using his solution, but UnAuthenticatedServerOAuth2AuthorizedClientRepository exception was thrown, when I booted the application up. I searched and found the final peace at StackOverflow.

Below, I will show you, how to make WebClient for Petfinder’s public API that will authorize itself.

Beside Spring Reactive Web, you will need OAuth 2 Client dependency. You can start developing your application from this bootstrapped project or add following dependencies to your Spring Boot application.

<dependency>
    <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Next you need to add configuration to src/main/resources/application.yml. In this example my client will be distinguished by key pet-finder. This key is a registration id and will be needed later. You can have multiple OAuth configurations and those registration ids will be used to separate them.

spring.security.oauth2.client:
    provider.pet-finder.token-uri: "https://api.petfinder.com/v2/oauth2/token"
    registration.pet-finder.authorization-grant-type: "client_credentials"

Then setup configuration with a WebClient bean. To authorize to Petfinder API, WebClient needs to be extended with a filter – ServerOAuth2AuthorizedClientExchangeFilterFunction. Using registration id – pet-finder – setup previously configured token URI and authorization grand type.

@Configuration
class PetFinderConfiguration {
    @Bean
    WebClient petFinderClient(
        clientRegistrations: ReactiveClientRegistrationRepository, 
        clientRepository: ServerOAuth2AuthorizedClientRepository
    ) {
        val filter = serverOAuth2AuthorizedClientExchangeFilterFunction(
            clientRegistrations, 
            clientRepository
        )
        filter.setDefaultClientRegistrationId("pet-finder")

        return WebClient.builder()
            .filter(filter)
            .build()
    }
}

The last part is configuring your client id and secret. They should not be included in your source code. Leaving secrets in code is asking yourself for trouble. The best way to set them up is to pass them as arguments when starting Spring application. With Maven it could look like:

./mvnw spring-boot:run -Dspring-boot.run.arguments=--spring.security.oauth2.client.registration.pet-finder.client-secret=YOUR_SECRET,--spring.security.oauth2.client.registration.pet-finder.client-id=YOUR_ID

That’s all that is needed. Now you can use this WebClient to fetch information about pets that are waiting for adoption.

@Component
class AnimalsService(private val petFinderClient: WebClient) {
    fun fetch(): Flux<Animal> = petFinderClient
        .get()
        .uri("https://api.petfinder.com/v2/animals")
        .retrieve()
        .bodyToMono(Payload::class.java)
        .flatMapIterable { it.animals }
}

data class Payload(val animals: Set<Animal>)

data class Animal(val id: String, val name: String, val species: String)

The summary of last 4 weeks and plans for future

I’ve learned a lot. I’ve read Designing Event-Driven Systems and Working Effectively with Legacy Code. I’ve been at Poznan University of Technology performing my presentation about Rest API. A lot has happened.

I will not be writing weekly articles next month. I want to take time and figure out how I see my blog in future. I feel like during this month, I wrote about many interesting but different issues. Software architecture – especially architecture of distributed systems – is the main topic that I want to focus. I want it to be the core, but not exclusive topic of my posts.

I wish I will not loose momentum and return in 2020 with many valuable articles that will expand your and my knowledge!

comments powered by Disqus