7

I'm suffering this one: Couldn't retrieve remote JWK set: Read timed out

I am using Java 11 with Spring boot 2.5.3,

for dependencies:

  • spring-security-oauth2-jose 5.5.1,
  • spring-boot-starter-oauth2-client
  • spring-boot-starter-security

Auth0 as users provider.

Any clue ?

...
Caused by: java.lang.IllegalStateException: com.nimbusds.jose.RemoteKeySourceException: Couldn't retrieve remote JWK set: Read timed out
    at org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(JwtDecoderProviderConfigurationUtils.java:107) ~[spring-security-oauth2-jose-5.5.1.jar:5.5.1]
    at org.springframework.security.oauth2.jwt.ReactiveJwtDecoders.withProviderConfiguration(ReactiveJwtDecoders.java:120) ~[spring-security-oauth2-jose-5.5.1.jar:5.5.1]
    at org.springframework.security.oauth2.jwt.ReactiveJwtDecoders.fromIssuerLocation(ReactiveJwtDecoders.java:100) ~[spring-security-oauth2-jose-5.5.1.jar:5.5.1]
    at org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerJwkConfiguration$JwtConfiguration.jwtDecoderByIssuerUri(ReactiveOAuth2ResourceServerJwkConfiguration.java:95) ~[spring-boot-autoconfigure-2.5.3.jar:2.5.3]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.9.jar:5.3.9]
JPG
  • 750
  • 2
  • 8
  • 23
  • Does this answer your question? [How to increase RemoteJWKSet cache TTL in spring-security 5.2](https://stackoverflow.com/questions/60409678/how-to-increase-remotejwkset-cache-ttl-in-spring-security-5-2) – Adam Millerchip Sep 10 '21 at 18:08

6 Answers6

2

I found that sometimes I still receive the timeout from Auth0. It seems that their response to the JWK request is slow enough to need more waiting time. The timeouts that are making it fail in my project are two constants defined in com.nimbusds.jose.jwk.source.RemoteJWKSet.

public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 500;
public static final int DEFAULT_HTTP_READ_TIMEOUT = 500;

So I came with a very ugly solution, that worked for me, until someone turns these constants into properties.

Assuming you are using the Auth0 guide https://auth0.com/docs/quickstart/backend/java-spring-security5/01-authorization and that you added the "jwk-set-uri: https://your-domain/.well-known/jwks.json" key to your properties, as the com.nimbusds.jose.jwk.source.RemoteJWKSet is not a bean I could replace by my own custom implementation or could not use AOP to replace the timeout parameters as those are passed in the constructor, I had to duplicate things making my own versions of the required classes.

SOLUTION

I made my own version of the following classes just copying the code from the original and customizing where I needed:

  • com.nimbusds.jose.jwk.source.RemoteJWKSet (customized increasing the timeout constants)
    public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 1000;
    public static final int DEFAULT_HTTP_READ_TIMEOUT = 1000;
  • org.springframework.security.oauth2.jwt.JwtDecoders (customized creating an instance of my custom version of the RemoteJWKSet instead)
    private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
        CustomJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, 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;
    }
  • org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils (this has no customization but I had to copy it as it has no visibility from outside its package)

Then I just replaced the decoder in my configuration by the custom one

@Bean
public JwtDecoder jwtDecoder() {
    NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
            CustomJwtDecoders.fromOidcIssuerLocation(issuer);

    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(
        withIssuer, audienceValidator);

    jwtDecoder.setJwtValidator(withAudience);

    return jwtDecoder;
}

So far, so good, no more timeouts.

Dharman
  • 30,962
  • 25
  • 85
  • 135
2

Main difference between this answer and Juan's is that rather than hack RemoteJWKSet I'm using DefaultResourceRetriever. I also had to copy and paste JwtDecoderProviderConfigurationUtils as JwtUtil to gain access to the helper functions that deal with the OAuth DOM.

