package auth import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "errors" "fmt" "strconv" "strings" "time" "golang.org/x/crypto/bcrypt" ) // HashPassword returns a bcrypt hash for the given plain text password. func HashPassword(password string) (string, error) { if password == "" { return "", errors.New("empty password") } b, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", err } return string(b), nil } // CheckPassword verifies a bcrypt hash against the given plain text password. func CheckPassword(hash, password string) bool { if hash == "" || password == "" { return false } return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil } // MakeSessionToken builds a simple HMAC-signed token: username|expUnix|sigHex func MakeSessionToken(username string, ttl time.Duration, secret []byte) (string, time.Time) { if ttl <= 0 { ttl = 24 * time.Hour } exp := time.Now().Add(ttl).UTC() payload := username + "|" + strconv.FormatInt(exp.Unix(), 10) mac := hmac.New(sha256.New, secret) mac.Write([]byte(payload)) sig := hex.EncodeToString(mac.Sum(nil)) return payload + "|" + sig, exp } // ParseSessionToken validates token and returns username if valid. func ParseSessionToken(token string, secret []byte) (string, bool) { parts := strings.Split(token, "|") if len(parts) != 3 { return "", false } username := parts[0] expStr := parts[1] sigHex := parts[2] // recompute payload := fmt.Sprintf("%s|%s", username, expStr) mac := hmac.New(sha256.New, secret) mac.Write([]byte(payload)) if hex.EncodeToString(mac.Sum(nil)) != sigHex { return "", false } // expiry check expUnix, err := strconv.ParseInt(expStr, 10, 64) if err != nil { return "", false } if time.Now().UTC().After(time.Unix(expUnix, 0)) { return "", false } return username, true }