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