Patrick C.
Patrick C.

Reputation: 1429

<Spring Security> bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' cannot be found

Hi I am learning about Spring Security and was trying to create a simple OAuth2 client and resource server based on the guidelines at https://dzone.com/articles/implement-oauth-20-easily-with-spring-boot-and-spr

I came into an issue where the compiler keeps saying that it cannot find a bean for "ClientRegistrationRepository". I did some digging on the web, which says that if the Spring client configurations are configured correctly, it should work. Someone having similar issues said the problem may be caused by indetation issue in the properties file, but I am not seeing such case.

May I seek for your help to see if there is anything configured incorrectly, thanks.

Console output

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of method webClient in com.somecompany.configuration.WebClientConfig required a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' that could not be found.

The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' in your configuration.

OAuth2 client main class

package com.somecompany;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Oauth2DemoClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(Oauth2DemoClientApplication.class, args);
    }

}

OAuth2 client controller

package com.somecompany.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;

@RestController
@RequestMapping("/api/client")
public class Oauth2DemoClientController {

    @Autowired
    private WebClient webClient;

    @Value("${resourceServer.url}")
    private String resourceServerUrl;

    @Value("${resourceServer.helloPath}")
    private String resourceServerHelloPath;

    @GetMapping("/")
    public String home(@AuthenticationPrincipal OidcUser user) {
        return "Welcome " + user.getFullName();
    }

    @GetMapping("/hello")
    public String sayHello() {
        return webClient.get().uri(resourceServerUrl + resourceServerHelloPath).retrieve().bodyToMono(String.class)
                .block();
    }
}

OAuth2 client configuration

package com.somecompany.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Value("${defaultClientApplication}")
    private String defaultClientApplication;

    @Bean
    WebClient webClient(ClientRegistrationRepository clientRegistrations,
            OAuth2AuthorizedClientRepository authorizedClients) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
                clientRegistrations, authorizedClients);
        oauth2.setDefaultOAuth2AuthorizedClient(true);
        oauth2.setDefaultClientRegistrationId(defaultClientApplication);
        return WebClient.builder().apply(oauth2.oauth2Configuration()).build();
    }
}

OAuth2 client application.yml

logging.level.root: "debug"

defaultClientApplication: "okta"

spring:
  security:
    oauth2:
      client:
        provider:
          okta:
            issuer-uri: "https://dev-27548664.okta.com/oauth2/default"
        registration:
          okta:
            client-id: {client ID}
            client-secret: {client secret}
      resourceServer:
        url: "http://localhost:8081"
        helloPath: "/api/resource/hello"

OAuth2 client pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.somecompany</groupId>
    <artifactId>oauth2-demo-client</artifactId>
    <version>1.0.0</version>
    <name>oauth2-demo-client</name>
    <description>oauth2-demo-client</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Upvotes: 5

Views: 16519

Answers (3)

xardbaiz
xardbaiz

Reputation: 817

Sometimes just auth-related application properties and maven configs aren't enough.

Let's try to explicitly enable spring's auto-configuration properties and create default ClientRegistrationRepository & OAuth2AuthorizedClientRepository (both are required for your filter function)

build.gradle

dependencies {
    implementation "org.springframework.boot:spring-boot-starter-oauth2-client"
}

Java

@Configuration
@EnableWebSecurity
@EnableAutoConfiguration
@EnableConfigurationProperties(OAuth2ClientProperties.class)
public class OAuth2Config {

    @Bean
    @ConditionalOnMissingBean
    public ClientRegistrationRepository clientRegistrationRepository(
            OAuth2ClientProperties oAuth2ClientProperties) {
        var clientRegistrations =
                List.copyOf(
                        new OAuth2ClientPropertiesMapper(oAuth2ClientProperties)
                                .asClientRegistrations()
                                .values());
        return new InMemoryClientRegistrationRepository(clientRegistrations);
    }

    @Bean
    @ConditionalOnMissingBean
    public OAuth2AuthorizedClientService authorizedClientService(
            ClientRegistrationRepository clientRegistrationRepository) {
        return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
    }

    @Bean
    @ConditionalOnMissingBean
    public OAuth2AuthorizedClientRepository authorizedClientRepository(
            OAuth2AuthorizedClientService authorizedClientService) {
        return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
    }
}

Upvotes: 0

Matt Raible
Matt Raible

Reputation: 8614

I'd suggest using the Okta Spring Boot starter. It shortens the spring.security.oauth2.* properties to be more intuitive.

okta.oauth2.issuer=<your-issuer>
okta.oauth2.client-id=<your-client-id>
okta.oauth2.client-secret=<your-client-secret>

If you want to use Spring Security, I'd recommend the following dependencies:

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

Then, configure it as follows:

spring:
  security:
    oauth2:
      client:
        provider:
          okta:
            issuer-uri: <your-issuer>
        registration:
          okta:
            client-id: <your-client-id>
            client-secret: <your-client-secret>
            scope: openid,profile,email

Upvotes: 0

Naresh J
Naresh J

Reputation: 2137

Issue is with your application.yml config indentation. security should be child of spring :

spring:
  security:
    oauth2:

Update:

YML properties are case sensitive. Try to change resourceServer to resourceserver

Upvotes: 2

Related Questions