persona-community-2/pkg/auth/password.go
jordan cb3d4d5786
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
ci/woodpecker/manual/woodpecker Pipeline was successful
Initialize project from skeleton template
2026-02-23 10:53:55 +00:00

72 lines
2.0 KiB
Go

package auth
import (
"errors"
"unicode"
"golang.org/x/crypto/bcrypt"
)
const (
// BcryptCost is the bcrypt hashing cost. 12 balances security and performance.
BcryptCost = 12
// MinPasswordLength is the minimum allowed password length.
MinPasswordLength = 8
// MaxPasswordLength is the maximum allowed password length (bcrypt limit).
MaxPasswordLength = 72
)
var (
// ErrPasswordTooShort is returned when a password is below the minimum length.
ErrPasswordTooShort = errors.New("password must be at least 8 characters")
// ErrPasswordTooLong is returned when a password exceeds the bcrypt limit.
ErrPasswordTooLong = errors.New("password must be at most 72 characters")
// ErrPasswordWeak is returned when a password lacks required character types.
ErrPasswordWeak = errors.New("password must contain at least one uppercase letter, one lowercase letter, and one number")
)
// HashPassword hashes a plaintext password using bcrypt.
func HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
if err != nil {
return "", err
}
return string(hash), nil
}
// CheckPassword compares a plaintext password against a bcrypt hash.
// Returns true if the password matches.
func CheckPassword(password, hash string) bool {
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
}
// ValidatePasswordStrength checks that a password meets minimum complexity requirements.
// Returns nil if the password is acceptable.
func ValidatePasswordStrength(password string) error {
if len(password) < MinPasswordLength {
return ErrPasswordTooShort
}
if len(password) > MaxPasswordLength {
return ErrPasswordTooLong
}
var hasUpper, hasLower, hasDigit bool
for _, r := range password {
switch {
case unicode.IsUpper(r):
hasUpper = true
case unicode.IsLower(r):
hasLower = true
case unicode.IsDigit(r):
hasDigit = true
}
}
if !hasUpper || !hasLower || !hasDigit {
return ErrPasswordWeak
}
return nil
}