Take one random upper case letter, one random lower case letter, one random digit, one random symbol and 12 random characters from all groups. Shuffle them (I've copied the shuffle function from How to randomize (shuffle) a JavaScript array?)
function getRandomChar(str) {
return str.charAt(Math.floor(Math.random() * str.length));
}
function shuffle(array) {
var currentIndex = array.length, randomIndex;
// While there remain elements to shuffle...
while (currentIndex != 0) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
function generateP(options) {
const groups = options?.groups ?? [
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz',
'1234567890',
'!@#$%^&()_+~`|}{[]:;?><,./-='
];
const length = options?.length ?? 16;
let pass = groups.map(getRandomChar).join('');
const str = groups.join('');
for (let i = pass.length; i <= length; i++) {
pass += getRandomChar(str)
}
return shuffle(pass);
}
console.log(generateP());
// Tests
console.log('Running tests...');
for (let i = 0; i < 1e5; ++i) {
const pass = generateP();
if (!/[A-Z]/.test(pass) || !/[a-z]/.test(pass) || !/[0-9]/.test(pass) || !/[!@#$%^&()_+~`|}{[\]:;?><,./-=]/.test(pass)) {
console.log('generateP() failed with: ' + pass);
}
}
console.log('Tests finished');