Asp.Net MVC password Validation with a Regular Expression

January 19, 2018    Development Asp.Net MVC .Net

Asp.Net MVC Password Validation with a Regular Expression

Originally posted on GeeksWithBlogs.net on May 26th, 2013.

I have requirement (specification) that the password has to be at least 7 characters long and contain a special character ( ~@#$ &*()-_+=) which are all the specials in the number keyboard row. I found that using the RegularExpression DataAnnotations is really slick, but there was a point that caught me for a while.

The problem is that @".([!@#$&()-+=]+).$" works in the UI, but fails in the unit test. @"[!@#$&()-+=]+" works in the test, but not in the UI (the MVC unobtrusive library wants a full match). When I removed the ‘()’ it works in both. So my solution, after losing some time, is to add the escape before the ‘-’. This works: ^.(?=.[!@#$%^&\(\)_\-+=]).$ (thanks to my co-worker Jason).

Here comes the code:

The Model for the MVC binding. Focus on the RegEx for NewPassword:

public class PasswordResetModel
{
    /// <summary>
    /// Gets or sets the new password.
    /// It must be at least 7 characters and have at least one special character.
    /// </summary>
    /// <value>
    /// The new password.
    /// </value>
    [Required(ErrorMessageResourceType = typeof(AuthenticationModelsResource),
        ErrorMessageResourceName = "NewPasswordRequiredMessage")]
    [StringLength(100, ErrorMessageResourceType = typeof(AuthenticationModelsResource),
        ErrorMessageResourceName = "PasswordTooShortErrorMessage", MinimumLength = 7)]
    [RegularExpression(@"^.*(?=.*[!@#$%^&*\\(\\)_\\-+=\]).*$",
        ErrorMessageResourceType = typeof(AuthenticationModelsResource),
        ErrorMessageResourceName = "PasswordNotStrongEnoughMessage")]
    [DataType(DataType.Password)]
    [Display(ResourceType = typeof(AuthenticationModelsResource), Name = "NewPassword"]
    public string NewPassword { get; set; }

    /// <summary>
    /// Gets or sets the confirm password.
    /// </summary>
    /// <value>
    /// The confirm password.
    /// </value>
    [DataType(DataType.Password)]
    [Display(ResourceType = typeof(AuthenticationModelsResource), Name = "ConfirmNewPassword")]
    [Required(ErrorMessageResourceType = typeof(AuthenticationModelsResource),
       ErrorMessageResourceName = "ConfirmNewPasswordRequiredMessage")]
    [Compare("NewPassword", ErrorMessageResourceType = typeof(AuthenticationModelsResource),
        ErrorMessageResourceName = "PasswordCompareErrorMessage")]
    public string ConfirmPassword { get; set; }
}

The Unit test:

[TestMethod,TestCategory("HomeController")]
public void It_Should_Validate_Password_Doesnt_Have_A_Special_Character()
{
    PasswordResetViewModel vm = new PasswordResetViewModel("match", 10)
                            {
                                PasswordResetModel =
                                {
                                    NewPassword = "test1122",
                                    ConfirmPassword = "test1122"
                                }
                            };
    ((ChangePasswordModel)vm.PasswordResetModel).OldPassword = "test1";
    this.Controller.PasswordReset(vm);

    // assert
    ValidateViewModel(vm.PasswordResetModel, this.Controller);
    Should.BooleanAssertionExtensions.ShouldBeTrue(this.Controller.ModelState.Values.Last()
        .Errors.Any(e => e.ErrorMessage == AuthenticationModelsResource.PasswordNotStrongEnoughMessage));
}

The ValidateViewModel method:

protected static void ValidateViewModel<TVm, TC>(TVm viewModelToValidate, TC controller) where TC : Controller
{
    var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => viewModelToValidate, viewModelToValidate.GetType());
    var validator = ModelValidator.GetModelValidator(modelMetadata, controller.ControllerContext);
    var result = validator.Validate(viewModelToValidate);
    foreach (var validationResult in result)
    {
        if (!string.IsNullOrWhiteSpace(validationResult.MemberName))
        {
            controller.ModelState.AddModelError(validationResult.MemberName, validationResult.Message);
        }
        else
        {
            controller.ModelState.AddModelError(string.Empty, validationResult.Message);
        }
    }
}

The MVC Unobtrusive Validation library wants a complete match (from the source code):

$jQval.addMethod("regex", function (value, element, params) {
    var match;
    if (this.optional(element)) {
        return true;
    }

    match = new RegExp(params).exec(value);
    return (match && (match.index === 0) && (match\[0\].length === value.length));
});


Watch the Story for Good News
I gladly accept BTC Lightning Network tips at [email protected]

Please consider using Brave and adding me to your BAT payment ledger. Then you won't have to see ads! (when I get to $100 in Google Ads for a payout, I pledge to turn off ads)

Use Brave

Also check out my Resources Page for referrals that would help me.


Swan logo
Use Swan Bitcoin to onramp with low fees and automatic daily cost averaging and get $10 in BTC when you sign up.