Describe the bug Spring security authenticates when using single tenancy with:

  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri:

but the same url doesn't work with multi-tenancy.

To Reproduce So following the multi-tenancy guide: here.

I have followed the steps and added: the authentication manager (fake uri):

    JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver
        ("https://jwt.com/token.json");

with the new oauth2ResourceServer:

        .oauth2ResourceServer(oauth2 -> oauth2.
            authenticationManagerResolver(authenticationManagerResolver));

but the code always returns 401. When using the same uri in the single tenancy, it worked successfully, so I am not sure what I am missing. Here is the old way.

yaml:

    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://jwt.com/token.json

with the implementation:

        .oauth2ResourceServer(oauth2 -> oauth2.
            jwt(Customizer.withDefaults()));

This successfully authenticates.

I do see a difference in the logs as well.

With the authenticationResolver:

2023-03-21 11:46:11.085 DEBUG 15271 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [cabda122-1] HTTP GET "/api/v1/redacted"
2023-03-21 11:46:11.136 DEBUG 15271 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [cabda122-1] Completed 401 UNAUTHORIZED

and the old way clearly calls the resource server and decodes:

2023-03-21 11:41:53.843 DEBUG 12500 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [963bfd7a-1] HTTP GET "/api/v1/redacted"
2023-03-21 11:41:53.932 DEBUG 12500 --- [ctor-http-nio-3] o.s.w.r.f.client.ExchangeFunctions       : [4740b9ba] HTTP GET https://jwt.com/token.json
2023-03-21 11:41:54.695 DEBUG 12500 --- [ctor-http-nio-3] o.s.w.r.f.client.ExchangeFunctions       : [4740b9ba] [3475d267-1, L:/10.26.8.242:58490 - R:jwt.com/99.64.754.467:443] Response 200 OK
2023-03-21 11:41:54.713 DEBUG 12500 --- [ctor-http-nio-3] o.s.core.codec.StringDecoder             : [4740b9ba] [3475d267-1, L:/10.26.8.242:58490 - R:jwt.com/99.64.754.467:443] Decoded "REDACTED"
2023-03-21 11:41:54.736 DEBUG 12500 --- [     parallel-1] o.s.w.s.s.DefaultWebSessionManager       : Created new WebSession.
2023-03-21 11:41:55.090 DEBUG 12500 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [963bfd7a-1] Completed 200 OK

So what am I missing to have the multi-tenant way actually use the resource server? I don't see any mentions in the docs.

Here is a full example of the code:


Expected behavior A clear and concise description of what you expected to happen.

Sample

@EnableWebFluxSecurity
public class SecurityConfig {

  private interface GatewayRoutes {
    String ACTUATOR = "/actuator/**";
    String METRICS = "/actuator/prometheus";
  }

  private interface ActuatorScopes {
    String METRICS_SCOPE = "SCOPE_urn:cable:scope:metrics";
  }

  private interface TimelineApiRoutes {
    String ENTITIES = "/api/v2/entity/**";
    String ALL_REMAINING_ROUTES = "/**";
  }

  private interface TimelineApiScopes {
    String TIMELINE_SCOPE = "SCOPE_urn:cable:scope:timeline";
  }

  public SecurityConfig() throws InstantiationException, IllegalAccessException {}

  @Bean
  SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver =
        new JwtIssuerReactiveAuthenticationManagerResolver(
            "https://jwt.com/token.json");

    http.authorizeExchange(
            (exchanges) ->
                exchanges
                    .pathMatchers(ApiRoutes.ENTITIES)
                    .hasAuthority(ApiScopes.API_SCOPE)
                    .pathMatchers(ApiRoutes.ALL_REMAINING_ROUTES)
                    .permitAll()
                    .anyExchange()
                    .authenticated())
        .oauth2ResourceServer(
            oauth2 ->
                oauth2
                    .authenticationManagerResolver(authenticationManagerResolver));
    return http.build();
  }
}

Apologies as this is an internal ResourceServer, I don't have an example to work with fully.