Search code examples
spring-securityspring-authorization-server

Autowire a particular password encoder for Spring Authorization Server


My app has two PasswordEncoder beans and wants to use spring-security-oauth2-authorization-server.

implementation("org.springframework.security:spring-security-oauth2-authorization-server:1.0.0")
  • Two passwordEncoder Beans
    @Bean("v1-encoder")
    fun v1Encoder(): PasswordEncoder {
        return V1PasswordEncoder()
    }

    @Bean("v0-encoder")
    fun v0Encoder(): PasswordEncoder {
        return V0PasswordEncoder()
    } 
  • So Spring Authorization Server complains about multiple beans:
Parameter 0 of method setFilterChains in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a single bean, but 2 were found:
PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class);

How can I autowire v1-encoder for OAuth2ServerSecurityConfig so it can find the v1-encoder bean?

  • My simple spring auth server config
@Configuration
class OAuth2ServerSecurityConfig {

   @Bean
   @Order(1)
   fun authorizationServerSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
      OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http)
      http.getConfigurer(OAuth2AuthorizationServerConfigurer::class.java)
         .oidc(Customizer.withDefaults())
      return http.build()
   }

   @Bean
   fun authorizationServerSettings(): AuthorizationServerSettings {
      return AuthorizationServerSettings.builder().build()
   }

   @Bean
   fun registeredClientRepository(): RegisteredClientRepository {
      val registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
         .clientId("client")
         .clientSecret("{bcrypt}secret")
         .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
         .build()

      return InMemoryRegisteredClientRepository(registeredClient)
   }

Solution

  • Unfortunately, the framework does strictly expect 0 or 1 beans to exist. If you can explain your use case, there might be other solutions. One option (workaround) is to wrap your second PasswordEncoder in another component, for example:

    @Bean
    public PasswordEncoderHolder passwordEncoderHolder() {
        return new PasswordEncoderHolder();
    }
    
    public static final class PasswordEncoderHolder {
        private final PasswordEncoder passwordEncoder = new V0PasswordEncoder();
    
        public PasswordEncoder getInstance() {
            return this.passwordEncoder;
        }
    }
    

    Then you only need an @Bean for v1-encoder.