ASP.NET MVC3 - Password Strength Indicator Using JQuery and XML

Introduction

The purpose of this article is to demonstrate on how to integrate Password Strength Indicator using jQuery and XML plug-in with ASP.NET MVC 3 Razor. In this tutorial, we will include the plug-in into the Register Account and Change Password views.

Getting Started

The following shows how to create the sample application. In Visual Studio 2010:

  1. Create a new project
  2. Get the latest version of the plug-in and copy the jquery.password-strength.js into the Scripts folder
  3. Copy the XML folder into the project root or place it into Content folder
  4. Create the PasswordSetting class under the Models folder
  5. The files that we're going to modify are AccountController.cs, AccountModels.cs, Register.cshtml and ChangePassword.cshtml
  6. Please note that the absolute link to the XML file in the jquery.password-strength.js is hardcoded to localhost. Make sure you modify it accordingly before deploying it to production.

Figure 1

solution.png
 

Changes in Account Model

In the RegisterModel class, remove the StringLength attribute from the Password property because the length will be checked by the plug-in. If we leave it there, it will display both the error message and the password strength indicator bar until we hit the six characters.

Listing 1
 

public class RegisterModel

 {

     [Required]

     // [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]

     [DataType(DataType.Password)]

     [Display(Name = "Password")]

     public string Password { get; set; }

 } 

Changes in Account Controller

In this version, we will use LINQ to read the XML file. The GetPasswordRegex() method in this controller is responsible for retrieving the password information from the XML file and storing it in the PasswordSetting object. Then it will use the information to put together the password strength regular expression. For this demo, the path to the XML file is hardcoded; you can place the path to the XML file in the web.config.

Listing 2
 

internal string GetPasswordRegex()

 {

      XDocument xmlDoc = XDocument.Load(Request.MapPath("~/xml/PasswordPolicy.xml"));

      var passwordSetting = (from p in xmlDoc.Descendants("Password")

      select new PasswordSetting

      {

                   Duration = int.Parse(p.Element("duration").Value),

                   MinLength = int.Parse(p.Element("minLength").Value),

                   MaxLength = int.Parse(p.Element("maxLength").Value),

                   NumsLength = int.Parse(p.Element("numsLength").Value),

                   SpecialLength = int.Parse(p.Element("specialLength").Value),

                   UpperLength = int.Parse(p.Element("upperLength").Value),

                   SpecialChars = p.Element("specialChars").Value

        }).First();

        StringBuilder sbPasswordRegx = new StringBuilder(string.Empty);

        //min and max

        sbPasswordRegx.Append(@"(?=^.{" + passwordSetting.MinLength + "," + passwordSetting.MaxLength + "}$)");

        //numbers length

        sbPasswordRegx.Append(@"(?=(?:.*?\d){" + passwordSetting.NumsLength + "})");

        //a-z characters

        sbPasswordRegx.Append(@"(?=.*[a-z])");

         //A-Z length

        sbPasswordRegx.Append(@"(?=(?:.*?[A-Z]){" + passwordSetting.UpperLength + "})");

         //special characters length

        sbPasswordRegx.Append(@"(?=(?:.*?[" + passwordSetting.SpecialChars + "]){" + passwordSetting.SpecialLength + "})");

         //(?!.*\s) - no spaces

         //[0-9a-zA-Z!@#$%*()_+^&] -- valid characters

        sbPasswordRegx.Append(@"(?!.*\s)[0-9a-zA-Z" + passwordSetting.SpecialChars + "]*$");

        return sbPasswordRegx.ToString();

} 

Add the code in Listing 3 to the beginning of the Register method. During testing with JavaScript disabled, I found that the error "System.ArgumentNullException: Value cannot be null. Parameter name: input" will occur when the Password input is empty. In order to avoid that, make sure the Password input is not empty before calling the Regex.IsMatch method. If the password does not meet the policy, display the "Password does not meet policy!" error message on the screen.

Listing 3
 

if (!string.IsNullOrEmpty(model.Password))

{

   if (!Regex.IsMatch(model.Password, GetPasswordRegex()))

     {

         ModelState.AddModelError("Password", "Password does not meet policy!");

     }

} 

Changes in Account Registration View

Include a reference to the jquery.password-strength.js plug-in on the page and add the script in listing 6 to the bottom of the view. Add a password policy link under the password textbox and set its ID to "passwordPolicy". Refer to listing 4.

Listing 4

@Html.ActionLink("Password policy","", null, new { id = "passwordPolicy" })

Initially, the only codes under the button submit click function was in listing 5. For some reason, the client side validation for the whole form was not working on a button submit click. Which does make sense because the plug-in should return false if the user clicks on the submit button without any input in the password textbox. But the same set of code works fine on the ASP.NET application. The work-around is to explicitly invoke the jQuery valid() function to check whether all the form elements are valid before checking if the password is valid.

Listing 5

$("[id='btnSubmit']").click(function () {
     return myPlugin.metReq(); //return true or false
});

Listing 6
 

<script type="text/javascript">

    $(document).ready(function () {

        var myPlugin = $("input[id='Password']").password_strength();

        $("[id='btnSubmit']").click(function () {

            var $form = $('form');

            if ($form.valid()) {

                return myPlugin.metReq(); //return true or false

            }

        });

        $("[id='passwordPolicy']").click(function (event) {

            var width = 350, height = 300, left = (screen.width / 2) - (width / 2),

            top = (screen.height / 2) - (height / 2);

            window.open("http://localhost:1234/xml/PasswordPolicy.xml", 'Password_poplicy',

           'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top);

            event.preventDefault();

            return false;

        });

    });

</script>

Conclusion

The plug-in is also implemented in the change password view in this sample. Again, I would recommend using an absolute URL to your XML file and download the demo and explore it in order to grasp the full concept of it because I might miss some important information in this article. I hope someone will find this information useful and make your programming job easier. If you find any bugs or disagree with the contents or want to help improve this article, please drop me a line and I'll work with you to correct it. Please send me an email if you want to help improve this article.

History

07/29/2012 First Release (v01.00.00)

Demo

http://mvc.ysatech.com/Account/Register

Download

http://mvc.ysatech.com/MvcPasswordStrength.zip(MVC3)


http://mvc.ysatech.com/jQueryXMLMvc2PasswordStrength.zip(MVC2)