auth0/go-jwt-middleware

Improve performance of JWKS Caching Provider

pdewilde opened this issue · 4 comments

Checklist

Describe the problem you'd like to have solved

Currently jwks/CachingProvider uses a global lock within its key function:

func (c *CachingProvider) KeyFunc(ctx context.Context) (interface{}, error) {

This is in the hot path of every call to validate:

key, err := v.keyFunc(ctx)

This effectively serializes every http call that the middelware handles, and adds a good deal of locking overhead.

Additionally, there is no "refresh before expiry" mechanism, which mean you are guaranteed to block when the key expires after TTL minutes.

Describe the ideal solution

To avoid a global lock, you could use a sync.RWMutex, which makes sense in the JWKS usecase where most calls to the cache will be readers. This allows multiple readers to concurrently access the critical path, and only blocks when a writer needs to update a key.

An example implementation can be found: https://github.com/sethvargo/go-cache/blob/main/ttl.go

To avoid the blocking problem, you can use a reloading chache. I'm not too familiar with the options in the go ecosystem, but in Java there is both caffine and Guava. The main idea is to reload keys asynchronously before they expire, so that you don't have to wait to refresh a hot key.

The semantics for such a cache are that a key is "stale" after some time, and stale reads return with the cached value immediately but asynchronously kick off a fetch (with additional checks that a fetch for the key isn't currently in progress). If a key does get old enough that expires, you then will have a blocking fetch.

Alternatives and current workarounds

No response

Additional context

No response

It appears that https://github.com/s12v/go-jwks may implement refreshing before expirary

Hey @pdewilde, thanks for the thorough issue! We'll review your suggestions and look to improve if the areas you've suggested.

We worked on improving this in #225 and it's now released as v2.2.0.

Thanks again for this issue @pdewilde!