I checked into this topic because I got the below error in my SpringBoot application:
.o.s.r.w.BearerTokenAuthenticationFilter : Authentication request for failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
What are validated in this BearerTokenAuthenticationFilter:
- signature
- expirationTime
Here is the call hierarchy:
org.springframework.web.filter.OncePerRequestFilter.doFilter
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter.doFilterInternal
org.springframework.security.authentication.ProviderManager.authenticate
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate
org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport.decode
org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport.createJwt
// Verify the signature
JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);
com.nimbusds.jwt.proc.DefaultJWTProcessor.process
List<? extends Key> keyCandidates = getJWSKeySelector().selectJWSKeys(signedJWT.getHeader(), context);
if (keyCandidates == null || keyCandidates.isEmpty()) {
throw NO_JWS_KEY_CANDIDATES_EXCEPTION;
}
private static final BadJOSEException NO_JWS_KEY_CANDIDATES_EXCEPTION =
new BadJOSEException("Signed JWT rejected: Another algorithm expected, or no matching key(s) found");
The question is:
How NimbusJwtDecoderJwkSupport be initialized?
By setup a BreakPoint at constructer method of it, we can easily found that
it is initialized at SpringBoot application startup:
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerJwkConfiguration.jwtDecoderByIssuerUri
@Bean
@Conditional(IssuerUriCondition.class)
@ConditionalOnMissingBean
public JwtDecoder jwtDecoderByIssuerUri() {
return JwtDecoders
.fromOidcIssuerLocation(this.properties.getJwt().getIssuerUri());
}
org.springframework.security.oauth2.jwt.JwtDecoders.fromOidcIssuerLocation
public static JwtDecoder fromOidcIssuerLocation(String oidcIssuerLocation) {
Map<String, Object> openidConfiguration = getOpenidConfiguration(oidcIssuerLocation);
String metadataIssuer = "(unavailable)";
if (openidConfiguration.containsKey("issuer")) {
metadataIssuer = openidConfiguration.get("issuer").toString();
}
if (!oidcIssuerLocation.equals(metadataIssuer)) {
throw new IllegalStateException("The Issuer \"" + metadataIssuer + "\" provided in the OpenID Configuration " +
"did not match the requested issuer \"" + oidcIssuerLocation + "\"");
}
OAuth2TokenValidator<Jwt> jwtValidator =
JwtValidators.createDefaultWithIssuer(oidcIssuerLocation);
NimbusJwtDecoderJwkSupport jwtDecoder =
new NimbusJwtDecoderJwkSupport(openidConfiguration.get("jwks_uri").toString());
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
}
Where it uses spring.security.oauth2.resourceserver.jwt.issuer-uri property to fetch
jwks uri and all the public keys.