As hacking and phishing scams become more advanced, many developers are taking extra precautions to protect their users. One example is implementing a random password generator, which prompts a user to select a completely randomized password when registering on a site or application, reducing the chances of hackers guessing or stealing login credentials.
In this article, we’ll build our own command-line Node.js random password generator. Our program will generate a random password of a specified length and character composition. After the password is generated, it will automatically be copied to your clipboard, meaning it’s ready for use. We’ll also review how to automatically save your generated password to a text file.
This tutorial is very beginner-friendly, so even if you’re brand new to Node.js, you should be able to follow along. We’ll use the Commander.js package, which enables our program to accept the different commands that we’ll type into the command line. You’ll also need clipboardy for automatically copying your password to your clipboard immediately after it is generated.
You can follow along with the full code for this tutorial at the repository. Let’s get started!
First, you need to create an empty folder; you can name it whatever you want. Then, open it with VS Code.
Run npm init
to create a package.json
file. The package name will be the same as the folder name, which is smartpassword
for me. Eventually, we’ll create a symbolic link to run our program using a command with this name:
Next, we’ll install the required dependencies for our project, Commander.js and clipboardy:
npm i commander clipboardy
From here, simply create the main file, index.js
, then head to your package.json
file. Under main: index.js
, add type:module
. Afterward, import Commander.js into your program with the following code:
import { Command } from "commander";
You can take the const program = new Command();
program variable and run some methods on it, including version number, description, options, required commands, etc. When running your program, if you use either the -h
or –help
option, you’ll see the different options available for your program. Let’s go ahead and add a few options to our application.
Instead of showing you how to run every single option, we’ll run through a few examples so you get the general idea.
length
optionFirst, let’s start with the option to specify the length
of the password. For this, you’ll need only the following code:
program.option('-l, --length <number>', 'length of password', '8')
The command above creates a length
option. The first part, -l, --length <number>
takes care of the name of the option in full, length
, the alias, l
, and the required variable, number
.
The second part, length of password
is the description of the option, while the third part, 8
, is the default value that will be passed in as the length of the password if you don’t specify one.
When you console log program.opts()
, the program will spit out an object with the length property of value 8
. If you run the Node.js index command with an option of length <number>
, say node index -l 12
, it will spit out an object with the length property of value 12
. -l
is an alias of the –length
option, so this is similar to node index –length 12
.
save
optionTo add a save
option, you’ll use the same syntax, except save
will not take in a value. It is more like a boolean with a default value of false
. If you include it in the running command, then the save
value will be saved as true
:
program.option('-s, --save', 'save the password to secrets.txt')
numbers
and symbols
optionsNow, we’ll add options to include numbers
and symbols
in your random password. Keep in mind that these will be boolean values. That being said, the syntax is largely the same, but with one small tweak:
program.option("-nn , --no-numbers", "password to not include numbers") program.option("-ns , --no-symbols", "password to not include symbols")
Notice that in these two commands, the –no
part prefixes the options. The default value for any boolean property is false
, but you want your passwords to include these by default except for when specified by the user, right? The –no
part reverses the boolean value, flipping it to true
.
That covers all the options required for the program. Keep in mind that you’ll require those values when you create the password generation logic. For that, you actually need to destructure that object and the values:
const { length, save, numbers, symbols } = program.opts()
So far, your file should like the following code:
// initializing the project with the different options program .version("1.0.0") .description("simple random secure password generator") .option("-l , --length <number>", "length of password", "8") .option("-s , --save", "save the password to secrets.txt") .option("-nn , --no-numbers", "password to not include numbers") .option("-ns , --no-symbols", "password to not include symbols") .parse(); const { length, save, numbers, symbols } = program.opts();
With everything laid out nicely, let’s create the function that actually generates the random password. My password creation function, createPassword
, will take in length
, numbers
, and symbols
as arguments within the function:
const generatedPassword = createPassword(length, numbers, symbols)
Now, let’s create variables for numbers
, symbols
, and characters
for you to use in the function:
const alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const integers = "0123456789"; const exCharacters = "!@#$%^&*_-=+"; const createPassword = (length, hasNumbers, hasSymbols) => { let chars = alpha; if (hasNumbers) { chars += integers; } if (hasSymbols) { chars += exCharacters; } return generatePassword(length, chars); };
If you console.log(generatedPassword)
, you’ll see that you have a long password with every character, symbol, and number included. You’ll need to create another function that uses the length
parameter to slice the password down to the required length with proper randomization. For this, I used the following code snippet:
const generatePassword = (length, chars) => { let password = ""; for (let i = 0; i < length; i++) { password += chars.charAt(Math.floor(Math.random() * chars.length)); } return password; };
Now, if you run the code above, you’ll get your desired password with length
, randomization
, numbers
, and characters
specified or not.
To copy the generated password into your clipboard, you need only import clipboardy using the command below:
import clipboard from "clipboardy";
After your password has been generated, add the following code:
clipboard.writeSync(generatedPassword);
To save your password to the file, you can create the following function in the same file, like so:
const savePassword = (password) => { fs.open(path.join(__dirname, "/", "passwords.txt"), "a", 777, (e, id) => { fs.write(id, password + os.EOL, null, "utf-8", () => { fs.close(id, () => { console.log("Password saved!!"); }); }); }); };
For this snippet to run successfully, you need to import the fs
, os
, and path
modules:
import os from "os"; import * as fs from "fs"; import * as path from "path"; import { dirname } from "path"; import { fileURLToPath } from "url";
The only thing left to do is to create a symbolic link. We need to edit the package.json
file yet again. Right under "main":"index.js"
, add "preferGlobal": true
, then add "bin":"/index.js"
.
In the index.js
file, add the following command above your existing code:
#!/usr/bin/env node
You should see that you’re able to launch your project without Node.js. You simply need to type in your project name and the different options that you have available for it. Your final code should look somewhat like the following:
#!/usr/bin/env node import clipboard from "clipboardy"; import { Command } from "commander"; import os from "os"; import * as fs from "fs"; import * as path from "path"; import { dirname } from "path"; import { fileURLToPath } from "url"; const program = new Command(); const __dirname = dirname(fileURLToPath(import.meta.url)); // initializing the project with the different options program .version("1.0.0") .description("simple random secure password generator") .option("-l , --length <number>", "length of password", "8") .option("-s , --save", "save the password to secrets.txt") .option("-nn , --no-numbers", "password to not include numbers") .option("-ns , --no-symbols", "password to not include symbols") .parse(); // this function creates the password default const { length, save, numbers, symbols } = program.opts(); const alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const integers = "0123456789"; const exCharacters = "!@#$%^&*_-=+"; const createPassword = (length, hasNumbers, hasSymbols) => { let chars = alpha; if (hasNumbers) { chars += integers; } if (hasSymbols) { chars += exCharacters; } return generatePassword(length, chars); }; // this function formats our password to however you need const generatePassword = (length, chars) => { let password = ""; for (let i = 0; i < length; i++) { password += chars.charAt(Math.floor(Math.random() * chars.length)); } return password; }; // this function saves the password if you have used the -save option. const savePassword = (password) => { fs.open(path.join(__dirname, "/", "passwords.txt"), "a", 777, (e, id) => { fs.write(id, password + os.EOL, null, "utf-8", () => { fs.close(id, () => { console.log("Password saved!!"); }); }); }); }; // getitng the generated password. const generatedPassword = createPassword(length, numbers, symbols); if (save) { savePassword(generatedPassword); } //this functions copies your code to your clipborad ready for use clipboard.writeSync(generatedPassword); console.log(generatedPassword);
Just like that, you’ve built your own, secure, random password generator. I hope that this article has been helpful and you’ll try it out for yourself.
Feel free to fork the repository and build on it. I’d suggest adding a feature to save your passwords in a database or even having the program email your passwords. If you have an improvement you think can be useful, feel free to contact me to collaborate, and if you have any questions, please leave a comment.
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowReact Native’s New Architecture offers significant performance advantages. In this article, you’ll explore synchronous and asynchronous rendering in React Native through practical use cases.
Build scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
One Reply to "Build a random password generator in Node.js"
`Math.random()` is not a cryptographically secure random number generator, a threat actor could reverse engineer the RNG process and derive the password.
It is not recommended to use the Math.random(), instead use something like `crypto.getRandomValues()` as described here: https://nodejs.org/api/webcrypto.html#webcrypto_class_crypto