Building a Node.js Password Generator

Think like an engineer

·

7 min read

A password generator is a great project for a beginner developer to test their mettle against. One can build a decent password generator in fewer than 10 lines of code, however, we will walk through building one from the ground up with a focus on robustness and customization. Furthermore, rather than regurgitating code, the focus here will be on the thought process behind developing the application.

Before writing any code, it is important to outline what you wish to accomplish and avoid. This starts by asking increasingly more specific questions about your application. For our purposes, here's what that outline might look like:

Password Generator Goals:

  • The ability to generate a password of a random length or choose one of a specified length.

  • Password generator should allow for a customizable minimum length and maximum length.

  • Character set should include upper and lower case letters, numbers, and symbols/special characters.

  • The inclusion of symbols should be optional.

With that basic outline in mind, we can begin coding our solution. Because we are building a Node.js password generator, there is no browser/front-end to accept user inputs. For this reason, we will start by installing the prompt-sync module. This will allow us to request and receive user input on the command line. After doing this, simply declare the prompt variable at the top of the file.

const prompt = require('prompt-sync')();

Okay, time to start building. Generally speaking, it is best to start with the core functionality of your application and work upward from there. That being said, we will start by generating the character set for our password.

const passwordGenerator =()=> {
    //fills an array with the numbers 0-9
    const numbers = [];
    for(let i = 0; i < 10; i++){
        numbers.push(i)
    }
   //fills an array with special characters
   const specialChars = [];
    for (let i = 33; i <= 47; i++) {
        specialChars.push(String.fromCharCode(i));
      }
//fill an array with letters a-z
const alphabetCharacters = [];
    for (let i = 97; i <= 122; i++) {
        alphabetCharacters.push(String.fromCharCode(i));
      }
//fill an array with letters a-z uppercase
const uppercaseAlphabetCharacters = [];    
      alphabetCharacters.forEach(e =>{
        uppercaseAlphabetCharacters.push(e.toUpperCase())
    })  

};

Though I utilized dynamic initialization to create those arrays, due to the relatively small size of each array, static initialization does make sense. Dynamic initialization does make sense when trying to maximize future customizability, however, it is not necessary for an application this small.

Now that we have the arrays that will collectively compromise the character set needed for password generation, we need some variables to store the values surrounding the password. Specifically, we need variables for what the minimum and maximum allowable lengths can be, the actual password length, and a single variable for the character set.

const passwordGenerator =(minNum = 12, maxNum = 20)=> {
let totalCharacterList;
let passwordLength;
    //fills an array with the numbers 0-9
    const numbers = [];
    for(let i = 0; i < 10; i++){
        numbers.push(i)
    }
   //fills an array with special characters
   const specialChars = [];
    for (let i = 33; i <= 47; i++) {
        specialChars.push(String.fromCharCode(i));
      }
//fill an array with letters a-z
const alphabetCharacters = [];
    for (let i = 97; i <= 122; i++) {
        alphabetCharacters.push(String.fromCharCode(i));
      }
//fill an array with letters a-z uppercase
const uppercaseAlphabetCharacters = [];    
      alphabetCharacters.forEach(e =>{
        uppercaseAlphabetCharacters.push(e.toUpperCase())
    })  

};

With the character set and core variables in place, we need a function to generate a random number should the user elect to generate a password of a random length. Because we will be utilizing a minimum and maximum value for password length, we already have a range to work with for our random number generator, and do not need to use any 'magic numbers'.

const randomLength =()=>{
   return Math.floor(Math.random() * (maxNum - minNum + 1)+ minNum) 
}

Now it's time to tackle the prompts/user inputs. Firstly, we need to determine if the user would like a password of randomly generated length, or set a password length of their own.

let passwordChoice = prompt(`Would you like us to generate a password of random length? Enter Yes or No: `);
if (passwordChoice === 'Yes' || passwordChoice === 'yes' || passwordChoice === 'Y' || passwordChoice === 'y'){
    passwordLength = randomLength();
} else if (passwordChoice === 'No' || passwordChoice === 'no' || passwordChoice === 'N' || passwordChoice === 'n'){
    passwordLength = prompt(`Choose your desired password length. Enter a number between ${minNum}-${maxNum}: `);
} else {
    throw new Error('Please select Yes or No for random password length')
}

In addition to allowing the user to decide if they want a password of random length, or input their length of choice, the code includes a safeguard in case the user fails to improperly answer the question, throwing an error to indicate as much.

We also need to institute safeguards for inputting the password length. Inputs in Node.js are all received as strings, even numbers. So we need to convert any input to a number. However, there also needs to be a safeguard to notify the user if a number was not inputted to begin with.

if (!isNaN(passwordLength)) passwordLength = Number(passwordLength);
if(typeof passwordLength !== 'number') throw new Error('Invalid input type. Please enter a number.');

With that taken care of, we also need safeguards for the length of the password, given that a minimum and maximum password length have already been established, an error needs to be generated if the user requests a password length outside of that establised range.

if(passwordLength > maxNum || passwordLength < minNum) throw new Error(`Please enter a number between ${minNum}-${maxNum}.`)

Now that we have established what the password length will be and put up safeguards to defend against erroneous entries, we need to allow the user to determine if they want to include special characters in their password.

To accomplish this, we will create another prompt asking this and include or exclude the array containing special characters from the totalCharsChoice variable depending on their choice. As with the password length, we also need safeguards here to protect against erroneous inputs.

let specialCharsChoice = prompt(`Would you like your password to include special characters?: `);
if(specialCharsChoice == 'Yes' || specialCharsChoice == 'yes' || specialCharsChoice == 'Y' || specialCharsChoice == 'y') {
    totalCharacterList = [...alphabetCharacters, ...uppercaseAlphabetCharacters, ...numbers, ...specialChars];
} else if(specialCharsChoice === 'No' || specialCharsChoice === 'N' || specialCharsChoice === 'n') {
    totalCharacterList = [...alphabetCharacters, ...uppercaseAlphabetCharacters, ...numbers];
} else {
    throw new Error('Please enter Yes or No for Special Characters!')
}

Okay, so far all of the code we have written has been somewhat ancillary. How do we actually generate a random password from the given character set? We will employ the Fisher-Yates shuffle algorithm. How exactly it works is beyond the scope of this tutorial, but definitely click the link to read more.

Here is what our implementation will look like.

for (let i = totalCharacterList.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [totalCharacterList[i], totalCharacterList[j]] = [totalCharacterList[j], totalCharacterList[i]];
  }

This shuffles the contents of the totalCharacterList array, from which we will use to create the password from. Which brings us to the final obstacle to tackle in order to generate the password. The maximum size of the totalCharacterList array is 77, 15 special characters, 10 numbers, and 52 letters(26 uppercase and 26 lowercase).

As a result, obviously the maximum password length would be 77 characters, and our password can be any combination of the characters in the array. Because the array is already shuffled, we can avoid needless complication for the purposes of this tutorial and use the first 'x' elements of the totalCharacterList array as the password. Where 'x' is the already determined password length.

This is easily accomplished as such:

return totalCharacterList.join('').slice(0, passwordLength)

And that's it! We have built a random password generator, but more importantly walked through the steps and thought process of building it with an eye on customization and robustness as previously stated.

It is worthwhile to note that while the passwords generated by this application can be relatively secure, it would not be considered cryptographically secure. This could be accomplished by utilizing the crypto module in tandem with the randomBytes() method.

Feel free to check out the code in its entirety here.

Did you find this article valuable?

Support Clint by becoming a sponsor. Any amount is appreciated!