Pulse/pkg/auth/password.go
2026-03-18 16:06:30 +00:00

67 lines
1.5 KiB
Go

package auth
import (
"fmt"
"os"
"strconv"
"strings"
"golang.org/x/crypto/bcrypt"
)
const (
// BcryptCost is the cost factor for bcrypt hashing
// Higher values are more secure but slower
BcryptCost = 12
// MinPasswordLength is the minimum required password length
MinPasswordLength = 12
)
// HashPassword generates a bcrypt hash from a plain text password
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), hashPasswordCost())
if err != nil {
return "", err
}
return string(bytes), nil
}
// CheckPasswordHash compares a plain text password with a hash
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// ValidatePasswordComplexity checks if a password meets complexity requirements
func ValidatePasswordComplexity(password string) error {
if len(password) < MinPasswordLength {
return fmt.Errorf("password must be at least %d characters long", MinPasswordLength)
}
// Let users choose their own passwords beyond length.
// No character type requirements.
return nil
}
func hashPasswordCost() int {
if !runningUnderGoTest() {
return BcryptCost
}
raw := strings.TrimSpace(os.Getenv("PULSE_TEST_BCRYPT_COST"))
if raw == "" {
return BcryptCost
}
cost, err := strconv.Atoi(raw)
if err != nil || cost < bcrypt.MinCost || cost > bcrypt.MaxCost {
return BcryptCost
}
return cost
}
func runningUnderGoTest() bool {
return strings.HasSuffix(os.Args[0], ".test")
}