This site contains the document for old version. Please upgrade to the latest version v1.0.0

Building a password strength meter with Dropbox's zxcvbn libary

Examples

In this example, we will use the awesome Dropbox's zxcvbn library to build a password strength meter.

For anyone who haven't known about zxcvbn library, it's a password strength estimator inspired by password crackers developed by Dropbox.

It can recognize and weighs 30k common passwords. For more information about this library, you can refer to its official page.

It's quite easy to use it to see how strong a given password is:

var result = zxcvbn(password);

// The password strength score. See the following table for available values
result.score

// Explain why the password is weak. "This is a top-10 common password", for example
result.feedback.warning

score is an integer number between 0 and 4 indicate the strength level:

Score Description
0 Too guessable: risky password
1 Very guessable: protection from throttled online attacks
2 Somewhat guessable: protection from unthrottled online attacks
3 Safely unguessable: moderate protection from offline slow-hash scenario
4 Very unguessable: strong protection from offline slow-hash scenario

We will use the callback validator to check the password strength, and treat it as an invalid password if the score is less than 3:

$('#passwordForm').formValidation({
    framework: 'bootstrap',
    icon: {
        ...
    },
    fields: {
        password: {
            validators: {
                notEmpty: {
                    message: 'The password is required'
                },
                callback: {
                    callback: function(value, validator, $field) {
                        var password = $field.val();
                        if (password == '') {
                            return true;
                        }

                        var result  = zxcvbn(password),
                            score   = result.score,
                            message = result.feedback.warning || 'The password is weak';

                        // We will treat the password as an invalid one if the score is less than 3
                        if (score < 3) {
                            return {
                                valid: false,
                                message: message    // Yeah, this will be set as error message
                            }
                        }

                        return true;
                    }
                }
            }
        }
    }
});
The sample code above shows how to set a dynamic error message returned from the callback validator. This capability is also available in a few of validators as listed in the dynamic message section.

We also can use Bootstrap's progress bar component to show the strength level:

<div class="progress password-progress">
    <div id="strengthBar" class="progress-bar" role="progressbar" style="width: 0;"></div>
</div>
$('#passwordForm').formValidation({
    framework: 'bootstrap',
    icon: {
        ...
    },
    fields: {
        password: {
            validators: {
                notEmpty: {
                    message: 'The password is required'
                },
                callback: {
                    callback: function(value, validator, $field) {
                        ...

                        var result  = zxcvbn(password),
                            score   = result.score;

                        // Update the progress bar width and add alert class
                        var $bar = $('#strengthBar');
                        switch (score) {
                            case 0:
                                $bar.attr('class', 'progress-bar progress-bar-danger')
                                    .css('width', '1%');
                                break;
                            case 1:
                                $bar.attr('class', 'progress-bar progress-bar-danger')
                                    .css('width', '25%');
                                break;
                            case 2:
                                $bar.attr('class', 'progress-bar progress-bar-danger')
                                    .css('width', '50%');
                                break;
                            case 3:
                                $bar.attr('class', 'progress-bar progress-bar-warning')
                                    .css('width', '75%');
                                break;
                            case 4:
                                $bar.attr('class', 'progress-bar progress-bar-success')
                                    .css('width', '100%');
                                break;
                        }

                        ...
                    }
                }
            }
        }
    }
});

If you use any framework which is supported by FormValidation, you can use the framework progress bar component as listed in the table below:

Lastly, for a full working demonstration, you can play with the following form:

<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.3.0/zxcvbn.js"></script>

<style>
.password-progress {
    margin-top: 10px;
    margin-bottom: 0;
}
</style>

<form id="passwordForm" method="post" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Password</label>
        <div class="col-xs-5">
            <input type="password" class="form-control" name="password" />

            <div class="progress password-progress">
                <div id="strengthBar" class="progress-bar" role="progressbar" style="width: 0;"></div>
            </div>
        </div>
    </div>

    <div class="form-group">
        <div class="col-xs-5 col-xs-offset-3">
            <button type="submit" class="btn btn-default">Validate</button>
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    $('#passwordForm').formValidation({
        framework: 'bootstrap',
        icon: {
            valid: 'glyphicon glyphicon-ok',
            invalid: 'glyphicon glyphicon-remove',
            validating: 'glyphicon glyphicon-refresh'
        },
        fields: {
            password: {
                validators: {
                    notEmpty: {
                        message: 'The password is required'
                    },
                    callback: {
                        callback: function(value, validator, $field) {
                            var password = $field.val();
                            if (password == '') {
                                return true;
                            }

                            var result  = zxcvbn(password),
                                score   = result.score,
                                message = result.feedback.warning || 'The password is weak';

                            // Update the progress bar width and add alert class
                            var $bar = $('#strengthBar');
                            switch (score) {
                                case 0:
                                    $bar.attr('class', 'progress-bar progress-bar-danger')
                                        .css('width', '1%');
                                    break;
                                case 1:
                                    $bar.attr('class', 'progress-bar progress-bar-danger')
                                        .css('width', '25%');
                                    break;
                                case 2:
                                    $bar.attr('class', 'progress-bar progress-bar-danger')
                                        .css('width', '50%');
                                    break;
                                case 3:
                                    $bar.attr('class', 'progress-bar progress-bar-warning')
                                        .css('width', '75%');
                                    break;
                                case 4:
                                    $bar.attr('class', 'progress-bar progress-bar-success')
                                        .css('width', '100%');
                                    break;
                            }

                            // We will treat the password as an invalid one if the score is less than 3
                            if (score < 3) {
                                return {
                                    valid: false,
                                    message: message
                                }
                            }

                            return true;
                        }
                    }
                }
            }
        }
    });
});
</script>