diff --git a/extensions/jwt/CHANGELOG.md b/extensions/jwt/CHANGELOG.md index 1b5c94c..15bc467 100644 --- a/extensions/jwt/CHANGELOG.md +++ b/extensions/jwt/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [Unreleased] + +- Fixed: the JWKS host cache never expired, so `poll_interval` only re-read the key set fetched at startup and a provider key rotation was not picked up until the gateway restarted. The cache entry now lives for `poll_interval`. + ## [1.3.0] - 2025-07-15 - Added support for static headers returned with 401 responses. diff --git a/extensions/jwt/src/lib.rs b/extensions/jwt/src/lib.rs index 2bd0958..831ba5b 100644 --- a/extensions/jwt/src/lib.rs +++ b/extensions/jwt/src/lib.rs @@ -31,7 +31,13 @@ impl AuthenticationExtension for Jwt { let config: Config = config.deserialize()?; Ok(Self { - jwks_cache: Cache::builder("jwks", 1).timeout(config.poll_interval).build(), + // `timeout` bounds the wait for a concurrent fetch; it is not the entry lifetime. Without a + // TTL the JWKS fetched at startup lives for the whole process, so a provider key rotation is + // invisible until a restart and every token signed by the new key is rejected with a 401. + // Expire the entry after `poll_interval`, which is what the README promises. + jwks_cache: Cache::builder("jwks", 1) + .time_to_live(Some(config.poll_interval)) + .build(), jwks: None, config, })