Describe the bug
We have a Spring Boot application that uses JWT Token based authentication. The issuer and IDP of the token is Auth0.
Sometime back we got error An error occurred while attempting to decode the Jwt: Timeout while waiting for cache refresh
. The error occurred at the time of validating JWT token provided in the header.
At the time of error, we checked the {{issuer}}/.well-known/openid-configuration
URL and it was giving response within a second. Hence we wanted to understand why it got timeout while waiting for cache refresh.
Surprisingly this error got resolved after we restarted the server.
Stacktrace
org.springframework.security.authentication.AuthenticationServiceException: An error occurred while attempting to decode the Jwt: Timeout while waiting for cache refresh (15000ms exceeded)
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt(JwtAuthenticationProvider.java:106)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:88)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.authentication.ObservationAuthenticationManager.lambda$authenticate$1(ObservationAuthenticationManager.java:54)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at io.micrometer.observation.Observation.observe(Observation.java:564)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.authentication.ObservationAuthenticationManager.authenticate(ObservationAuthenticationManager.java:53)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:144)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:227)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.wrapFilter(ObservationFilterChainDecorator.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$AroundFilterObservation$SimpleAroundFilterObservation.lambda$wrap$0(ObservationFilterChainDecorator.java:323)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$ObservationFilter.doFilter(ObservationFilterChainDecorator.java:224)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.ObservationFilterChainDecorator$VirtualFilterChain.doFilter(ObservationFilterChainDecorator.java:137)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.ServletRequestPathFilter.doFilter(ServletRequestPathFilter.java:52)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebSecurityConfiguration.java:319)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.servlet.handler.HandlerMappingIntrospector.lambda$createCacheFilter$4(HandlerMappingIntrospector.java:267)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:113)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:74)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy.doFilter(WebMvcSecurityConfiguration.java:240)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:362)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:278)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:110)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:116)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:398)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:903)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1769)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1189)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:658)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)"
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider | | | |" at java.base/java.lang.Thread.run(Thread.java:840)"
To Reproduce
Follow the SecurityConfiguration
and CustomJwtDecoderProviderConfigurationUtils
to setup JWT token based authentication. However, not sure how the issue can be reproduced
import com.nimbusds.jose.proc.SecurityContext;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.http.HttpHeaders;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.util.Map;
import java.util.Set;
@Configuration
@EnableWebSecurity
@EnableScheduling
@EnableWebMvc
@Profile({"test-profile"})
public class SecurityConfiguration implements WebMvcConfigurer {
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
@Bean
public JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = fromOidcIssuerLocation(issuer);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
}
public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerLocation) {
Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty");
Map<String, Object> configuration = CustomJwtDecoderProviderConfigurationUtils.getConfigurationForOidcIssuerLocation(oidcIssuerLocation);
return (T) withProviderConfiguration(configuration, oidcIssuerLocation);
}
private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
String jwkSetUri = configuration.get("jwks_uri").toString();
CustomRemoteJWKSet<SecurityContext> jwkSource = new CustomRemoteJWKSet(url(jwkSetUri));
Set<SignatureAlgorithm> signatureAlgorithms = CustomJwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(jwkSource);
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).jwsAlgorithms((algs) -> {
algs.addAll(signatureAlgorithms);
}).build();
jwtDecoder.setJwtValidator(jwtValidator);
return jwtDecoder;
}
private static URL url(String url) {
try {
return new URL(url);
} catch (IOException var2) {
throw new UncheckedIOException(var2);
}
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests( (registry -> {
registry.requestMatchers("/**").authenticated();
})).addFilterAfter(new DoubleAuthenticationFilter(jwtDecoder()), BearerTokenAuthenticationFilter.class).
oauth2ResourceServer( oAuth2 -> {
oAuth2.jwt( jwtConfigurer -> {
jwtConfigurer.decoder(jwtDecoder());
}).bearerTokenResolver(this::tokenExtractor);
}).headers( httpSecurityHeadersConfigurer -> {
httpSecurityHeadersConfigurer.httpStrictTransportSecurity( hstsConfig -> {
hstsConfig.requestMatcher(AnyRequestMatcher.INSTANCE).
includeSubDomains(true).maxAgeInSeconds(31536000);
});
});
return http.build();
}
public String tokenExtractor(HttpServletRequest request) {
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (header != null) {
return header.replace("Bearer ", "");
}
return null;
}
}
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.KeySourceException;
import com.nimbusds.jose.jwk.*;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.util.Assert;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.*;
public class CustomJwtDecoderProviderConfigurationUtils {
private static final String OIDC_METADATA_PATH = "/.well-known/openid-configuration";
private static final String OAUTH_METADATA_PATH = "/.well-known/oauth-authorization-server";
private static final RestTemplate rest = new RestTemplate();
private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() {
};
private CustomJwtDecoderProviderConfigurationUtils() {
}
static Map<String, Object> getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) {
return getConfiguration(oidcIssuerLocation, oidc(URI.create(oidcIssuerLocation)));
}
static Map<String, Object> getConfigurationForIssuerLocation(String issuer) {
URI uri = URI.create(issuer);
return getConfiguration(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri));
}
static void validateIssuer(Map<String, Object> configuration, String issuer) {
String metadataIssuer = getMetadataIssuer(configuration);
Assert.state(issuer.equals(metadataIssuer), () -> {
return "The Issuer \"" + metadataIssuer + "\" provided in the configuration did not match the requested issuer \"" + issuer + "\"";
});
}
static Set<SignatureAlgorithm> getSignatureAlgorithms(JWKSource<SecurityContext> jwkSource) {
JWKMatcher jwkMatcher = (new JWKMatcher.Builder()).publicOnly(true).keyUses(new KeyUse[]{KeyUse.SIGNATURE, null}).keyTypes(new KeyType[]{KeyType.RSA, KeyType.EC}).build();
HashSet jwsAlgorithms = new HashSet();
Iterator var4;
try {
List<? extends JWK> jwks = jwkSource.get(new JWKSelector(jwkMatcher), (SecurityContext)null);
var4 = jwks.iterator();
while(var4.hasNext()) {
JWK jwk = (JWK)var4.next();
if (jwk.getAlgorithm() != null) {
JWSAlgorithm jwsAlgorithm = JWSAlgorithm.parse(jwk.getAlgorithm().getName());
jwsAlgorithms.add(jwsAlgorithm);
} else if (jwk.getKeyType() == KeyType.RSA) {
jwsAlgorithms.addAll(JWSAlgorithm.Family.RSA);
} else if (jwk.getKeyType() == KeyType.EC) {
jwsAlgorithms.addAll(JWSAlgorithm.Family.EC);
}
}
} catch (KeySourceException var7) {
throw new IllegalStateException(var7);
}
Set<SignatureAlgorithm> signatureAlgorithms = new HashSet();
var4 = jwsAlgorithms.iterator();
while(var4.hasNext()) {
JWSAlgorithm jwsAlgorithm = (JWSAlgorithm)var4.next();
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.from(jwsAlgorithm.getName());
if (signatureAlgorithm != null) {
signatureAlgorithms.add(signatureAlgorithm);
}
}
Assert.notEmpty(signatureAlgorithms, "Failed to find any algorithms from the JWK set");
return signatureAlgorithms;
}
private static String getMetadataIssuer(Map<String, Object> configuration) {
return configuration.containsKey("issuer") ? configuration.get("issuer").toString() : "(unavailable)";
}
private static Map<String, Object> getConfiguration(String issuer, URI... uris) {
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of \"" + issuer + "\"";
URI[] var3 = uris;
int var4 = uris.length;
int var5 = 0;
while(true) {
if (var5 < var4) {
URI uri = var3[var5];
try {
RequestEntity<Void> request = RequestEntity.get(uri).build();
ResponseEntity<Map<String, Object>> response = rest.exchange(request, STRING_OBJECT_MAP);
Map<String, Object> configuration = response.getBody();
if (null != configuration)
Assert.isTrue(configuration.get("jwks_uri") != null, "The public JWK set URI must not be null");
return configuration;
} catch (IllegalArgumentException var10) {
throw var10;
} catch (RuntimeException var11) {
if (var11 instanceof HttpClientErrorException && ((HttpClientErrorException)var11).getStatusCode().is4xxClientError()) {
++var5;
continue;
}
throw new IllegalArgumentException(errorMessage, var11);
}
}
throw new IllegalArgumentException(errorMessage);
}
}
private static URI oidc(URI issuer) {
return UriComponentsBuilder.fromUri(issuer).replacePath(issuer.getPath() + "/.well-known/openid-configuration").build(Collections.emptyMap());
}
private static URI oidcRfc8414(URI issuer) {
return UriComponentsBuilder.fromUri(issuer).replacePath("/.well-known/openid-configuration" + issuer.getPath()).build(Collections.emptyMap());
}
private static URI oauth(URI issuer) {
return UriComponentsBuilder.fromUri(issuer).replacePath("/.well-known/oauth-authorization-server" + issuer.getPath()).build(Collections.emptyMap());
}
}
Expected behavior
Not sure what to expect but such kind of errors should not require server restart. Also, response from {{auth0_domain}}/.well-known/openid-configuration
did not take more than a second to provide public keys. Hence it should not have timed out for refreshing cache.
Comment From: jzheaux
Thanks for getting in touch, @chintan-siemens! It feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add more detail if you feel this is a genuine bug.