right now the app is slowed a bit by HTTP checks it has to make when the app startsup. other implementations do a lot of these HTTP requests lazily, on the first use. this means that developers don't have to pay the sometimes significant cost of making the HTTP request to slow providers unless they trigger a request that needs it for their dev iteration cycle
Comment From: jzheaux
Hey, @joshlong!
The following setup is the preferred way to defer HTTP calls on startup:
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://authz.example.org/jwks
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://authz.example.org
If the jwk-set-uri property is present, then Spring Security will do checks lazily. This is because it relies on Nimbus, which operates lazily.
Here is a sample where these two properties point to an intentionally slow endpoint. The endpoint is configured to take 30 seconds to reply; however, the app still starts up in less than 2 seconds. This implies that no HTTP requests are getting made when the sample starts up.
Using that sample, you can try the following:
export TOKEN="eyJraWQiOiJjOWEyMzIxYy01N2NmLTQ1YmYtOWIzYS1kYjdjYzk3ODkwZTEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiYXVkIjoiZ29hbHMtY2xpZW50IiwibmJmIjoxNjI0OTk1ODMwLCJzY29wZSI6WyJnb2FsOnJlYWQiLCJ1c2VyOnJlYWQiLCJnb2FsOndyaXRlIl0sImlzcyI6Imh0dHBzOlwvXC9vYXV0aDIucHJvdmlkZXIuY29tIiwiZXhwIjoxNjI0OTk5NDMwLCJpYXQiOjE2MjQ5OTU4MzAsImp0aSI6IjM5OWIzMWUxLWZlNDItNDQwNS1iYjY0LWVkYmY5YTQ1M2I4ZSJ9.Z8Jy3JhFDK7Ijv9IWT9taLTbXXiXw8Eb0ZO2mD4pRFVLODYfAXLpt-fx5j9ZiApsrejj0pvyCBXOnjsroG8spZmSEHNlUO4_eVlrBtuB3pF54E7jDF6CrnS5nP2u5CSfsXAPoXqzcxHyghQ-n0vtaKhuCbgpVUyV6w5Gye7JrQa-Sj7eIPv4CfumC9vbphrnjVOlQvF_i7qJnp4cnDFM5AuGPkzdmOy_7nTwaYgXczyp4_2Qr4qPhju38g-GK-6S2YtPSmy5tgLGoMgllRU5elVmHZ5jddZAmnVu4couNM9jDxfIU3g3cfGiVSDVdXoqR3E2mpLuHn45Ml2mkmNl5g"
http :8080 "Authorization: Bearer $TOKEN"
and the request will timeout, having taken 30 seconds to get a reply from the JWKS endpoint.
I remember that you and Matt Raible were experiencing something different, though, where startup seemed to be different based on internet speeds. Can you share a sample that is making those HTTP calls on startup and we can take a look?
Comment From: mraible
Hello, @jzheaux!
I published a blog post with my findings on the Okta developer blog. See the build native images section to see how to build each example and run it. It also explains what we found.
You can find all the code in https://github.com/oktadev/native-java-examples.
Comment From: jzheaux
Hi, @mraible, @joshlong. I noticed something in Matt's blog post that I didn't before. I want to make sure that this ticket is resolving the issue you identified, and the way I read it this afternoon made me think that there might be more to discuss.
It says:
Our hypothesis is Micronaut and Quarkus do the JWKS lookup on the first request rather than at startup. That’s how they achieve faster startup times.
I think there's more to this since Micronaut, Quarkus, and Boot all three already do the JWKS lookup on the first request rather than at startup. I think their faster startup times are due to something else.
... Follow this Spring Security issue to see when Spring Security adds lazy OIDC discovery support.
This ticket indeed combined with https://github.com/spring-projects/spring-boot/issues/28122 adds lazy OIDC discovery support. But your second experiment already disables OIDC discovery, so even with these changes your second chart will stay the same.
IOW, after updating to Boot 2.6, your first chart and your second chart will be the same as the current second chart.
Is that what you would have expected? If not, we can reopen the ticket to take a second look.
FWIW, when I run the Boot sample from the blog post, I can confirm that Boot already does the JWKS lookup on the first request rather than at startup. I confirmed this by deploying a local authorization server and watching the request logs.
Comment From: mraible
Thanks for the clarification, @jzheaux. Will I need both the JWKS URI and issuer to trigger lazy-loading in Spring Boot 2.6? Or will using either one result in lazy-loading? If I want eager-loading instead, how do I configure that?
Comment From: jzheaux
Good questions, @mraible.
Will I need both the JWKS URI and issuer to trigger lazy-loading in Spring Boot 2.6?
No. In 2.6 both the JWK Set URI and its public keys will be lazily fetched when supplying an issuer-uri. (In 2.5, only the public keys are lazy.)
This will cause the first HTTP request to be longer since it now has more work to do. Applications that additionally provide a jwk-set-uri will see equivalent startup times, but a faster first HTTP request.
Or will using either one result in lazy-loading?
If only issuer-uri is provided, the JWK Set URI and keys are not fetched until the first HTTP request.
If both issuer-uri and jwk-set-uri are provided, then the keys are not fetched until the first HTTP request (same as 2.5).
If I want eager-loading instead, how do I configure that?
If you want the URI and keys to be fetched eagerly, you can configure the bean yourself, like so:
@Bean
JwtDecoder eagerJwtDecoder() {
return JwtDecoders.fromIssuerLocation(issuerUri);
}
If you want only the keys to be eager, this isn't something that Spring Security supports. But, I believe you can get it by querying the RemoteJWKSource yourself, and then providing that RemoteJWKSet to the NimbusJwtDecoder as is done in JwtDecoders.