Files
amcs/internal/auth/token_store.go
Hein 56c84df342 feat(auth): implement OAuth 2.0 authorization code flow and dynamic client registration
- Add OAuth 2.0 support with authorization code flow and dynamic client registration.
- Introduce new handlers for OAuth metadata, client registration, authorization, and token issuance.
- Enhance authentication middleware to support OAuth client credentials.
- Create in-memory stores for authorization codes and tokens.
- Update configuration to include OAuth client details.
- Ensure validation checks for OAuth clients in the configuration.
2026-03-26 21:17:55 +02:00

75 lines
1.5 KiB
Go

package auth
import (
"crypto/rand"
"encoding/hex"
"sync"
"time"
)
const defaultTokenTTL = time.Hour
type tokenEntry struct {
keyID string
expiresAt time.Time
}
// TokenStore issues and validates short-lived opaque access tokens for OAuth
// client credentials flow.
type TokenStore struct {
mu sync.RWMutex
tokens map[string]tokenEntry
ttl time.Duration
}
func NewTokenStore(ttl time.Duration) *TokenStore {
if ttl <= 0 {
ttl = defaultTokenTTL
}
s := &TokenStore{
tokens: make(map[string]tokenEntry),
ttl: ttl,
}
go s.sweepLoop()
return s
}
// Issue generates a new token for the given keyID and returns the token and its TTL.
func (s *TokenStore) Issue(keyID string) (string, time.Duration, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", 0, err
}
token := hex.EncodeToString(b)
s.mu.Lock()
s.tokens[token] = tokenEntry{keyID: keyID, expiresAt: time.Now().Add(s.ttl)}
s.mu.Unlock()
return token, s.ttl, nil
}
// Lookup validates a token and returns the associated keyID.
func (s *TokenStore) Lookup(token string) (string, bool) {
s.mu.RLock()
entry, ok := s.tokens[token]
s.mu.RUnlock()
if !ok || time.Now().After(entry.expiresAt) {
return "", false
}
return entry.keyID, true
}
func (s *TokenStore) sweepLoop() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
now := time.Now()
s.mu.Lock()
for token, entry := range s.tokens {
if now.After(entry.expiresAt) {
delete(s.tokens, token)
}
}
s.mu.Unlock()
}
}