This commit is contained in:
2025-08-22 17:32:57 -04:00
parent 74fc72ef4a
commit d648a55c0c
18 changed files with 3687 additions and 308 deletions

View File

@ -1,6 +1,7 @@
package auth
import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
@ -9,6 +10,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"go.uber.org/zap"
"github.com/kms/api-key-service/internal/cache"
"github.com/kms/api-key-service/internal/config"
"github.com/kms/api-key-service/internal/domain"
"github.com/kms/api-key-service/internal/errors"
@ -16,15 +18,18 @@ import (
// JWTManager handles JWT token operations
type JWTManager struct {
config config.ConfigProvider
logger *zap.Logger
config config.ConfigProvider
logger *zap.Logger
cacheManager *cache.CacheManager
}
// NewJWTManager creates a new JWT manager
func NewJWTManager(config config.ConfigProvider, logger *zap.Logger) *JWTManager {
cacheManager := cache.NewCacheManager(config, logger)
return &JWTManager{
config: config,
logger: logger,
config: config,
logger: logger,
cacheManager: cacheManager,
}
}
@ -189,19 +194,45 @@ func (j *JWTManager) ExtractClaims(tokenString string) (*CustomClaims, error) {
func (j *JWTManager) RevokeToken(tokenString string) error {
j.logger.Debug("Revoking JWT token")
// Extract claims to get token ID
// Extract claims to get token ID and expiration
claims, err := j.ExtractClaims(tokenString)
if err != nil {
return err
}
// TODO: Implement token blacklisting mechanism
// This could be implemented using Redis or database storage
// For now, we'll just log the revocation
j.logger.Info("Token revoked",
// Calculate TTL for the blacklist entry (until token would naturally expire)
ttl := time.Until(claims.ExpiresAt.Time)
if ttl <= 0 {
// Token is already expired, no need to blacklist
j.logger.Debug("Token already expired, skipping blacklist",
zap.String("jti", claims.ID))
return nil
}
// Store token ID in blacklist cache
ctx := context.Background()
blacklistKey := cache.CacheKey(cache.KeyPrefixTokenRevoked, claims.ID)
// Store revocation info
revocationInfo := map[string]interface{}{
"revoked_at": time.Now().Unix(),
"user_id": claims.UserID,
"app_id": claims.AppID,
"reason": "manual_revocation",
}
if err := j.cacheManager.SetJSON(ctx, blacklistKey, revocationInfo, ttl); err != nil {
j.logger.Error("Failed to blacklist token",
zap.String("jti", claims.ID),
zap.Error(err))
return errors.NewInternalError("Failed to revoke token").WithInternal(err)
}
j.logger.Info("Token successfully revoked",
zap.String("jti", claims.ID),
zap.String("user_id", claims.UserID),
zap.String("app_id", claims.AppID))
zap.String("app_id", claims.AppID),
zap.Duration("ttl", ttl))
return nil
}
@ -216,14 +247,25 @@ func (j *JWTManager) IsTokenRevoked(tokenString string) (bool, error) {
return false, err
}
// TODO: Implement token blacklist checking
// This could be implemented using Redis or database storage
// For now, we'll assume no tokens are revoked
// Check blacklist cache
ctx := context.Background()
blacklistKey := cache.CacheKey(cache.KeyPrefixTokenRevoked, claims.ID)
exists, err := j.cacheManager.Exists(ctx, blacklistKey)
if err != nil {
j.logger.Error("Failed to check token blacklist",
zap.String("jti", claims.ID),
zap.Error(err))
// In case of cache error, we'll assume token is not revoked to avoid blocking valid requests
// This could be made configurable based on security requirements
return false, nil
}
j.logger.Debug("Token revocation check completed",
zap.String("jti", claims.ID),
zap.Bool("revoked", false))
zap.Bool("revoked", exists))
return false, nil
return exists, nil
}
// generateJTI generates a unique JWT ID