package auth import ( "crypto/subtle" "errors" "time" "github.com/golang-jwt/jwt/v5" "golang.org/x/crypto/bcrypt" ) var ErrToken = errors.New("invalid token") func HashPassword(plain string) (string, error) { b, err := bcrypt.GenerateFromPassword([]byte(plain), 12) return string(b), err } func CheckPassword(hash, plain string) bool { return bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)) == nil } type Claims struct { UID int64 `json:"uid"` Role string `json:"role"` jwt.RegisteredClaims } func Sign(secret []byte, uid int64, role string) (string, error) { return SignWithTTL(secret, uid, role, 72*time.Hour) } func SignWithTTL(secret []byte, uid int64, role string, ttl time.Duration) (string, error) { t := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{ UID: uid, Role: role, RegisteredClaims: jwt.RegisteredClaims{ExpiresAt: jwt.NewNumericDate(time.Now().Add(ttl))}, }) return t.SignedString(secret) } func Parse(secret []byte, token string) (*Claims, error) { c := &Claims{} parsed, err := jwt.ParseWithClaims(token, c, func(t *jwt.Token) (any, error) { m, ok := t.Method.(*jwt.SigningMethodHMAC) if !ok || subtle.ConstantTimeCompare([]byte(m.Alg()), []byte("HS256")) != 1 { return nil, ErrToken } return secret, nil }, jwt.WithValidMethods([]string{"HS256"})) if err != nil || !parsed.Valid { return nil, ErrToken } return c, nil }