The default JWKS retriever in Spring Security (RestOperationsResourceRetriever) performs its operation synchronously as part of processing an incoming request.
We have noticed a couple of issues due to this synchronous nature.
Extra latency When JWKS retrieval happens, the request gets extra latency.
Request failure
When JWKS retrieval request fails, the entire request fails with 401.
org.springframework.security.oauth2.jwt.JwtException: An error occurred while attempting to decode the Jwt:
Couldn't retrieve remote JWK set: org.springframework.web.client.ResourceAccessException:
I/O error on GET request for "https://example.com/auth/token-public-key":
Connect to example.com:443 [example.com/10.0.0.1] failed:
Connection refused (Connection refused);
nested exception is org.apache.http.conn.HttpHostConnectException:
Connect to example.com:443 [example.com/10.0.0.1] failed:
Connection refused (Connection refused)
To solve the issues, I have implemented a custom JWKS retriever. Here is the Implementation and unit test.
The highlights of this implementation are:
- Perform JWKS renewal asynchronous to the incoming request. (use reactor api)
- Perform synchronous request when necessary (e.g. initial request, when JWKS reached its configurable lifetime)
- Two configurable expirations
- Expiration to renew the JWKS.
When this expiration is reached, the retriever tries to renew the JWKS asynchronously.
- Expiration for the lifetime to use the current JWKS.
Until it reaches to the lifetime, the current JWKS is used even if async renewals have failed.
Once it has passed the lifetime, it performs a synchronous blocking request to get a new JWKS.
We are using this in our production and so far haven't seen any issues.
I would like to donate this code if Spring Security thinks it is useful.
I don't have the integration part of the code to Spring Security since we specify the async retriever directly to our JwtDecoder bean.
But, in any case, if it is a good enhancement to Spring Security, please feel free to take the code and modify them.
Thanks,
Comment From: jzheaux
Thank you for the offer, @ttddyy. This definitely contributes to the conversation happening now in https://github.com/spring-projects/spring-security/issues/9646.
I'm going to close this as a duplicate but will link to it from #9646 so that we can easily find the code should it be needed.