Add auth package: MCIAS token validation with caching
- Authenticator with Login, ValidateToken, Logout - 30-second SHA-256-keyed cache with lazy eviction - TLS 1.3, custom CA support, service context (name + tags) - Error types: ErrInvalidToken, ErrInvalidCredentials, ErrForbidden, ErrUnavailable - Context helpers for TokenInfo propagation - 14 tests with mock MCIAS server and injectable clock Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
64
auth/cache.go
Normal file
64
auth/cache.go
Normal file
@@ -0,0 +1,64 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
Reference in New Issue
Block a user