package auth import ( "crypto/rand" "encoding/hex" "errors" "strconv" "strings" "unicode" ) // CheckPassword reports whether a password satisfies the configured policy, // returning an error phrased for the end user (the caller can surface it // verbatim on a signup or change-password form). func (a *Authenticator) CheckPassword(password string) error { return CheckPasswordPolicy(a.cfg.Password, password) } // CheckPasswordPolicy is CheckPassword against an explicit policy, for callers // that validate a password without an Authenticator in hand. func CheckPasswordPolicy(policy PasswordPolicy, password string) error { if strings.TrimSpace(password) == "" { return errors.New("Password cannot be blank.") } if len(password) < policy.MinLength { return errors.New("Password must be at least " + strconv.Itoa(policy.MinLength) + " characters long.") } var uppercase, lowercase, numbers, symbols int for _, r := range password { switch { case unicode.IsUpper(r): uppercase++ case unicode.IsLower(r): lowercase++ case unicode.IsNumber(r): numbers++ default: symbols++ } } switch { case uppercase < policy.RequiredUppercase: return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredUppercase) + " uppercase character(s).") case lowercase < policy.RequiredLowercase: return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredLowercase) + " lowercase character(s).") case numbers < policy.RequiredNumbers: return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredNumbers) + " number(s).") case symbols < policy.RequiredSymbols: return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredSymbols) + " symbol(s).") } return nil } // GeneratePasswordResetToken returns a 32-byte crypto-random token, hex-encoded // (64 characters). The app stores it against the user with an expiry and mails // the same string out. func GeneratePasswordResetToken() (string, error) { b := make([]byte, 32) if _, err := rand.Read(b); err != nil { return "", err } return hex.EncodeToString(b), nil }