package auth import ( "fmt" "time" "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" ) // JWTService handles JWT token generation and validation type JWTService struct { secret []byte expiration time.Duration } // NewJWTService creates a new JWTService func NewJWTService(secret string, expiration time.Duration) *JWTService { return &JWTService{ secret: []byte(secret), expiration: expiration, } } // Claims represents JWT claims type Claims struct { UserID string `json:"user_id"` Email string `json:"email"` Role string `json:"role"` jwt.RegisteredClaims } // GenerateToken generates a JWT token for a user func (s *JWTService) GenerateToken(userID uuid.UUID, email string) (string, time.Time, error) { return s.GenerateTokenWithRole(userID, email, "user") } // GenerateTokenWithRole generates a JWT token for a user with a specific role func (s *JWTService) GenerateTokenWithRole(userID uuid.UUID, email, role string) (string, time.Time, error) { expiresAt := time.Now().Add(s.expiration) // Default role to "user" if not specified if role == "" { role = "user" } claims := &Claims{ UserID: userID.String(), Email: email, Role: role, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(expiresAt), IssuedAt: jwt.NewNumericDate(time.Now()), NotBefore: jwt.NewNumericDate(time.Now()), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString(s.secret) if err != nil { return "", time.Time{}, fmt.Errorf("failed to sign token: %w", err) } return tokenString, expiresAt, nil } // ValidateToken validates a JWT token and returns the claims func (s *JWTService) ValidateToken(tokenString string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (any, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } return s.secret, nil }) if err != nil { return nil, fmt.Errorf("failed to parse token: %w", err) } claims, ok := token.Claims.(*Claims) if !ok || !token.Valid { return nil, fmt.Errorf("invalid token") } return claims, nil }