Oleksiy's Blog

Password Strength directive for AngularJS

10/28/2014

Have just written yet another tool password strength checker.

Module:

angular.module('myApp.strengthPassword', [])
    .directive('myPasswordStrength', ['passwordStrengthService',
        function (service) {

            function updateValues (password, scope) {
                password            = password || '';
                scope.lengthLevel   = service.getLengthLevel(password);
                scope.strengthLevel = service.getStrengthLevel(password);
                scope.strengthLabel = service.getLabel(scope.strengthLevel);
            }

            function link (scope) {
                scope.$watch('password', function(password) {
                    updateValues(password, scope);
                });
            }

            return {
                restrict: 'EA',
                replace: true,
                scope: { password: '=' },
                templateUrl: 'directives/strengthpassword/strengthpassword.tpl.html',
                link: link
            };
    }])

    .factory('passwordStrengthService', ['passwordStrengthConstants',
        function (constant) {
            var _matchPatterns  = constant.patterns;
            var _maxLengthLevel = constant.maxLengthLevel;

            function _getStrengthLevel (password) {
                for(var level in _matchPatterns) {
                    if (_matchPatterns[level].test(password)) { return level; }
                }
            }

            function _getLengthLevel (password) {
                var level = password.length / _maxLengthLevel * 100;
                return level < 100 ? level : 100;
            }

            function _getLabel (level) {
                return constant.labels[level];
            }

            return {
                getStrengthLevel    : _getStrengthLevel,
                getLengthLevel      : _getLengthLevel,
                getLabel            : _getLabel
            };
        }])

    .constant('passwordStrengthConstants', {
        patterns : {
            empty   : /^$/i,                                                   // not looping if empty
            strong  : /^.*(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*\W).*$/, // 8+ sym, small+capital, digits, alpha
            medium  : /^.*(?=.{6,})(?=.*[a-z])(?=.*[\d\W]).*$/i,               // 6+ sym letters, digits
            weak    : /^.*(?=.{6,})(?=.*[a-z\d]).*$/i,                         // 6+ letters or digits
            useless : /^.*$/i                                                  // anything other
        },

        labels: {
            empty   : '',
            strong  : 'Good password!',
            medium  : 'Password is acceptable, but you could better',
            weak    : 'Your password is a piece of crap',
            useless : 'Password is too short'
        },

        maxLengthLevel : 20
    });

Template:

<div class="password-strength">
    <div class="password-strength-label">
        {{strengthLabel || 'js.passwordStrength'}}
    </div>
    <div class="bar-container">
        <div class="bar {{strengthLevel}}" style="width: {{lengthLevel}}%"></div>
    </div>
</div>

HTML:

<input type="password" name="password" id="password" ng-model="password" />
<div my-password-strength password="password"></div>

LESS/SASS:

.password-strength {
  margin-bottom: 10px;

  .bar-container {
    background: #eee;
    margin-bottom: 10px;

    .password-strength-label {
      margin-bottom: 5px;
    }

    .bar {
      height: 5px;
      -ms-transition:     width .5s ease;
      -webkit-transition: width .5s ease;
      transition:         width .5s ease;

      &.strong  { background: #008641; }
      &.medium  { background: #2573d9; }
      &.weak    { background: #f60; }
      &.useless { background: #e51400; }
    }
  }
}