I have implemented code as specified here to add the multi-tenancy by issuer feature to my Spring Security configuration. However, when my Spring Boot application starts, I encounter the following error:
2021-10-26 | 10:31:37.762 | main | WARN | ConfigServletWebServerApplicationContext | Trace: | Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' available
2021-10-26 | 10:31:39.361 | main | ERROR | o.s.b.d.LoggingFailureAnalysisReporter | Trace: |
***************************
APPLICATION FAILED TO START
***************************
Description:
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' in your configuration.
The documentation states:
This is nice because the issuer endpoints are loaded lazily. In fact, the corresponding JwtAuthenticationProvider is instantiated only when the first request with the corresponding issuer is sent.
I wouldn't think that at application startup a JwtDecoder would be expected to be already instantiated according to this documentation. What am I missing in my configuration?
Update
After Steve Riesenberg's help, I have the following code compiling now. You can see in my code snippet what I used to have working (i.e., before we had the multi-tenant requirement) is now commented out:
//.jwt().jwtAuthenticationConverter(jwtAccessTokenConverter);
String[] issuers = new String[] {"https://www.example.com/auth/realms/example"};
JwtIssuerAuthenticationManagerResolver jwtIssuerAuthenticationManagerResolver =
new JwtIssuerAuthenticationManagerResolver(issuers);
...
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer(
oauth2ResourceServerConfigurer ->
oauth2ResourceServerConfigurer
.authenticationManagerResolver(jwtIssuerAuthenticationManagerResolver)
.authenticationEntryPoint(authenticationExceptionHandler));
// .jwt().jwtAuthenticationConverter(jwtAccessTokenConverter);
However, without the ability now to supply my own token converter since I had to remove .jwt(), I'm still unclear on what the default converter provides me.
Also, I'm not clear why I need to use the third constructor of JwtIssuerAuthenticationManagerResolver and provide my own AuthenticationManagerResolver<String>? If my code above is compiling, why do I need to do this?