In this tutorial, we would be creating a simple registration form with just fields for fullname, email and password. We would use zxcvbn to estimate the strength of the password in the form and also provide a visual feedback. We would also use AngularJS for effortless two-way data bindings.

At the end of the tutorial, the final page will behave as shown in the following live demo:

Passwords are commonly used for user authentication in most web applications and as such, it is required that passwords be stored in a safe way. Over the years, techniques such as one-way password hashing - which involves salting most of the times, have been employed to hide the real representation of passwords being stored in a database.

Although password hashing is a great step in securing password, the user still poses a major challenge to password security. A user who uses a very common word as password makes the effort of hashing fruitless, since a bruteforce attack can crack such password in a very short time. Infact, if highly sophisticated infrastructure is used for the attack, it may even take split milliseconds, depending on the password complexity or length.

Many web applications today such as Google, Twitter, Dropbox, etc insist on users having considerably strong passwords either by ensuring a minimum password length or some combination of alphanumeric characters and maybe symbols in the password.

How then is password strength measured? Dropbox developed an algorithm for a realistic password strength estimator inspired by password crackers. This algorithm is packaged in a Javascript library called zxcvbn. In addition, the package contains a dictionary of commonly used English words, names and passwords.

Before we begin the tutorial, we would download all the dependencies we need using the Bower package manager. If you don't already have Bower in your system, you can follow the Bower Installation Guide. Run the following command to install all the dependencies for the tutorial.

bower install zxcvbn angularjs#1.5.9 bootstrap

The root folder should contain two folders and one file.

assets folder

folder bower_components folder

folder index.html file

The folder structure for our project should look like the following screenshot - you can create the folders and directories as required.

Upgrade Your JS Go from vanilla JavaScript 👉 React

Watch for FREE

Let's begin by adding the basic markup for our page in the index.html file. We would link to the Bootstrap files - bootstrap.min.css and bootstrap-theme.min.css , and also the angular.min.js framework from our bower_components folder. We would also link to our project's css and js files in the assets folder. See the following code for the basic HTML markup of our page.

<!DOCTYPE html> < html class = " no-js " > < head > < meta charset = " utf-8 " > < meta http-equiv = " X-UA-Compatible " content = " IE=edge " > < title > Password Strength </ title > < meta name = " viewport " content = " width=device-width, initial-scale=1 " > < link rel = " stylesheet " href = " bower_components/bootstrap/dist/css/bootstrap.min.css " > < link rel = " stylesheet " href = " bower_components/bootstrap/dist/css/bootstrap-theme.min.css " > < link rel = " stylesheet " href = " assets/css/main.css " > </ head > < body > < div class = " main-container " > < div class = " form-container " > < form action = " " method = " POST " role = " form " > < legend class = " form-label " > Join the Team </ legend > < div class = " form-group " > < label for = " fullname " > Fullname </ label > < input type = " text " class = " form-control " id = " fullname " placeholder = " Enter Fullname " > </ div > < div class = " form-group " > < label for = " email " > Email </ label > < input type = " email " class = " form-control " id = " email " placeholder = " Enter Email Address " > </ div > < div class = " form-group " > < label for = " password " > Password </ label > < div class = " form-hint " > To conform with our Strong Password policy, you are required to use a sufficiently strong password. Password must be more than 7 characters. </ div > < input type = " password " class = " form-control " id = " password " placeholder = " Enter Password " > </ div > < button type = " submit " class = " btn btn-primary " > Submit </ button > </ form > </ div > </ div > < script src = " bower_components/angular/angular.min.js " > </ script > < script src = " assets/js/app.js " > </ script > </ body > </ html >

At this point, our page should look like the following screenshot:

Now we would add some CSS rules to the assets/css/main.css file to spice up the page.

