Expected Behavior
Allow to supply custom:
* Converter<ServerWebExchange, Mono<String>> issuerConverter
into JwtIssuerReactiveAuthenticationManagerResolver
Current Behavior
private final Converter<ServerWebExchange, Mono<String>> issuerConverter is hardcoded and there is no way to supply own implementation.
public final class JwtIssuerReactiveAuthenticationManagerResolver
implements ReactiveAuthenticationManagerResolver<ServerWebExchange> {
private final ReactiveAuthenticationManagerResolver<String> issuerAuthenticationManagerResolver;
private final Converter<ServerWebExchange, Mono<String>> issuerConverter = new JwtClaimIssuerConverter();
Context
I am migrating my project from single tenant to multi tenant using Spring Security. I used to do
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
...
.oauth2ResourceServer(server -> server
.jwt(jwt -> jwt.jwtAuthenticationConverter(
new CustomTokenConverter()
)))
}
@Bean
ReactiveJwtDecoder jwtDecoder() {
return ReactiveJwtDecoders.fromOidcIssuerLocation(securityConfigurationProperties.getIssuerLocation());
}
and now with following implementation
@Autowired
ReactiveAuthenticationManagerResolver<ServerWebExchange> reactiveAuthenticationManagerResolver;
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
...
.oauth2ResourceServer(server -> server.authenticationManagerResolver(reactiveAuthenticationManagerResolver))
}
@Bean
ReactiveAuthenticationManagerResolver<ServerWebExchange> reactiveAuthenticationManagerResolver() {
return new JwtIssuerReactiveAuthenticationManagerResolver(securityConfigurationProperties.getIssuers());
}
I can't use
CustomTokenConverter implements Converter<Jwt, Mono<AbstractAuthenticationToken>>
anymore.
I need to copy entire JwtIssuerReactiveAuthenticationManagerResolver class and:
* inject own implementation of issuerConverter
Comment From: bbednarek
After digging into the code I managed to solve it by implementing
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders;
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
import reactor.core.publisher.Mono;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
public class TrustedIssuerOidcAuthenticationManagerResolver implements ReactiveAuthenticationManagerResolver<String> {
private final Map<String, String> issuers;
private final Converter<Jwt, Mono<AbstractAuthenticationToken>> jwtAuthenticationConverter;
private final Map<String, Mono<ReactiveAuthenticationManager>> authenticationManagers = new ConcurrentHashMap<>();
public TrustedIssuerOidcAuthenticationManagerResolver(
Collection<String> trustedIssuers,
Converter<Jwt, Mono<AbstractAuthenticationToken>> jwtAuthenticationConverter
) {
this.issuers = trustedIssuers.stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
this.jwtAuthenticationConverter = jwtAuthenticationConverter;
}
@Override
public Mono<ReactiveAuthenticationManager> resolve(String issuer) {
return this.authenticationManagers.computeIfAbsent(issuer, this::fromTenant);
}
private Mono<ReactiveAuthenticationManager> fromTenant(String issuer) {
return Mono.justOrEmpty(this.issuers.get(issuer))
.map(ReactiveJwtDecoders::fromOidcIssuerLocation)
.map(JwtReactiveAuthenticationManager::new)
.map(it -> {
it.setJwtAuthenticationConverter(jwtAuthenticationConverter);
return it;
});
}
}
and passing it into JwtIssuerReactiveAuthenticationManagerResolver
@Bean
ReactiveAuthenticationManagerResolver<ServerWebExchange> reactiveAuthenticationManagerResolver() {
return new JwtIssuerReactiveAuthenticationManagerResolver(
new TrustedIssuerOidcAuthenticationManagerResolver(
securityConfigurationProperties.getIssuers(),
new OrgUserTokenConverter()
)
);
}