Since I've seen tons of password validation help requests on regexadvice.com (where I hang out from time to time), I've written up a more general-purpose JavaScript password validation function. It's reasonably straightforward, and covers the validation requirements I've most frequently encountered. Plus, if it doesn't handle your exact needs, its functionality can be augmented by passing it custom functions and regular expressions.
Here are the validation types supported out of the box. All are optional, which means that all passwords are allowed by default.
- Minimum and maximum length.
- Minimum n lowercase characters (a–z).
- Minimum n uppercase characters (A–Z).
- Minimum n combined a–z and A–Z characters.
- Minimum n numeric characters (0–9).
- Minimum n special characters (characters other than a–z, A–Z, and 0–9).
- Ban particular words (tested case-insensitively).
- Ban n-length character sequences (e.g. "abc", "XYZ", or "789", with a sequence length of
3
; does not apply to special characters).
- Ban n-length qwerty character sequences (e.g. "qwerty" or "asdf", with a sequence length of
4
; does not apply to special characters).
- Ban sequential, identical characters (e.g. "aa" or "!!").
- Use custom regular expressions (tested using
RegExp.prototype.test
) and functions (the password is provided as the first argument, and a Boolean value is expected in return).
Here's an example of how it can be used:
var password = "password";
var passed = validatePassword(password, {
length: [8, Infinity],
lower: 1,
upper: 1,
numeric: 1,
special: 1,
badWords: ["password", "steven", "levithan"],
badSequenceLength: 4
});
The above requires that password
is at least eight characters long; has at least one lowercase, uppercase, numeric, and special character; doesn't include the words "password", "steven", or "levithan"; and doesn't include an alphanumeric sequence four or more characters in length (e.g. "1234").
Here's the code (there are no external library dependencies):
function validatePassword (pw, options) {
var o = {
lower: 0,
upper: 0,
alpha: 0,
numeric: 0,
special: 0,
length: [0, Infinity],
custom: [ ],
badWords: [],
badSequenceLength: 0,
noQwertySequences: false,
noSequential: false
};
for (var property in options)
o[property] = options[property];
var re = {
lower: /[a-z]/g,
upper: /[A-Z]/g,
alpha: /[A-Z]/gi,
numeric: /[0-9]/g,
special: /[\W_]/g
},
rule, i;
if (pw.length < o.length[0] || pw.length > o.length[1])
return false;
for (rule in re) {
if ((pw.match(re[rule]) || []).length < o[rule])
return false;
}
for (i = 0; i < o.badWords.length; i++) {
if (pw.toLowerCase().indexOf(o.badWords[i].toLowerCase()) > -1)
return false;
}
if (o.noSequential && /([\S\s])\1/.test(pw))
return false;
if (o.badSequenceLength) {
var lower = "abcdefghijklmnopqrstuvwxyz",
upper = lower.toUpperCase(),
numbers = "0123456789",
qwerty = "qwertyuiopasdfghjklzxcvbnm",
start = o.badSequenceLength - 1,
seq = "_" + pw.slice(0, start);
for (i = start; i < pw.length; i++) {
seq = seq.slice(1) + pw.charAt(i);
if (
lower.indexOf(seq) > -1 ||
upper.indexOf(seq) > -1 ||
numbers.indexOf(seq) > -1 ||
(o.noQwertySequences && qwerty.indexOf(seq) > -1)
) {
return false;
}
}
}
for (i = 0; i < o.custom.length; i++) {
rule = o.custom[i];
if (rule instanceof RegExp) {
if (!rule.test(pw))
return false;
} else if (rule instanceof Function) {
if (!rule(pw))
return false;
}
}
return true;
}
You can download it here.
Lemme know if you have any feature requests or other suggestions about how to improve it, or if you need help writing custom rules for it.