body { margin : 0 ; padding : 0 ; } .main-container { display : table ; width : 400 px ; position : absolute ; top : 0 ; bottom : 0 ; left : 0 ; right : 0 ; margin : auto ; } .form-container { position : relative ; bottom : 100 px ; display : table-cell ; vertical-align : middle ; } .form-container form > div { padding : 0 15 px ; } .form-container form > button { margin-left : 15 px ; } legend .form-label { font-size : 24 pt ; padding : 0 15 px ; } .form-hint { font-size : 7 pt ; line-height : 9 pt ; margin : - 5 px auto 5 px ; color : #999 ; }

At this point, our page should look like the following screenshot:

Now we would asynchronously load the zxcvbn package into our page. We would add the script in the assets/js/app.js file. The following script programmatically creates a new <script> element that is inserted before the first script element defined in the page, when the page is finished loading. The src of this script element points to the zxcvbn.js file. The async attribute is also set to true to enable asynchronous loading.

( function ( ) { var ZXCVBN_SRC = 'bower_components/zxcvbn/dist/zxcvbn.js' ; var async_load = function ( ) { var first , s ; s = document . createElement ( 'script' ) ; s . src = ZXCVBN_SRC ; s . type = 'text/javascript' ; s . async = true ; first = document . getElementsByTagName ( 'script' ) [ 0 ] ; return first . parentNode . insertBefore ( s , first ) ; } ; if ( window . attachEvent != null ) { window . attachEvent ( 'onload' , async_load ) ; } else { window . addEventListener ( 'load' , async_load , false ) ; } } ) . call ( this ) ;

Now we can try using the zxcvbn() function with any password string from our browser's console. The zxcvbn() function returns a result object with several properties. In this tutorial, we would be concerned only with the score property, which is an integer from 0 - 4 (useful for implementing a strength bar).

0 - too guessable

- too guessable 1 - very guessable

- very guessable 2 - somewhat guessable

- somewhat guessable 3 - safely unguessable

- safely unguessable 4 - very unguessable

console . log ( zxcvbn ( 'password' ) ) ;

See the following video on testing the zxcvbn() method on the browser's console.

Now we would make some little improvements to our code to engage AngularJS. First let's create a module for our app called PasswordStrength , and a simple controller for our form called FormController . We would append the following script to the assets/js/app.js file.

angular . module ( 'PasswordStrength' , [ ] ) ; angular . module ( 'PasswordStrength' ) . controller ( 'FormController' , function ( $scope ) { } ) ;

Next, we would edit our index.html file to add an ng-app directive for the module on the root <html> element, and an ng-controller directive for the controller on the <form> element.

< html class = " no-js " ng-app = " PasswordStrength " > < form action = " " method = " POST " role = " form " ng-controller = " FormController " >

Now we can add our validation logic to the form.

First, we would give names to the form and the input fields, and also add ng-model directives to the input fields to be able to harness the built-in NgModelController .

directives to the input fields to be able to harness the built-in . Next, we would add the ng-required constraint to the input fields, since we desire they should be filled.

constraint to the input fields, since we desire they should be filled. Next, we would disable the submit button using the ng-disabled directive and only enable it when the form is fully validated.

directive and only enable it when the form is fully validated. Finally, we would harness the ng-class and ng-show directives to provide error feedback and messages for the input elements.

The modified form in our index.html file should look like this:

< form action = " " method = " POST " name = " joinTeamForm " role = " form " ng-controller = " FormController " > < legend class = " form-label " > Join the Team </ legend > < div class = " form-group " > < label for = " fullname " > Fullname </ label > < div class = " error form-hint " ng-show = " joinTeamForm.fullname.$dirty && joinTeamForm.fullname.$error.required " ng-cloak > {{"This field is required."}} </ div > < input type = " text " class = " form-control " ng-class = " (joinTeamForm.fullname.$dirty && joinTeamForm.fullname.$invalid) ? ' error ' : ' ' " id = " fullname " name = " fullname " placeholder = " Enter Fullname " ng-required = " true " ng-model = " fullname " > </ div > < div class = " form-group " > < label for = " email " > Email </ label > < div class = " error form-hint " ng-show = " joinTeamForm.email.$dirty && joinTeamForm.email.$error.required " ng-cloak > {{"This field is required."}} </ div > < div class = " error form-hint " ng-show = " joinTeamForm.email.$dirty && joinTeamForm.email.$error.email " ng-cloak > {{"Email is invalid."}} </ div > < input type = " email " class = " form-control " ng-class = " (joinTeamForm.email.$dirty && joinTeamForm.email.$invalid) ? ' error ' : ' ' " id = " email " name = " email " placeholder = " Enter Email Address " ng-required = " true " ng-model = " email " > </ div > < div class = " form-group " > < label for = " password " > Password </ label > < div class = " form-hint " > To conform with our Strong Password policy, you are required to use a sufficiently strong password. Password must be more than 7 characters. </ div > < input type = " password " class = " form-control " ng-class = " (joinTeamForm.password.$dirty && joinTeamForm.password.$invalid) ? ' error ' : ' ' " id = " password " name = " password " placeholder = " Enter Password " ng-required = " true " ng-model = " password " > </ div > < button type = " submit " class = " btn btn-primary " ng-disabled = " joinTeamForm.$invalid " > Submit </ button > </ form >

In the preceeding code, we have named our form as joinTeamForm and have given names to the input elements making it possible for us to harness Angular's built-in NgModelController . We have also created data-bindings for the input elements using the ng-model directive.

We used some of the validation state properties - $dirty , $invalid , $error , provided by the NgModelController API to determine if our form is fully validated. The ng-class directive was also used to dynamically add an error class to the input elements based on the validation criteria.

Also, we used ng-cloak to prevent the browser from showing our error messages while rendering. Since, we included angular.js at the end of our page, this would not be effective. To correct this, we would add the following CSS rule to the main.css file.

[ng\ :cloak ], [ng-cloak] , [data-ng-cloak] , [x-ng-cloak] , .ng-cloak , .x-ng-cloak { display : none !important ; }

Now, we would add some CSS rules to the main.css file for our error feedback.

.form-control.error { border-color : red ; } .form-hint.error { color : #C00 ; font-weight : bold ; font-size : 8 pt ; }

At this point, our page should look like the following screenshot - observe that the form submit button is now disabled on page load.

Now we would go ahead to create the password strength meter. We would also create a new directive in our module called okPassword , that will define a custom validation constraint for the password element, which will ensure that a valid password must be more than 7 characters and must have a minimum zxcvbn score of 2 . We will also add a visual feedback to keep track of the password length.

First, let's add the following into our index.html file, immediately after the password field element, for our password strength meter.

< div class = " label password-count " ng-class = " password.length > 7 ? ' label-success ' : ' label-danger ' " ng-cloak > {{ password | passwordCount:7 }} </ div > < div class = " strength-meter " > < div class = " strength-meter-fill " data-strength = " {{passwordStrength}} " > </ div > </ div >

Here, we are using the ng-class directive and Bootstrap's label classes to provide feedback based on the password length. We are also using a custom passwordCount filter to slightly format the display of the password length.

Also, we are binding the value for the data-strength attribute to the passwordStrength property of the controller's scope, which will contain the password strength score.

Now, let's add the following css rules to the main.css file to style the password strength meter which we just created.

.password-count { float : right ; position : relative ; bottom : 24 px ; right : 10 px ; } .strength-meter { position : relative ; height : 3 px ; background : #DDD ; margin : 10 px auto 20 px ; border-radius : 3 px ; } .strength-meter :before , .strength-meter :after { content : '' ; height : inherit ; background : transparent ; display : block ; border-color : #FFF ; border-style : solid ; border-width : 0 5 px 0 5 px ; position : absolute ; width : 80 px ; z-index : 10 ; } .strength-meter :before { left : 70 px ; } .strength-meter :after { right : 70 px ; } .strength-meter-fill { background : transparent ; height : inherit ; position : absolute ; width : 0 ; border-radius : inherit ; transition : width 0.5 s ease-in-out, background 0.25 s ; } .strength-meter-fill [data-strength='0'] { background : darkred ; width : 20% ; } .strength-meter-fill [data-strength='1'] { background : orangered ; width : 40% ; } .strength-meter-fill [data-strength='2'] { background : orange ; width : 60% ; } .strength-meter-fill [data-strength='3'] { background : yellowgreen ; width : 80% ; } .strength-meter-fill [data-strength='4'] { background : green ; width : 100% ; }

Here, we have made our password strength meter to indicate five levels for the different password strength scores ranging from 0 to 4 . We have also specified different colors and fill width for each score level.

At this point, our page should look like the following screenshot. Observe that the page exhibits some strange behaviour since we have not yet defined the passwordCount filter.

Before we proceed, we would define the passwordCount filter which slightly formats the display of the password length in the view. Let's append the following to the app.js file to create the filter.

angular . module ( 'PasswordStrength' ) . filter ( 'passwordCount' , [ function ( ) { return function ( value , peak ) { var value = angular . isString ( value ) ? value : '' , peak = isFinite ( peak ) ? peak : 7 ; return value && ( value . length > peak ? peak + '+' : value . length ) ; } ; } ] ) ;

In the preceeding code, the passwordCount filter takes a string value and an optional peak parameter which defaults to 7 if omitted or not a valid integer. If the length of the input string is less than the peak , it returns the length of the string; otherwise, it returns {peak}+ .

Now we can get visual feedback for our password length as we type. See the following screenshot:

Now, we would go on to create the okPassword directive for the password field. We would also create a service that will encapsulate the implementation of the zxcvbn() function. Let's add the following to the app.js file to create the service.

angular . module ( 'PasswordStrength' ) . factory ( 'zxcvbn' , [ function ( ) { return { score : function ( ) { var compute = zxcvbn . apply ( null , arguments ) ; return compute && compute . score ; } } ; } ] ) ;

Here, we have created a service called zxcvbn that provides just one API method score() . The score() method takes the same parameters as the zxcvbn() function and calls it internally. It returns the estimated password strength score.

Now, we can apply this service to create our directive. Add the following to the app.js file to create the directive.

angular . module ( 'PasswordStrength' ) . directive ( 'okPassword' , [ 'zxcvbn' , function ( zxcvbn ) { return { restrict : 'AC' , require : 'ngModel' , link : function ( $scope , $element , $attrs , ngModelCtrl ) { $element . on ( 'blur change keydown' , function ( evt ) { $scope . $evalAsync ( function ( $scope ) { var pwd = $scope . password = $element . val ( ) ; $scope . passwordStrength = pwd ? ( pwd . length > 7 && zxcvbn . score ( pwd ) || 0 ) : null ; ngModelCtrl . $setValidity ( 'okPassword' , $scope . passwordStrength >= 2 ) ; } ) ; } ) ; } } ; } ] ) ;

In the preceeding code, we defined the okPassword directive with zxcvbn service as a dependency. We specified that the directive can be used either as a class or an attribute. We also specified that we need the NgModelController .

In the link function, we added an event listener on the element which is triggered on blur , change and keyup events. The event listener uses the $scope.$evalAsync() method to delay the update of the scope's properties. Also, the $setValidity() method of the NgModelController was used to define the validity criterion for the okPassword validation constraint.

Finally, we go ahead to add the ok-password attribute or class to our password element to ensure that the validation constraint defined in our directive is applied.

< input type = " password " class = " form-control ok-password " ng-class = " (joinTeamForm.password.$dirty && joinTeamForm.password.$invalid) ? ' error ' : ' ' " id = " password " name = " password " placeholder = " Enter Password " ng-required = " true " ng-model = " password " >

Now we can get visual feedback for our password strength as we type. See the following screenshot:

In this tutorial, we have been able to implement a password strength meter based on the zxcvbn Javascript library in our AngularJS application. For a detailed usage guide and documentation of the zxcvbn library, see the zxcvbn repository on Github. For a complete code sample of this tutorial, checkout the password-strength-demo repository on Github. You can also get a live demo of this tutorial on jsFiddle.

Like this article? Follow @gladchinda on Twitter