With the demise of passwordsgenerator.net I wrote my own generator;
Please review with an eye on correctness and maintainability;
//Inspiration; https://web.archive.org/web/20220711113233/https://passwordsgenerator.net/
button.addEventListener("click", generatePassword);
//Yes, not super, global constant..
const symbolList = ';!#$%&*+-=?^_';
function generatePassword(){
const includeSymbols = symbols.checked;
const includeNumbers = numbers.checked;
const includeLower = lower.checked;
const includeUpper = upper.checked;
const excludeHard = easy.checked;
const length = size.valueAsNumber;
const charPool = [];
let pwd;
//Users who check everything off get an all spaces password, not really secure
if(!includeSymbols && !includeNumbers && !includeLower && !includeUpper){
setPassword(' '.repeat(length));
return
}
if(includeLower){ //No i or o
charPool.push(... 'abcdefghjklmnpqrtsuvwxyz'.split(''));
}
if(includeUpper){ //No I or O
charPool.push(... 'ABCDEFGHJKLMNPQRSTUVWXYZ'.split(''));
}
if(includeNumbers){ //No 0 or 1
charPool.push(... '23456789'.split(''));
}
if(includeSymbols){ //No @ or |
charPool.push(... symbolList.split(''));
}
if(!excludeHard){
charPool.push(... ''.split('ioIo01|'));
}
const charPoolLength = charPool.length;
do {
pwd = '';
for(let i = 0; i < length; i++){
pwd += charPool[Math.floor(Math.random()*charPoolLength)];
}
}while(
(includeLower && !hasLowercase(pwd)) ||
(includeUpper && !hasUppercase(pwd)) ||
(includeNumbers && !hasNumber(pwd)) ||
(includeSymbols && !hasSymbol(pwd))
)
setPassword(pwd);
}
function hasLowercase(s) {
return s.toUpperCase() != s;
}
function hasUppercase(s) {
return s.toUpperCase() != s;
}
function hasNumber(s){
return s.split('').some(c => !!~'0123456789'.indexOf(c));
}
function hasSymbol(s){
return s.split('').some(c => !!~symbolList.indexOf(c));
}
function setPassword(s){
pwd.value = s;
pwd.select();
}
/**form {text-align: center;display: block}*/
/**pair {display:block; text-align: left}*/
form {display: block; margin-left: auto; margin-right: auto; width: 75%}
pair {display:block}
<form>
<h2>Secure Password Generator</h2>
<pair>
<label for="size">Password Length</label>
<!-- Minimum 4 so that we have space for 1 upper, 1 lower, 1 symbol, 1 number -->
<!-- Maximum 42 because that's the answer -->
<input type="number" id="size" value="12" min="4" max="42">
</pair>
<pair>
<label for="symbols">Include Symbols ( e.g. @#$% )</label>
<input type="checkbox" id="symbols" checked>
</pair>
<pair>
<label for="numbers">Include Numbers:( e.g. 123456 )</label>
<input type="checkbox" id="numbers" checked>
</pair>
<pair>
<label for="lower">Include Lowercase Characters:( e.g. abcdefgh )</label>
<input type="checkbox" id="lower" checked>
</pair>
<pair>
<label for="upper">Include Uppercase Characters:( e.g. ABCDEFGH )</label>
<input type="checkbox" id="upper" checked>
</pair>
<pair>
<label for="easy">Exclude Similar & Ambiguous Characters:( e.g. oO0iI1| )</label>
<input type="checkbox" id="easy" checked>
</pair>
<br>
<button type="button" id="button">Generate</button>
<br><br>
<pair>
<label for="pwd">Generated password</label>
<input type="text" id="pwd">
</pair>
<br><br>
Inspired by <a href="https://web.archive.org/web/20220711113233/https://passwordsgenerator.net/">passwordsgenerator.net</a>
<!-- This will never generate server side, always try to use cookies, and always autoselect the pwd-->
</form>