A simple password validation helper

In my previous posts, I made reference to using a separate authentication service. Normally you'd set your password complexity policy in IdentityConfig but ideally you want server-side validation on your application before the call to the authentication service.

This post contains code for a simple helper to do validation.

The Problem

Need for server-side password validation on the application before it calls the authentication service, IdentityConfig won't work since the user isn't created in the application.

The Solution

A helper to validate passwords on the application and on the authentication service. DON'T EVER TRUST YOUR OWN APIs.

The How

The Password Validation Helper
public class PasswordValidator
{
    private int RequiredLength { get; set; }
    private bool RequireLowercase { get; set; }
    private bool RequireUppercase { get; set; }
    private bool RequireDigit { get; set; }
    private bool RequireNonLetterOrDigit { get; set; }    


    public PasswordValidator(int requiredLength=8, bool requireLowercase=true, bool  requireUppercase=true, bool requireDigit=true, bool requireNonLetterOrDigit=true)
    {
        RequiredLength = requiredLength;
        RequireLowercase = requireLowercase;
        RequireUppercase = requireUppercase;
        RequireDigit = requireDigit;
        RequireNonLetterOrDigit = requireNonLetterOrDigit;
    }
    private string GetRegexStringForLowercaseLetter()
    {
        return @"(?=\S*[a-z])";
    }
    private string GetRegexStringForUppercaseLetter()
    {
        return @"(?=\S*[A-Z])";
    }
    private string GetRegexStringForDigit()
    {
        return @"(?=\S*\d)";
    }
    private string GetRegexStringForNonLetterOrDigit()
    {
        return @"(?=\S*[^\w\s])";
    }
    private string GetRegexStringForLength()
    {
        return @"\S{" + RequiredLength + @",}";
    }

    private Regex GetRegex()
    {
        var temp = "";
        if (RequireLowercase)
        {
            temp += GetRegexStringForLowercaseLetter();
        }
        if (RequireUppercase)
        {
            temp += GetRegexStringForUppercaseLetter();
        }
        if (RequireDigit)
        {
            temp += GetRegexStringForDigit();
        }
        if (RequireNonLetterOrDigit)
        {
            temp += GetRegexStringForNonLetterOrDigit();
        }
       return new Regex($@"^{temp}{GetRegexStringForLength()}$");

    }
    private bool Match(string password, string regexString)
    {
        var match = new Regex(regexString).Match(password);
        return match.Success;
    }
    private bool Valid(string password)
    {
        var regex = GetRegex();
        var match = regex.Match(password);
        return match.Success;
    }
    private string Reason(string password)
    {
        var temp = "";
        if (password.Length< RequiredLength)
        {
            temp += $"Minimum password length of {RequiredLength} is required. ";
        }
        if (RequireLowercase)
        {
            if(!Match(password, GetRegexStringForLowercaseLetter())){
                temp += $"A lowercase letter is required. ";
            }               
        }
        if (RequireUppercase)
        {
            if (!Match(password, GetRegexStringForUppercaseLetter()))
            {
                temp += $"An uppercase letter is required. ";
            }

        }
        if (RequireDigit)
        {
            if (!Match(password, GetRegexStringForDigit()))
            {
                temp += $"A digit/number is required. ";
            }

        }
        if (RequireNonLetterOrDigit)
        {
            if (!Match(password, GetRegexStringForNonLetterOrDigit()))
            {
                temp += $"A special character(non-letter or digit) is required. ";
            }             
        }
        return temp;
    }

    public PasswordValidatorResultModel Validate(string password)
    {
        return new PasswordValidatorResultModel() { Valid = Valid(password), Reason = Reason(password) };
    }
    public class PasswordValidatorResultModel{
        public string Reason { get; set; }
        public bool Valid { get; set; }
    }
}

Putting It Together

The helper, has the same properties as PasswordValidator in AspNet.Identity, with default values. You can overload the constructor for different password policies.
RequiredLength = 8
RequireLowercase = true
RequireUppercase = true
RequireDigit = true
RequireNonLetterOrDigit = true

var passwordValidator = new PasswordValidator();
var passwordValidationResult = passwordValidator.Validate(password);

The Validate function returns a object with two properties.
Valid: Boolean value of the result.
Reason: String value of why validation failed.