Answer
Under high‑latency or throttled network conditions, relying on the stateless quarkus‑jwt to validate the exp claim locally provides more predictable latency and throughput, while automatic revocation (quarkus.oidc.revocation.enabled=true) adds variable network latency unless the revocation check is served from a low‑latency local cache.
Confirmed facts
- Quarkus JWT validation rejects any request with an expired
exp claim by default (quarkus.jwt.enabled=true and quarkus.jwt.exp.validation=true). No extra code is needed.
- Automatic revocation requires contacting Keycloak’s token revocation endpoint on each request unless the JTI (token ID) is checked against a local cache that is updated on logout or admin revocation.
- The revocation cache must store each JTI with a TTL matching the token’s remaining lifetime to avoid blocking legitimate tokens or allowing revoked ones to linger.
Likely explanation
When network round‑trips to Keycloak are slow or subject to throttling, each request that performs an automatic revocation lookup incurs added latency and increases response‑time variance. This can reduce throughput, especially under bursty traffic. In contrast, the local exp check is a pure in‑memory operation with negligible and consistent overhead, giving tighter latency guarantees.
However, the local expiration check leaves a window where a stolen token remains usable until its natural expiry (plus any clock skew). This window can be bounded by issuing short‑lived access tokens (e.g., 5–15 min) and using secure refresh‑token rotation. Application‑level caching of revoked JTIs (with a TTL equal to the token’s remaining life) can further reduce the window without requiring a per‑request call to Keycloak.
Steps for this case
- If you need immediate revocation and can provision a low‑latency store (Redis, Cassandra, or an embedded Caffeine cache) close to the service:
- Add the SmallRye JWT extension.
- Set
quarkus.oidc.revocation.enabled=true and point to the Keycloak revocation endpoint.
- Configure a cache (e.g.,
quarkus.cache.caffeine.revoked-tokens.enabled=true or a Redis datasource).
- Create a custom
@ApplicationScoped SecurityPolicy that extracts the JTI from the incoming JWT and rejects the request if the JTI is present in the cache.
- On logout or admin revocation, insert the JTI into the cache with a TTL equal to the token’s remaining lifetime (
exp - now).
- If you cannot guarantee a low‑latency revocation store, prefer the manual expiration approach:
- Ensure
quarkus.jwt.enabled=true and the issuer/public‑key are configured.
- Leave
quarkus.jwt.exp.validation=true (default).
- Optionally add a simple bean to log expiration events for auditing.
- Issue access tokens with a short lifetime (5–15 min) and implement refresh‑token rotation.
Missing diagnostic detail
Do you already have access to a low‑latency distributed cache (Redis or embedded Caffeine) that can be co‑located with the Quarkus service? If not, the automatic revocation path may introduce unacceptable latency, making the manual expiration approach the safer choice for least‑privilege under high‑latency or throttled conditions.