394 lines
11 KiB
Go
394 lines
11 KiB
Go
package helper
|
|
|
|
import (
|
|
"authentication/models"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
func TestExtractEmailFromToken_ValidAccessToken(t *testing.T) {
|
|
// Set up test environment
|
|
secretKey := "test-secret-key-123"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
email := "test@example.com"
|
|
|
|
// Create valid AccessToken
|
|
claims := &models.AccessToken{
|
|
Email: email,
|
|
SessionID: "session123",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, err := token.SignedString([]byte(secretKey))
|
|
if err != nil {
|
|
t.Fatalf("Failed to sign token: %v", err)
|
|
}
|
|
|
|
// Test extraction
|
|
extractedEmail, err := ExtractEmailFromToken(tokenString)
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
if extractedEmail != email {
|
|
t.Errorf("Expected email '%s', got '%s'", email, extractedEmail)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_ValidMapClaims(t *testing.T) {
|
|
secretKey := "test-secret-key-456"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
email := "mapuser@example.com"
|
|
|
|
// Create token with MapClaims
|
|
claims := jwt.MapClaims{
|
|
"email": email,
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Unix(),
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, err := token.SignedString([]byte(secretKey))
|
|
if err != nil {
|
|
t.Fatalf("Failed to sign token: %v", err)
|
|
}
|
|
|
|
// Test extraction
|
|
extractedEmail, err := ExtractEmailFromToken(tokenString)
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
if extractedEmail != email {
|
|
t.Errorf("Expected email '%s', got '%s'", email, extractedEmail)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_BearerPrefix(t *testing.T) {
|
|
secretKey := "test-secret-bearer"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
email := "bearer@example.com"
|
|
|
|
claims := &models.AccessToken{
|
|
Email: email,
|
|
SessionID: "session789",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, err := token.SignedString([]byte(secretKey))
|
|
if err != nil {
|
|
t.Fatalf("Failed to sign token: %v", err)
|
|
}
|
|
|
|
// Test with Bearer prefix
|
|
bearerToken := "Bearer " + tokenString
|
|
extractedEmail, err := ExtractEmailFromToken(bearerToken)
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
if extractedEmail != email {
|
|
t.Errorf("Expected email '%s', got '%s'", email, extractedEmail)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_EmptyToken(t *testing.T) {
|
|
os.Setenv("JWT_SECRET_KEY", "test-key")
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
testCases := []string{"", "null", "undefined"}
|
|
|
|
for _, tokenString := range testCases {
|
|
_, err := ExtractEmailFromToken(tokenString)
|
|
if err == nil {
|
|
t.Errorf("Expected error for token '%s', got nil", tokenString)
|
|
}
|
|
|
|
if !strings.Contains(err.Error(), "no valid token provided") {
|
|
t.Errorf("Expected 'no valid token provided' error, got: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_InvalidSignature(t *testing.T) {
|
|
os.Setenv("JWT_SECRET_KEY", "correct-secret")
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
// Create token with wrong secret
|
|
wrongSecret := "wrong-secret"
|
|
claims := &models.AccessToken{
|
|
Email: "test@example.com",
|
|
SessionID: "session123",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(wrongSecret))
|
|
|
|
// Try to extract with different secret
|
|
_, err := ExtractEmailFromToken(tokenString)
|
|
if err == nil {
|
|
t.Error("Expected error for invalid signature")
|
|
}
|
|
|
|
if !strings.Contains(err.Error(), "invalid token signature") {
|
|
t.Errorf("Expected 'invalid token signature' error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_ExpiredToken(t *testing.T) {
|
|
secretKey := "test-expired-key"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
// Create expired token
|
|
claims := &models.AccessToken{
|
|
Email: "expired@example.com",
|
|
SessionID: "session999",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour)), // Expired 1 hour ago
|
|
IssuedAt: jwt.NewNumericDate(time.Now().Add(-2 * time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(secretKey))
|
|
|
|
// Note: The ExtractEmailFromToken function doesn't validate expiration,
|
|
// it relies on ParseWithClaims which may or may not enforce expiration
|
|
// depending on jwt library version. We'll just verify it can extract the email.
|
|
extractedEmail, _ := ExtractEmailFromToken(tokenString)
|
|
if extractedEmail != "expired@example.com" {
|
|
t.Logf("Extracted email from expired token: %s", extractedEmail)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_NoEmailInClaims(t *testing.T) {
|
|
secretKey := "test-no-email"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
// Create token without email
|
|
claims := jwt.MapClaims{
|
|
"user_id": "user123",
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(secretKey))
|
|
|
|
_, err := ExtractEmailFromToken(tokenString)
|
|
if err == nil {
|
|
t.Error("Expected error for token without email")
|
|
}
|
|
|
|
if !strings.Contains(err.Error(), "email not found in token") {
|
|
t.Errorf("Expected 'email not found in token' error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_InvalidEmailFormat(t *testing.T) {
|
|
secretKey := "test-invalid-email"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
// Create token with invalid email (no @ symbol)
|
|
claims := &models.AccessToken{
|
|
Email: "notanemail",
|
|
SessionID: "session123",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(secretKey))
|
|
|
|
_, err := ExtractEmailFromToken(tokenString)
|
|
if err == nil {
|
|
t.Error("Expected error for invalid email format")
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_NoSecretKey(t *testing.T) {
|
|
// Ensure no secret key is set
|
|
os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
claims := &models.AccessToken{
|
|
Email: "test@example.com",
|
|
SessionID: "session123",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte("any-key"))
|
|
|
|
_, err := ExtractEmailFromToken(tokenString)
|
|
if err == nil {
|
|
t.Error("Expected error when secret key not set")
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_WrongSigningMethod(t *testing.T) {
|
|
secretKey := "test-wrong-method"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
// Try to create token with non-HMAC signing method (would need RSA keys in real scenario)
|
|
// For simplicity, we'll create a malformed token string
|
|
malformedToken := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifQ.invalid"
|
|
|
|
_, err := ExtractEmailFromToken(malformedToken)
|
|
if err == nil {
|
|
t.Error("Expected error for wrong signing method")
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_MalformedToken(t *testing.T) {
|
|
os.Setenv("JWT_SECRET_KEY", "test-key")
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
malformedTokens := []string{
|
|
"not.a.token",
|
|
"invalid",
|
|
"Bearer invalid",
|
|
"...",
|
|
"a.b",
|
|
}
|
|
|
|
for _, tokenString := range malformedTokens {
|
|
_, err := ExtractEmailFromToken(tokenString)
|
|
if err == nil {
|
|
t.Errorf("Expected error for malformed token '%s'", tokenString)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_MultipleAtSymbols(t *testing.T) {
|
|
secretKey := "test-multiple-at"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
email := "user@sub@example.com"
|
|
|
|
claims := &models.AccessToken{
|
|
Email: email,
|
|
SessionID: "session123",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(secretKey))
|
|
|
|
// Should extract successfully (just checks for @ presence)
|
|
extractedEmail, err := ExtractEmailFromToken(tokenString)
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
if extractedEmail != email {
|
|
t.Errorf("Expected email '%s', got '%s'", email, extractedEmail)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_WhitespaceEmail(t *testing.T) {
|
|
secretKey := "test-whitespace"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
// Email with spaces (should still work if it has @)
|
|
email := " user@example.com "
|
|
|
|
claims := jwt.MapClaims{
|
|
"email": email,
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(secretKey))
|
|
|
|
extractedEmail, err := ExtractEmailFromToken(tokenString)
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
if extractedEmail != email {
|
|
t.Errorf("Expected email '%s', got '%s'", email, extractedEmail)
|
|
}
|
|
}
|
|
|
|
func TestExtractEmailFromToken_CaseInsensitiveBearer(t *testing.T) {
|
|
secretKey := "test-case-bearer"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
email := "case@example.com"
|
|
|
|
claims := &models.AccessToken{
|
|
Email: email,
|
|
SessionID: "session123",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(secretKey))
|
|
|
|
// Test with standard "Bearer " prefix
|
|
bearerToken := "Bearer " + tokenString
|
|
extractedEmail, err := ExtractEmailFromToken(bearerToken)
|
|
if err != nil {
|
|
t.Errorf("Expected no error for Bearer prefix, got: %v", err)
|
|
}
|
|
|
|
if extractedEmail != email {
|
|
t.Errorf("Expected email '%s', got '%s'", email, extractedEmail)
|
|
}
|
|
}
|
|
|
|
func BenchmarkExtractEmailFromToken(b *testing.B) {
|
|
secretKey := "benchmark-secret"
|
|
os.Setenv("JWT_SECRET_KEY", secretKey)
|
|
defer os.Unsetenv("JWT_SECRET_KEY")
|
|
|
|
claims := &models.AccessToken{
|
|
Email: "bench@example.com",
|
|
SessionID: "sessionBench",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
|
},
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
tokenString, _ := token.SignedString([]byte(secretKey))
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
ExtractEmailFromToken(tokenString)
|
|
}
|
|
}
|