Description
Expected Behavior
It would be great if the ReactiveJwtDecoders.java
helper methods exposed the underlying NimbusReactiveJwtDecoder & associated methods rather than only the interface just as the non-reactive version (JwtDecoders.java
) does. This makes it easier to override the default Jwt validator(s) when necessary.
For example, the decompiled code today in JwtDecoders.java looks like this:
public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
return withProviderConfiguration(configuration, oidcIssuerLocation);
}
public static <T extends JwtDecoder> T fromIssuerLocation(String issuer) {
Assert.hasText(issuer, "issuer cannot be empty");
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
}
It would be awesome to see similar code in ReactiveJwtDecoders that returns anything extending ReactiveJwtDecoder instead of just the interface so that it can be tweaked outside of the default use-case if needed. Ideal code would look like:
public static <T extends ReactiveJwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
return withProviderConfiguration(configuration, oidcIssuerLocation);
}
public static <T extends ReactiveJwtDecoder> T fromIssuerLocation(String issuer) {
Assert.hasText(issuer, "issuer cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(issuer);
return withProviderConfiguration(configuration, issuer);
}
private static <T extends ReactiveJwtDecoder> T withProviderConfiguration(Map<String, Object> configuration, String issuer) {
JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
String jwkSetUri = configuration.get("jwks_uri").toString();
NimbusReactiveJwtDecoder jwtDecoder = NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).jwtProcessorCustomizer(ReactiveJwtDecoderProviderConfigurationUtils::addJWSAlgorithms).build();
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
}
Current Behavior
ReactiveJwtDecoders.java currently looks like this:
public static ReactiveJwtDecoder fromOidcIssuerLocation(String oidcIssuerLocation) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
return withProviderConfiguration(configuration, oidcIssuerLocation);
}
public static ReactiveJwtDecoder fromIssuerLocation(String issuer) {
Assert.hasText(issuer, "issuer cannot be empty");
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(issuer);
return withProviderConfiguration(configuration, issuer);
}
and the only accessible method on ReactiveJwtDecoder is .decode()
. This means that because JwtDecoderProviderConfigurationUtils.java
is not publicly accessible, I'll need to re-write a good portion of its functionality in my own code to essentially rebuild what is happening in the ReactiveJwtDecoders method with some customization of the JWT validation (in our case we want to allow for multiple allowed issuers which allows us to have a CNAMEd issuer + non-CNAMEd or do things like switch issuer without downtime).
Context
The default JwtValidator with issuer provided validates the Jwt Timestamp (with 60s clock skew) + the Issuer (only). We want to validate:
- Timestamp (with customizable skew)
- Issuer (as "one-of" many possible issuers, configurable)
- Audience
The current reactive code makes it pretty difficult to both harness the "smarts" inside JwtDecoderProviderConfigurationUtils
that uses OpenID provider config or an Authorization Server Metadata request to get provider information AND customize the JWT validations, while the WebMVC version of JwtDecoders.java
makes this fairly easy.