package auth import ( "fmt" "time" "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" ) // Claims holds the JWT payload. type Claims struct { Username string `json:"username"` Role string `json:"role"` jwt.RegisteredClaims } // GenerateJWT creates a JWT token valid for 24h. func GenerateJWT(secret string, userID int64, username, role string) (string, error) { claims := Claims{ Username: username, Role: role, RegisteredClaims: jwt.RegisteredClaims{ Subject: fmt.Sprintf("%d", userID), ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) } // ParseJWT verifies the token and returns claims. func ParseJWT(secret, tokenStr string) (*Claims, error) { token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (interface{}, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } return []byte(secret), nil }) if err != nil { return nil, err } claims, ok := token.Claims.(*Claims) if !ok || !token.Valid { return nil, fmt.Errorf("invalid token") } return claims, nil } // VerifyPassword compares a plain-text password against a bcrypt hash. func VerifyPassword(hash, password string) error { return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) } // HashPassword generates a bcrypt hash from the given password. func HashPassword(password string) (string, error) { b, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) return string(b), err }