package auth import ( "errors" "time" "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" ) var ( ErrInvalidToken = errors.New("invalid token") ErrExpiredToken = errors.New("token has expired") ) type Claims struct { UserID uint `json:"userId"` Username string `json:"username"` Role string `json:"role"` jwt.RegisteredClaims } type JWTManager struct { secretKey []byte issuer string } func NewJWTManager(secretKey, issuer string) *JWTManager { return &JWTManager{ secretKey: []byte(secretKey), issuer: issuer, } } func (j *JWTManager) GenerateToken(userID uint, username, role string, duration time.Duration) (string, error) { claims := &Claims{ UserID: userID, Username: username, Role: role, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(duration)), IssuedAt: jwt.NewNumericDate(time.Now()), Issuer: j.issuer, }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(j.secretKey) } func (j *JWTManager) ValidateToken(tokenString string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, ErrInvalidToken } return j.secretKey, nil }) if err != nil { if errors.Is(err, jwt.ErrTokenExpired) { return nil, ErrExpiredToken } return nil, ErrInvalidToken } claims, ok := token.Claims.(*Claims) if !ok || !token.Valid { return nil, ErrInvalidToken } return claims, nil } func HashPassword(password string) (string, error) { hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", err } return string(hash), nil } func CheckPassword(password, hash string) bool { err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) return err == nil }