- Add TokenType enum and include in Claims struct - GenerateRefreshToken now creates tokens with TokenRefresh type - AuthRequired middleware rejects refresh tokens - AuthService.Refresh validates token type - Tests verify type validation
126 lines
3.1 KiB
Go
126 lines
3.1 KiB
Go
package auth
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestGenerateAccessToken(t *testing.T) {
|
|
secret := []byte("test-secret")
|
|
token, err := GenerateAccessToken("user-1", secret, 15*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("GenerateAccessToken = %v", err)
|
|
}
|
|
if token == "" {
|
|
t.Fatal("token is empty")
|
|
}
|
|
if !strings.Contains(token, ".") {
|
|
t.Fatal("token does not look like a JWT")
|
|
}
|
|
}
|
|
|
|
func TestParseTokenValid(t *testing.T) {
|
|
secret := []byte("test-secret")
|
|
token, err := GenerateAccessToken("user-1", secret, 15*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("GenerateAccessToken = %v", err)
|
|
}
|
|
|
|
claims, err := ParseToken(token, secret)
|
|
if err != nil {
|
|
t.Fatalf("ParseToken = %v", err)
|
|
}
|
|
if claims.UserID != "user-1" {
|
|
t.Errorf("UserID = %q, want %q", claims.UserID, "user-1")
|
|
}
|
|
if claims.Type != TokenAccess {
|
|
t.Errorf("Type = %q, want %q", claims.Type, TokenAccess)
|
|
}
|
|
}
|
|
|
|
func TestParseTokenWrongSecret(t *testing.T) {
|
|
secret := []byte("test-secret")
|
|
token, err := GenerateAccessToken("user-1", secret, 15*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("GenerateAccessToken = %v", err)
|
|
}
|
|
|
|
_, err = ParseToken(token, []byte("wrong-secret"))
|
|
if err == nil {
|
|
t.Fatal("expected error for wrong secret, got nil")
|
|
}
|
|
}
|
|
|
|
func TestParseTokenExpired(t *testing.T) {
|
|
secret := []byte("test-secret")
|
|
token, err := GenerateAccessToken("user-1", secret, -1*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("GenerateAccessToken = %v", err)
|
|
}
|
|
|
|
_, err = ParseToken(token, secret)
|
|
if err == nil {
|
|
t.Fatal("expected error for expired token, got nil")
|
|
}
|
|
}
|
|
|
|
func TestParseTokenInvalidFormat(t *testing.T) {
|
|
_, err := ParseToken("not-a-jwt", []byte("secret"))
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid format, got nil")
|
|
}
|
|
}
|
|
|
|
func TestGenerateRefreshToken(t *testing.T) {
|
|
secret := []byte("test-secret")
|
|
token, err := GenerateRefreshToken("user-1", secret, 7*24*time.Hour)
|
|
if err != nil {
|
|
t.Fatalf("GenerateRefreshToken = %v", err)
|
|
}
|
|
if token == "" {
|
|
t.Fatal("token is empty")
|
|
}
|
|
if !strings.Contains(token, ".") {
|
|
t.Fatal("token does not look like a JWT")
|
|
}
|
|
|
|
claims, err := ParseToken(token, secret)
|
|
if err != nil {
|
|
t.Fatalf("ParseToken = %v", err)
|
|
}
|
|
if claims.Type != TokenRefresh {
|
|
t.Errorf("Type = %q, want %q", claims.Type, TokenRefresh)
|
|
}
|
|
}
|
|
|
|
func TestTokenUserIDCarried(t *testing.T) {
|
|
secret := []byte("test-secret")
|
|
token, _ := GenerateAccessToken("alice-42", secret, 15*time.Minute)
|
|
claims, err := ParseToken(token, secret)
|
|
if err != nil {
|
|
t.Fatalf("ParseToken = %v", err)
|
|
}
|
|
if claims.UserID != "alice-42" {
|
|
t.Errorf("UserID = %q, want %q", claims.UserID, "alice-42")
|
|
}
|
|
}
|
|
|
|
func TestRefreshTokenRejectedByMiddleware(t *testing.T) {
|
|
secret := []byte("test-secret")
|
|
token, err := GenerateRefreshToken("user-1", secret, 7*24*time.Hour)
|
|
if err != nil {
|
|
t.Fatalf("GenerateRefreshToken = %v", err)
|
|
}
|
|
|
|
// Simulate what the middleware does: parse + check type
|
|
claims, err := ParseToken(token, secret)
|
|
if err != nil {
|
|
t.Fatalf("ParseToken = %v", err)
|
|
}
|
|
if claims.Type != TokenRefresh {
|
|
t.Fatalf("expected refresh token type, got %q", claims.Type)
|
|
}
|
|
// The actual middleware rejection is tested in middleware/auth_test.go
|
|
}
|