-
This commit is contained in:
382
test/jwt_test.go
Normal file
382
test/jwt_test.go
Normal file
@ -0,0 +1,382 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user