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!