Files
traefikoidc/internal/token/verifier.go
T
lukaszraczylo c3f23cb99b Release 0.7.5 (#70)
* Resolve issue with opaque tokens not being parsed correctly

* Increase test coverage

* Further improvements to test coverage and code quality

* Add new providers.

* fixup! Add new providers.

* Cleanup.

* fixup! Cleanup.

* fixup! fixup! Cleanup.

* fixup! fixup! fixup! Cleanup.

* fixup! fixup! fixup! fixup! Cleanup.

* Memory management optimisation

24 bytes per Put < 256-4096 bytes per buffer allocation avoided (10-170x difference)

* Pooling cleanup.
2025-10-01 12:13:10 +01:00

140 lines
3.7 KiB
Go

// Package token provides token verification and management functionality
package token
import (
"fmt"
"strings"
"time"
traefikoidc "github.com/lukaszraczylo/traefikoidc"
)
// Verifier handles token verification operations
type Verifier struct {
tokenCache TokenCache
tokenBlacklist Cache
jwkCache JWKCache
limiter RateLimiter
logger Logger
}
// Cache interface for token operations
type Cache interface {
Get(key string) (interface{}, bool)
Set(key string, value interface{}, ttl time.Duration)
}
// TokenCache interface for verified token storage
type TokenCache interface {
Get(key string) (map[string]interface{}, bool)
Set(key string, claims map[string]interface{}, ttl time.Duration)
}
// JWKCache interface for key management
type JWKCache interface {
GetJWKS(providerURL string) (*traefikoidc.JWKSet, error)
}
// RateLimiter interface for request limiting
type RateLimiter interface {
Allow() bool
}
// Logger interface for logging
type Logger interface {
Debugf(format string, args ...interface{})
Errorf(format string, args ...interface{})
}
// JWT represents a parsed JWT token
type JWT struct {
Header map[string]interface{}
Claims map[string]interface{}
}
// NewVerifier creates a new token verifier
func NewVerifier(tokenCache TokenCache, tokenBlacklist Cache, jwkCache JWKCache, limiter RateLimiter, logger Logger) *Verifier {
return &Verifier{
tokenCache: tokenCache,
tokenBlacklist: tokenBlacklist,
jwkCache: jwkCache,
limiter: limiter,
logger: logger,
}
}
// VerifyToken verifies the validity of an ID token or access token
func (v *Verifier) VerifyToken(token string, clientID string, jwksURL string, issuerURL string) error {
if token == "" {
return fmt.Errorf("invalid JWT format: token is empty")
}
if strings.Count(token, ".") != 2 {
return fmt.Errorf("invalid JWT format: expected JWT with 3 parts, got %d parts", strings.Count(token, ".")+1)
}
if len(token) < 10 {
return fmt.Errorf("token too short to be valid JWT")
}
// Check blacklist
if v.tokenBlacklist != nil {
if blacklisted, exists := v.tokenBlacklist.Get(token); exists && blacklisted != nil {
return fmt.Errorf("token is blacklisted")
}
}
// Check cache first
if claims, exists := v.tokenCache.Get(token); exists && len(claims) > 0 {
return nil
}
// Rate limiting
if !v.limiter.Allow() {
return fmt.Errorf("rate limit exceeded")
}
// Parse and verify JWT
jwt, err := v.parseJWT(token)
if err != nil {
return fmt.Errorf("failed to parse JWT: %w", err)
}
if err := v.verifyJWTSignatureAndClaims(jwt, token, clientID, jwksURL, issuerURL); err != nil {
return err
}
// Cache successful verification
v.cacheVerifiedToken(token, jwt.Claims)
return nil
}
// parseJWT parses a JWT token into its components
func (v *Verifier) parseJWT(token string) (*JWT, error) {
// This would contain the actual JWT parsing logic
// For now, return a placeholder
return &JWT{
Header: make(map[string]interface{}),
Claims: make(map[string]interface{}),
}, nil
}
// verifyJWTSignatureAndClaims verifies JWT signature and claims
func (v *Verifier) verifyJWTSignatureAndClaims(jwt *JWT, token string, clientID string, jwksURL string, issuerURL string) error {
// This would contain the actual signature verification logic
// For now, return nil (placeholder)
return nil
}
// cacheVerifiedToken stores a successfully verified token
func (v *Verifier) cacheVerifiedToken(token string, claims map[string]interface{}) {
if expClaim, ok := claims["exp"].(float64); ok {
expirationTime := time.Unix(int64(expClaim), 0)
duration := time.Until(expirationTime)
if duration > 0 {
v.tokenCache.Set(token, claims, duration)
}
}
}