Expected Behavior
It would be nice to have - a JwtDecoder supporting multiple token issuers - spring-boot to auto configure such a multi-issuers JwtDecoder when configuration has several issuer-uris
Current Behavior
spring.security.oauth2.resourceserver.jwt.issuer-uri is single valued and spring-boot provides with SupplierJwtDecoder or ReactiveJwtDecoder which are both designed to work with single issuer
Context
The company I currently work for has two different authorization-servers: one for intranet users and a different one for internet ones.
Some back-end services (resource-servers) are expected to provide data to users identified against either one or the other of the authorization-servers.
I came with following solution:
- JwtDecoders for both servlet and reactive apps which map a SupplierJwtDecoder (or ReactiveJwtDecoder) for each issuer-uri. When it is asked to decode a JWT, it first extract iss claim from the payload and then delegates JWT validation and conversion to the right SupplierJwtDecoder (or ReactiveJwtDecoder)
- Configuration aggregating issuer-uris (from spring.security.oauth2.resourceserver.jwt.issuer-uri and a private multi-valued property) and providing as JwtDecoder @bean, either this new JwtDecoder (if two or more issuers) or the former ones (no over-head for single issuer)
Notes:
- yaml and properties files syntax would allow to turn spring.security.oauth2.resourceserver.jwt.issuer-uri into a string array while keeping backward compatibility
- if you are interested and instruct me where and how, I'm ok to contribute some of the code I linked above
Comment From: jzheaux
Hi, @ch4mpy, thanks for the suggestion.
Have you already tried JwtIssuerAuthenticationManagerResolver? If so, what is making that approach not work? The reason multiple issuer support is at a higher level is so that the JwtAuthenticationConverter, etc. can also be keyed by the issuer URI.
Comment From: ch4mpy
@jzheaux thanks for quick answer.
No I had not tried JwtIssuerAuthenticationManagerResolver. To be totally honest I didn't know about it. But getting a quick look at it, it seems that it forces usage of JwtAuthenticationConverter which builds JwtAuthenticationTokens.
We do not use JwtAuthenticationToken but OidcAuthentication<T extends OidcToken> and thus have to configure a custom authentication converter.
Side note: turning spring.security.oauth2.resourceserver.jwt.issuer-uri into a String[] would ease the configuration of JwtIssuerAuthenticationManagerResolver too.
Comment From: jzheaux
Makes sense. You can provide your own AuthenticationManagerss like so:
@Bean
JwtIssuerAuthenticationManagerResolver byIssuer(MyJwtConverter converter) {
Map<String, AuthenticationManager> managers = new HashMap<>();
for (String issuer : issuers) {
JwtDecoder decoder = new SupplierJwtDecoder(() -> JwtDecoders.fromIssuerLocation(issuer));
JwtAuthenticationProvider provider = new JwtAuthenticationProvider(decoder);
provider.setJwtAuthenticationConverter(converter);
managers.put(issuer, provider::authenticate);
}
return new JwtIssuerAuthenticationManagerResolver(managers::get);
}
Agreed that changing to an array would simplify things. It's yet to be seen if multiple issuers is a common enough practice to merit adding to Boot. If you like, you can create a ticket in Spring Boot about it and then we can see if there are enough votes for it from the community.
Comment From: jzheaux
Also, https://github.com/spring-projects/spring-security/issues/9096 may provide some helpful further reading about JwtIssuerAuthenticationManagerResolver and JwtAuthenticationConverter, if you are interested.
Comment From: ch4mpy
spring-boot ticket created