To response from Monneratrj It already solves most of what you need. I would just like to add a regex to check the last condition (do not allow two or more identical consecutive characters, such as aa
, bb
), since this was not covered in the links that I suggested in the comments:
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[$*&@#])(?:([0-9a-zA-Z$*&@#])(?!\1)){8,}$
The section that makes the check is (?:([0-9a-zA-Z$*&@#])(?!\1))
. Explaining from the inside out:
([0-9a-zA-Z$*&@#])
: is a character class containing numbers, letters and the special characters you are considering. They are within parentheses to form a catch group
(?!\1)
: is a Lookahead negative, which checks if something does not exist in front. In case, \1
means "the stretch that was captured by the first capture group".
As the first capture group is a character that can be number, letter or $*&@#
, the Lookahead negative checks if that same character is not ahead. If it is (i.e., if the same character appears repeated twice in a row), the regex fails.
Finally, I put it all inside (?:
and )
, because it forms a catch group (I don’t want this whole stretch to interfere with \1
, then he shouldn’t be a capture group), and then I put the {8,}
so that it has at least 8 characters.
The "trick" of Lookahead is that it only checks what is (or what is not) ahead, and then goes back to where it was and continues to evaluate the rest of the regex. Hence the Lookahead negative does not interfere with the character count: it only sees if the next one is the same (thanks to the reference \1
), and if not, go back to where you were and continue to check the regex (in this case, if you have at least 8 characters among those specified).
Testing:
let r = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[$*&@#])(?:([0-9a-zA-Z$*&@#])(?!\1)){8,}$/;
console.log(r.test('a12B@cde')); // true
console.log(r.test('a12B@cce')); // false
console.log(r.test('a22B@cde')); // false
console.log(r.test('a12@@cde')); // false
console.log(r.test('a12B@cCe')); // true
If you also want to reject sequences like aA
, just use the option i
in regex:
let r = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[$*&@#])(?:([0-9a-zA-Z$*&@#])(?!\1)){8,}$/i;
console.log(r.test('a12B@cde')); // true
console.log(r.test('a12B@cce')); // false
console.log(r.test('a22B@cde')); // false
console.log(r.test('a12@@cde')); // false
console.log(r.test('a12B@cCe')); // false
You can rely on in that question and in that other also
– hkotsubo