You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Users may affect each other calling the server, that under the hood uses JwkProvider to validate the tokens. See the code below:
funmain() {
val jwksUrl ="https://login.microsoftonline.com/common/discovery/keys".let {
URI(it).normalize().toURL()
}
val foundKey =UrlJwkProvider(jwksUrl).all.first().id
val notFoundKey =UUID.randomUUID().toString()
val jwkProvider =JwkProviderBuilder(jwksUrl)
.cached(100, 10, TimeUnit.MINUTES)
.rateLimited(10, 1, TimeUnit.MINUTES)
.build()
/* someone requesting server with JWT, but which has invalid kid*/
repeat(11) {
assertThrows<SigningKeyNotFoundException> {
jwkProvider.get(notFoundKey)
}
}
/* someone would like to request the server with valid JWT and will get rate limit error*/
jwkProvider.get(foundKey)
}
Rate limit should not be there at all - it's responsibility of upper layer
It's more preferable to cache all jwks at once, not one by one
I think the strategy should be the following: cache all jwks response from server and return found jwk by kid. Cache should be update by time.
Alternatives and current work-arounds
classCachedJwkProvider(
privatevaldelegate:UrlJwkProvider,
privatevalexpiration:Duration
) : JwkProvider, Closeable {
privatevar cache = mapOf<String, Jwk>()
privateval cacheUpdaterJob = timer(
name ="jwks-cache-updater",
daemon =true,
period = expiration.toMillis()
) {
val actual = delegate.all.associateBy { it.id }
cache = actual
}
overridefunget(keyId:String): Jwk {
return cache[keyId] ?:throwSigningKeyNotFoundException("No key found with kid $keyId", null)
}
overridefunclose() {
cacheUpdaterJob.cancel()
}
}
funmain() {
val jwksUrl ="https://login.microsoftonline.com/common/discovery/keys".let {
URI(it).normalize().toURL()
}
val urlJwkProvider =UrlJwkProvider(jwksUrl)
val foundKey = urlJwkProvider.all.first().id
val notFoundKey =UUID.randomUUID().toString()
val jwkProvider =CachedJwkProvider(urlJwkProvider, Duration.ofMinutes(10))
/* waiting cache to load. It's up to implementation make the "get()" call blocking or not. But I prefer do not wait any 3party system, therefore we need to wait here just for test*/while (runCatching { jwkProvider.get(foundKey) }.isFailure)
/* someone requesting server with JWT, but which has invalid kid*/
repeat(1000) {
assertThrows<SigningKeyNotFoundException> {
jwkProvider.get(notFoundKey)
}
}
/* someone would like to request the server with valid JWT and will NOT get rate limit error*/
jwkProvider.get(foundKey)
}
The text was updated successfully, but these errors were encountered:
Thanks @scrat98 for the feedback and proposed alternatives! Regarding the rate-limiting, you can choose to not enable rate limiting by not configuring it, correct? Regarding the caching behavior, that's something we should look into - it sounds familiar, I'll look into if this is something we looked into in the past and if there was any findings regarding the cache behaving that way.
Describe the problem you'd like to have solved
Describe the ideal solution
I think the strategy should be the following: cache all jwks response from server and return found jwk by kid. Cache should be update by time.
Alternatives and current work-arounds
The text was updated successfully, but these errors were encountered: