Files
skybridge/test/jwt_test.go
2025-08-22 17:32:57 -04:00

383 lines
11 KiB
Go

package test
import (
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/kms/api-key-service/internal/auth"
"github.com/kms/api-key-service/internal/config"
"github.com/kms/api-key-service/internal/domain"
)
func TestJWTManager_GenerateToken(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read", "write"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
Claims: map[string]string{
"department": "engineering",
"role": "developer",
},
}
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
assert.NotEmpty(t, tokenString)
// Verify token structure (should have 3 parts separated by dots)
parts := len(tokenString)
assert.Greater(t, parts, 100) // JWT tokens are typically longer than 100 chars
}
func TestJWTManager_ValidateToken(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read", "write"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
Claims: map[string]string{
"department": "engineering",
},
}
// Generate token
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Validate token
claims, err := jwtManager.ValidateToken(tokenString)
require.NoError(t, err)
assert.Equal(t, userToken.UserID, claims.UserID)
assert.Equal(t, userToken.AppID, claims.AppID)
assert.Equal(t, userToken.Permissions, claims.Permissions)
assert.Equal(t, userToken.TokenType, claims.TokenType)
assert.Equal(t, userToken.Claims, claims.Claims)
}
func TestJWTManager_ValidateToken_InvalidToken(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
// Test with invalid token
_, err := jwtManager.ValidateToken("invalid.token.here")
assert.Error(t, err)
assert.Contains(t, err.Error(), "Invalid token")
}
func TestJWTManager_ValidateToken_ExpiredToken(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read"},
IssuedAt: time.Now().Add(-2 * time.Hour),
ExpiresAt: time.Now().Add(-1 * time.Hour), // Expired 1 hour ago
MaxValidAt: time.Now().Add(-30 * time.Minute), // Max valid also expired
TokenType: domain.TokenTypeUser,
}
// Generate token (this should work even with past dates)
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Validate token (this should fail due to expiration)
_, err = jwtManager.ValidateToken(tokenString)
assert.Error(t, err)
// The error could be either JWT expiration or our custom max valid check
assert.True(t,
strings.Contains(err.Error(), "expired beyond maximum validity") ||
strings.Contains(err.Error(), "token is expired"),
"Expected expiration error, got: %s", err.Error())
}
func TestJWTManager_RefreshToken(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read", "write"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
}
// Generate original token
originalToken, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Refresh token with new expiration
newExpiration := time.Now().Add(2 * time.Hour)
refreshedToken, err := jwtManager.RefreshToken(originalToken, newExpiration)
require.NoError(t, err)
assert.NotEmpty(t, refreshedToken)
assert.NotEqual(t, originalToken, refreshedToken)
// Validate refreshed token
claims, err := jwtManager.ValidateToken(refreshedToken)
require.NoError(t, err)
assert.Equal(t, userToken.UserID, claims.UserID)
assert.Equal(t, userToken.AppID, claims.AppID)
}
func TestJWTManager_RefreshToken_ExpiredMaxValid(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read"},
IssuedAt: time.Now().Add(-2 * time.Hour),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(-30 * time.Minute), // Max valid expired
TokenType: domain.TokenTypeUser,
}
// Generate token
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Try to refresh (should fail due to max valid expiration)
newExpiration := time.Now().Add(2 * time.Hour)
_, err = jwtManager.RefreshToken(tokenString, newExpiration)
assert.Error(t, err)
assert.Contains(t, err.Error(), "expired beyond maximum validity")
}
func TestJWTManager_ExtractClaims(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read", "write"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(-1 * time.Hour), // Expired token
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
}
// Generate expired token
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Extract claims (should work even for expired tokens)
claims, err := jwtManager.ExtractClaims(tokenString)
require.NoError(t, err)
assert.Equal(t, userToken.UserID, claims.UserID)
assert.Equal(t, userToken.AppID, claims.AppID)
assert.Equal(t, userToken.Permissions, claims.Permissions)
}
func TestJWTManager_RevokeToken(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
}
// Generate token
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Revoke token
err = jwtManager.RevokeToken(tokenString)
assert.NoError(t, err)
// Check if token is revoked
revoked, err := jwtManager.IsTokenRevoked(tokenString)
assert.NoError(t, err)
assert.True(t, revoked)
}
func TestJWTManager_RevokeToken_AlreadyExpired(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read"},
IssuedAt: time.Now().Add(-2 * time.Hour),
ExpiresAt: time.Now().Add(-1 * time.Hour), // Already expired
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
}
// Generate expired token
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Revoke expired token (should succeed but not add to blacklist)
err = jwtManager.RevokeToken(tokenString)
assert.NoError(t, err)
// Check if token is revoked (should be false since it was already expired)
revoked, err := jwtManager.IsTokenRevoked(tokenString)
assert.NoError(t, err)
assert.False(t, revoked)
}
func TestJWTManager_IsTokenRevoked_NotRevoked(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
}
// Generate token
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Check if token is revoked (should be false)
revoked, err := jwtManager.IsTokenRevoked(tokenString)
assert.NoError(t, err)
assert.False(t, revoked)
}
func TestJWTManager_GetTokenInfo(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read", "write"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
Claims: map[string]string{
"department": "engineering",
},
}
// Generate token
tokenString, err := jwtManager.GenerateToken(userToken)
require.NoError(t, err)
// Get token info
info := jwtManager.GetTokenInfo(tokenString)
assert.Equal(t, userToken.UserID, info["user_id"])
assert.Equal(t, userToken.AppID, info["app_id"])
assert.Equal(t, userToken.Permissions, info["permissions"])
assert.Equal(t, userToken.TokenType, info["token_type"])
assert.NotNil(t, info["issued_at"])
assert.NotNil(t, info["expires_at"])
assert.NotNil(t, info["max_valid_at"])
assert.NotNil(t, info["jti"])
}
func TestJWTManager_GetTokenInfo_InvalidToken(t *testing.T) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
// Get info for invalid token
info := jwtManager.GetTokenInfo("invalid.token.here")
assert.Contains(t, info["error"], "Invalid token format")
}
// Benchmark tests
func BenchmarkJWTManager_GenerateToken(b *testing.B) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read", "write"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := jwtManager.GenerateToken(userToken)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkJWTManager_ValidateToken(b *testing.B) {
cfg := config.NewConfig()
logger := zap.NewNop()
jwtManager := auth.NewJWTManager(cfg, logger)
userToken := &domain.UserToken{
UserID: "test-user-123",
AppID: "test-app-456",
Permissions: []string{"read", "write"},
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(1 * time.Hour),
MaxValidAt: time.Now().Add(24 * time.Hour),
TokenType: domain.TokenTypeUser,
}
tokenString, err := jwtManager.GenerateToken(userToken)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := jwtManager.ValidateToken(tokenString)
if err != nil {
b.Fatal(err)
}
}
}