What a pain.


  @Value("${auth0.audience}")
  private String audience;

  @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
  private String issuer;

  @Bean
  JwtDecoder jwtDecoder(RestTemplateBuilder builder, CacheManager cacheManager) throws MalformedURLException {

    RestOperations restOperations = builder
        .setConnectTimeout(Duration.ofSeconds(60))
        .setReadTimeout(Duration.ofSeconds(60))
        .build();

    // JwtUtil is a copy and paste of JwtDecoderProviderConfigurationUtils in order to be able to
    // call a couple of functions. The Spring class is inconveniently package protected.
    Map<String, Object> configuration = JwtUtil.getConfigurationForIssuerLocation(issuer);
    String jwkSetUri = configuration.get("jwks_uri").toString();
    RemoteJWKSet<SecurityContext> jwkSource = new RemoteJWKSet<>(new URL(jwkSetUri), new DefaultResourceRetriever());
    NimbusJwtDecoder nimbusDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
        .jwsAlgorithms((algos) -> algos.addAll(JwtUtil.getSignatureAlgorithms(jwkSource)))
        .restOperations(restOperations)
        .cache(Objects.requireNonNull(cacheManager.getCache("auth")))
        .build();

    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
    OAuth2TokenValidator<Jwt> withAudience =
        new DelegatingOAuth2TokenValidator<>(JwtValidators.createDefaultWithIssuer(issuer), audienceValidator);

    nimbusDecoder.setJwtValidator(withAudience);

    return nimbusDecoder;
  }

YAML

auth0:
  # Replace with the API Identifier for your Auth0 API.
  audience: https://myapp.eu.auth0.com/api/v2/

# The following is standard Spring Security OAuth2 configuration.
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          # Replace with the domain of your Auth0 tenant.
          # Note the trailing slash is important!
          issuer-uri: https://myapp.eu.auth0.com/

Mike Holdsworth
  • 1,088
  • 11
  • 12
1

I had the same issue, seems that it could not get the jwk set from the well-known configuration. I still need to find out if this is an issue in my Project or in Auth0. Try adding it explicitly in your properties, it worked for me after that. Mine looks something like this:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://your-domain/
          jwk-set-uri: https://your-domain/.well-known/jwks.json

Additionally, the spring-boot-starter-oauth2-resource-server dependency should be enough to do this. I'm using 2.5.4.

bloussou
  • 91
  • 4
  • still getting timeouts with jwk-set-uri set – JPG Oct 19 '21 at 15:13
  • how to resolve this ? – Feroz Siddiqui Oct 26 '21 at 08:56
  • I don't this the issue is related to issuer-uri/ jwk-set-uri. I already had this config and still was facing Read timeout issue. Overriding RemoteJWKSet is the only solution at the moment. – Chintan Pandya Dec 06 '21 at 06:03
  • @FerozSiddiquim it can resolve the issue if JWTDecoder bean is not explicitly defined, https://docs.spring.io/spring-security/site/docs/5.2.12.RELEASE/reference/html/oauth2.html#oauth2resourceserver-jwt-sansboot says default bean is used when not defined. Default bean will require these fields, in my case after springboot 2.5.0 `jwk-set-uri:` became mandatorily required. before that i believe a util class was guessing the uri – Captain Levi Feb 21 '22 at 07:29
1

You can configure custom timeout when creating the bean for JwtDecoder like below:

@Bean
public JwtDecoder jwtDecoder(RestTemplateBuilder builder) {
    RestOperations rest = builder
            .setConnectionTimeout(60000)
            .setReadTimeout(60000)
            .build();

    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).restOperations(rest).build();
    return jwtDecoder;
}

Source - Spring Security Documentation: https://docs.spring.io/spring-security/site/docs/5.2.12.RELEASE/reference/html/oauth2.html#oauth2resourceserver-jwt-timeouts

In the case of Auth0 configuration, the jwtDecoder bean looks something like below:

    @Bean
    JwtDecoder jwtDecoder(RestTemplateBuilder builder) {
        RestOperations rest = builder
                .setConnectTimeout(Duration.ofSeconds(10))
                .setReadTimeout(Duration.ofSeconds(10))
                .build();

        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).restOperations(rest).build();

        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder
                .setJwtValidator(withAudience);

        return jwtDecoder;
    }
Sai Upadhyayula
  • 2,404
  • 3
  • 23
  • 35
0

I have found that my auth0 tenant was hosted in a different location (in US, even though I live in Europe), so as suggested in the first answer, reading timeout constraints were too small. This issue went away after I created a new tenant in a different region.

  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 20 '21 at 08:15
0

Timeout settings can be increased by simply defining the following system properties:

System.setProperty("com.nimbusds.jose.jwk.source.RemoteJWKSet.defaultHttpReadTimeout", "2000")

System.setProperty("com.nimbusds.jose.jwk.source.RemoteJWKSet.defaultHttpConnectTimeout", "2000")

DefaultResourceRetriever is initialized with Http Connection Timeout and Http Read Timeout values during RemoteJWKSet initialization, which uses the default values, i.e. 500 milliseconds, if not specifically set in system properties.

Syuzi
  • 56
  • 1