How to create a strongly random password generator

OpenJavaScript 0

Last updated: September 23, 2022.

Using the global crypto.getRandomValues() method, it is possible to create a strongly random password generator in JavaScript.

Ironically, selecting characters randomly using crypto.getRandomValues() achieves a result that is closer to true randomness than the commonly used Math.random() method.

In this tutorial, we cover how to create a random password generator function that produces new passwords to a specified length from a string of available characters.

Table of contents

Creating the generator

Setting the characters

To create the generator, you’ll need to specify the characters that can appear in the passwords in produces.

We’ll be using this string below. But you can customize this without the need for any additional code as we’ll be detecting its length dynamically.

const chars = "0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ";

Generating random values

Now for the trickiest parts to code.

To use crypto.getRandomValues(), you need to first create a new typed array. In case you are not familiar with this concept, a typed array is an array-like object that is optimized for reading and writing data quickly.

In this case, we’ll create an 32-bit unsigned array (unsigned means that only positive values can exist in the array).

You do this by creating a new instance of the Uint32Arrayobject with the length of the array to create passed in:

const length = 8;
const array = new Uint32Array(length); // Creates a new unsigned 32-bit array

Now you can assign random values to the array like this:

window.crypto.getRandomValues(array); // Assigns random values to the array

Selecting characters

To select characters to include in the password, we’ll create a loop that runs its code block as many times as there should be characters in the password.

Inside each loop, a new character is assigned to password.

This is selected by calculating the remainder of the current value in the array (a random value) divided by the length of the character string. The remainder cannot exceed the length of the character string.

This random remainder value is used to select the index value of the character to add to the password.

const chars = "0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const length = 8;
let password = "";

const array = new Uint32Array(length);
window.crypto.getRandomValues(array);

for (let i = 0; i < length; i++) {
  password += chars[array[i] % chars.length]; // % operator returns remainder of division
}

Result: A reusable password generator function

With just a little editing, you can create a reusable password generator function from the code above in which the length of the password is specified dynamically each time you call the function:

function getPassword(length) {
  const chars = "0123456789abcdefghijklmnopqrstuvwxyz!@#$%^&*()ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  let password = "";
  const array = new Uint32Array(length);
  window.crypto.getRandomValues(array);
  for (let i = 0; i < length; i++) {
    password += chars[array[i] % chars.length]; // % operator returns remainder of division
  }
  return password;
}
console.log(getPassword(5)); // e.g. "z4EYZ"
console.log(getPassword(10)); // e.g. "Mf6XW6utTG"

Extension: Copying password to the clipboard

After a password has been generated, you'll probably want to provide the user with an easy way of copying it to their clipboard.

Here's some code for that:

function copyToClipboard(text) {
  navigator.clipboard.writeText(text)
  .then(res => alert(`Copied "${text}" successfully!`)) // triggered if copied successfully
  .catch(err => alert("Copy failed: " + err)) // triggered if fails and prints information about error
}
 
copyToClipboard("Password");

What about Math.random()?

In the example above crypto.getRandomValues() is used to assign values to a 32-bit unsigned array. These values are used to randomly select characters for the password.

It would also be possible to use Math.random() to create the random values. But this is not recommended because, ironically, the values it produces are not that random.

In fact, Math.random() was never intended to be used to generate securely random values. It was created in the first version of JavaScript for lightweight scripting purposes (e.g. selecting random content to display). For this, 'as good as random' was sufficient for this purpose.

But, when generating a password, the degree of randomness should be as high as possible. Therefore, it is best not to rely on Math.random().

Instead, for tasks where the degree of randomness should be high, the more recently developed crypto.getRandomValues(), which creates cryptographically secure random values (i.e. it's algorithm is complex enough that the values it generates are hard to predict), should be used.

Related links: