How to use request-scoped tokens with caching

Heiko W. Rupp
ITNEXT
Published in
2 min readApr 25, 2022

--

In a project I am working on I need to get tokens from a single-signon (SSO) -server to be able to talk to another service. That token has a limited lifespan and needs to be refreshed from time to time.

Diagram with my service in the middle, receiving an event and then calling SSO and the other service
100000k feet overview

The setup here is relatively simple. And in theory My Service could even hit SSO on each incoming event, which is of course ok when testing by e.g. hitting a http endpoint with a browser to trigger the event.
The situation is different when running the code in production, as a) it poses a huge load on the SSO service, which most of the times is a shared resource. And b) each call to SSO adds additional latency to users of My Service.

Internally in my service which runs on Quarkus, I use CDI to further pass the token around, like the following

@Produces
public Token getToken() {
Token = sso.getToken(credentials);
}

Now the question about CDI scopes comes into play. Using @ApplicationScoped nicely caches the token, but fails to invalidate it when it has expired. Using @RequestScoped on the other hand, calls the method each time the token gets injected into a bean, so that it is fresh, but there is no caching at all.

The solution is to indirectly obtaining the token from the CDI production process like this:

@RequestScoped
@Produces
public Token getToken() {
Token = getTokenInternal()
}

and then using Quarkus-Cache to cache and expire the token:

@CacheResult(cacheName = "token-cache")
Token getTokenInternal() {
Token = sso.getToken(credentials);
}

With this code we tell Quarkus to cache the token obtained from SSO (if the remote call did not throw an Exception). The time the token is cached is determined by an entry in application.properties:

quarkus.cache.caffeine.token-cache.expire-after-write=PT120s

Tokens in the cache expire in this case after 2 minutes. Of course that value should be tuned with the expiry of the token and should ideally be a bit shorter to prevent any race conditions.

We could have parsed the token for its refresh time and then tuned the cache from within code, but this solution is pretty straightforward and the framework does all of the heavy lifting.

The full code is available on GitHub.

--

--