It would be interesting if we had a method in JavaScript that would automatically build passwords. Something like a built-in makePassword() method in vanilla JS. There isn’t. But, it’s not a big deal: we can easily whip up our own password generator in vanilla JS. This tutorial shows you how to do it, step by step, with each step explained in detail.

Password security

The requirement for our password is simple:

  • It needs to be longer than 8 characters
  • It needs to have uppercase letters
  • It needs to have lowercase letters
  • It needs to have numbers

Let’s start with a quick way to generate a password:

Math.random().toString(32)

The above code will return, for example:

0.orrq36ai09

But, that’s a decimal. So how do we get rid of the first two characters? Easily, using the slice() method.

The slice method works like this: we pass it a number X, and it removes X characters from the left of the string.

For example:

'0.1'.slice(2); // returns '1'
'..1'.slice(2); // returns '1'

The slice() method doesn’t “care” that there is a 0 and a . in our three-character strings above. It just takes the first two characters and removes them. Thus, if we pass it 0.orrq36ai09, it will remove the first two characters:

'0.orrq36ai09'.slice(2); // returns: 'orrq36ai09'

To reiterate, if we did just:

Math.random().toString();

… we’d get something like:

0.4250184826863386

With addition of slice(2), we’d get:

4250184826863386

And of course, we can pass the number value to toString() method for various number systems:

  • 8 for octal
  • 16 for hexadecimal
  • 36 for alphadecimal

What if we pass the number 2 to the toString() method?

Math.random().toString(2);

We’ll get back a binary number result:

"0.0010111011110000111111101010001101011110000010011111"

The toString() method accepts a range of numbers from 2 to 36 as its parameters.

Let’s go back to our random password generator.

1
2
3
4
5
6
7
let pw = Math.random().toString(36).slice(2);
console.log(pw);
/*
returns:

mba01xdkkcg
*/

What if we wanted to make it more random? Could we add some uppercase characters?

Here’s a quick way to do it:

1
2
3
4
5
6
7
let pw2 = Math.random().toString(36).toUpperCase().slice(2);
console.log(pw2);
/* 
returns:

J0KJN3QPZ5
*/

Now, how do we make it so that we have a combination of lower and upper characters? We can combine these two arrays, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
let pw = Math.random().toString(36).slice(2);
pw = [...pw];
console.log(pw);

let pw2 = Math.random().toString(36).toUpperCase().slice(2);
pw2 = [...pw2];
console.log(pw2);

for(let i = 0; i < pw.length; i+=2) {
    pw.splice(i,1, pw2[i] );
}

console.log(pw);

Here’s the result of the above code:

(11) ["9", "c", "w", "x", "2", "q", "c", "g", "i", "q", "8"]
(10) ["K", "D", "D", "Y", "L", "E", "V", "L", "L", "G"]
(11) ["K", "c", "D", "x", "L", "q", "V", "g", "L", "q", undefined]

Now we have the issue of the length of these arrays resulting in undefined being added to the resulting array. To solve that, as usual, we can take many approaches, but here’s an interesting one. We’ll use the Array.prototype.length property to make arrays equal length:

pw.length = pw2.length;

This will immediately trim the extra members from the array that has an extra member. Let’s try our code again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let pw = Math.random().toString(36).slice(2);
pw = [...pw];
console.log(pw);

let pw2 = Math.random().toString(36).toUpperCase().slice(2);
pw2 = [...pw2];
console.log(pw2);

if (pw.length > pw2.length) {
    pw.length = pw2.length;
} else {
    pw2.length = pw.length;
}

console.log('pw length, pw2 length:', pw.length, pw2.length);

for(let i = 0; i < pw.length; i+=2) {
    pw.splice(i,1, pw2[i] );
}

console.log(pw);

Here’s a sample output now:

(11) ["a", "1", "w", "9", "n", "m", "i", "5", "d", "q", "c"]
(11) ["5", "0", "K", "K", "5", "T", "7", "I", "7", "J", "V"]
pw length, pw2 length: 11 11
(11) ["5", "1", "K", "9", "5", "m", "7", "5", "7", "q", "V"]

Here’s another sample output:

(11) ["a", "q", "a", "2", "2", "f", "5", "j", "6", "d", "5"]
(10) ["I", "6", "A", "C", "R", "Z", "J", "J", "8", "8"]
pw length, pw2 length: 10 10
(10) ["I", "q", "A", "2", "R", "f", "J", "j", "8", "d"]

Our code is almost completed now. All we need to do is join the array back into a string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let pw = Math.random().toString(36).slice(2);
pw = [...pw];
console.log(pw);

let pw2 = Math.random().toString(36).toUpperCase().slice(2);
pw2 = [...pw2];
console.log(pw2);

if (pw.length > pw2.length) {
    pw.length = pw2.length;
} else {
    pw2.length = pw.length;
}

console.log('pw length, pw2 length:', pw.length, pw2.length);

for(let i = 0; i < pw.length; i+=2) {
    pw.splice(i,1, pw2[i] );
}

pw = pw.join("");

console.log(pw);

Here’s a sample resulting pasword:

X5Um2uS6Nk



Note:
This exercise comes from Book 3 of my book series on JS, available on Leanpub.com.