Summary
TokenCache.Get in pkg/workloadmanager/client_cache.go only checks how long ago the cache entry was last accessed (time.Since(entry.lastAccess) > c.ttl). Every time a cached token is used, lastAccess is reset to time.Now(), continuously pushing the expiry window forward. The token's actual JWT exp claim is never checked on cache hits. By contrast, ClientCache.Get (which caches K8s user clients) correctly parses the JWT expiry via parseJWTExpiry and evicts entries once the token has expired. TokenCache has parseJWTExpiry available but does not use it.
Affected code
func (c *TokenCache) Get(token string) (found bool, authenticated bool, username string) {
c.mu.Lock()
defer c.mu.Unlock()
entry, exists := c.cache[token]
if !exists {
return false, false, ""
}
// Only checks sliding lastAccess — token's real JWT exp is neververified
if time.Since(entry.lastAccess) > c.ttl {
c.lruList.Remove(entry.element)
delete(c.cache, token)
return false, false, ""
}
// Resets the window — a busy token never expires from cache
entry.lastAccess = time.Now()
c.lruList.MoveToFront(entry.element)
return true, entry.authenticated, entry.username
}
Reproduction scenario
- Use a K8s service-account token with a 1-minute lifetime.
- Validate it once so it gets cached in
TokenCache as authenticated=true.
- Send requests every 4 minutes.
- Each hit updates
lastAccess, keeping the cache entry alive.
- After the token expires ,
TokenCache continues to return the cached authenticated=true result until there is a full 5-minute gap with no requests.
Expected behavior
TokenCache.Get should not return a cached authentication result once the JWT has expired (based on exp), even if requests keep arriving within the TTL window.
Actual behavior
TokenCache.Get can continue returning authenticated=true after the token has expired because only sliding lastAccess TTL is enforced.
Suggested fix
Store and enforce JWT expiry in tokenCacheEntry similar to clientCacheEntry:
Summary
TokenCache.Getinpkg/workloadmanager/client_cache.goonly checks how long ago the cache entry was last accessed (time.Since(entry.lastAccess) > c.ttl). Every time a cached token is used,lastAccessis reset totime.Now(), continuously pushing the expiry window forward. The token's actual JWTexpclaim is never checked on cache hits. By contrast,ClientCache.Get(which caches K8s user clients) correctly parses the JWT expiry viaparseJWTExpiryand evicts entries once the token has expired.TokenCachehasparseJWTExpiryavailable but does not use it.Affected code
Reproduction scenario
TokenCacheasauthenticated=true.lastAccess, keeping the cache entry alive.TokenCachecontinues to return the cachedauthenticated=trueresult until there is a full 5-minute gap with no requests.Expected behavior
TokenCache.Getshould not return a cached authentication result once the JWT has expired (based onexp), even if requests keep arriving within the TTL window.Actual behavior
TokenCache.Getcan continue returningauthenticated=trueafter the token has expired because only slidinglastAccessTTL is enforced.Suggested fix
Store and enforce JWT expiry in
tokenCacheEntrysimilar toclientCacheEntry: