package auth import ( "sync" "time" ) // cacheEntry holds a cached TokenInfo and its expiration time. type cacheEntry struct { info *TokenInfo expiresAt time.Time } // validationCache provides a concurrency-safe, TTL-based cache for token // validation results. Tokens are keyed by their SHA-256 hex digest. type validationCache struct { mu sync.RWMutex entries map[string]cacheEntry ttl time.Duration now func() time.Time // injectable clock for testing } // newCache creates a validationCache with the given TTL. func newCache(ttl time.Duration) *validationCache { return &validationCache{ entries: make(map[string]cacheEntry), ttl: ttl, now: time.Now, } } // get returns cached TokenInfo for the given token hash, or false if // the entry is missing or expired. Expired entries are lazily evicted. func (c *validationCache) get(tokenHash string) (*TokenInfo, bool) { c.mu.RLock() entry, ok := c.entries[tokenHash] c.mu.RUnlock() if !ok { return nil, false } if c.now().After(entry.expiresAt) { // Lazy evict the expired entry. c.mu.Lock() if e, exists := c.entries[tokenHash]; exists && c.now().After(e.expiresAt) { delete(c.entries, tokenHash) } c.mu.Unlock() return nil, false } return entry.info, true } // clear removes all entries from the cache. func (c *validationCache) clear() { c.mu.Lock() c.entries = make(map[string]cacheEntry) c.mu.Unlock() } // put stores TokenInfo in the cache with an expiration of now + TTL. func (c *validationCache) put(tokenHash string, info *TokenInfo) { c.mu.Lock() c.entries[tokenHash] = cacheEntry{ info: info, expiresAt: c.now().Add(c.ttl), } c.mu.Unlock() }