Santa's head elf is one of those old-school dudes who creates passwords from the top of his head instead of using a password manager.
The board of elves has asked us to create a password generator to help the head elf come up with unique and secure passwords.
And they came to the right place!
You can find the complete puzzle here.
Thinking about the problem
Before we can dive into the solution, let's see what we have to work with.
There are two parameters the function should take:
- Length: The length of the password
- Options: Certain options the password should include, see below:
The options are as follows:
-
lowercase
: Lowercase letters (a-z) -
uppercase
: Uppercase letters (A-Z) -
numbers
: Numbers (0-9) -
specialCharacters
: Special characters (only!@#$%^&*()
)
Knowing this, we should be able to help the elves.
Two side notes are essential and will help us:
- We should throw an error if no options are passed
- When the length of the option is longer than the length we should also throw an error
Creating a JavaScript password generator
Alright, let's get right into it.
The first thing I did, check the two errors we should throw.
Since the options are an object, and we want to check the length, I've converted it with Object.keys
.
This will convert it into an array.
const optionKeys = Object.keys(options);
if (!optionKeys.length) throw Error('NOT_ENOUGH_OPTIONS');
if (length < optionKeys.length) throw Error('PASSWORD_TOO_SHORT');
That will make sure the errors are thrown when needed.
Then I've decided to create a new object with the option values.
const optionValues = {
lowercase: 'abcdefghijklmnopqrstuvwxyz',
numbers: '0123456789',
specialCharacters: '!@#$%^&*()',
get uppercase() {
return this.lowercase.toUpperCase();
},
};
You can see all the alphabetic characters defined in the lowercase property, all numbers, and the special characters.
For the uppercase version, I've decided to use a function to leverage our existing input.
Note: The keys here match the option keys we should support.
Since we want to mix a random password with the options, I want to loop over each option and get a random number from that option.
We can check the main password length with a basic while loop like so.
let password = '';
while (password.length < length) {
// password += 'SOMETHING';
}
This will loop until the length of the password is long enough.
As mentioned above, I want to use an equal number of options for each password.
So I've decided to use a for...of
loop. I've chosen this particular loop because we can break out of it.
We need to break out of it because we could push too many letters.
For example, we want to generate a 3 character password with 2 options.
The while loop will fire 2 times, and the options will loop 2 times as well, meaning we get a 4 character password.
while (password.length < length) {
for (let option of optionKeys) {
if (password.length >= length) break;
// Add a character
}
}
As you can see, I break the loop if we hit the length inside the for loop.
Now we just need to grab a random character for the current looped option and add it to the password.
password += optionValues[option][Math.floor(Math.random() * optionValues[option].length)];
Some things to note:
- optionValues[option] refers to our option value object, and picks the current option
-
Math.floor(Math.random() * optionValues[option].length)
picks a random item from the current option array
With this in place we finished our function, so it looks like this:
export const generatePassword = (length, options = {}) => {
const optionKeys = Object.keys(options);
if (!optionKeys.length) throw Error('NOT_ENOUGH_OPTIONS');
if (length < optionKeys.length) throw Error('PASSWORD_TOO_SHORT');
let password = '';
while (password.length < length) {
for (let option of optionKeys) {
if (password.length >= length) break;
password += optionValues[option][Math.floor(Math.random() * optionValues[option].length)];
}
}
return password;
};
One last thing, the test should turn green.
And yes, we did it!
I'm always looking forward to hearing what you would have done differently and why.
Thank you for reading, and let's connect!
Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter
Top comments (4)
First of all: don't jeopardize the security of Santa and his elves by using Math.random() to generate passwords. Use
crypto.getRandomValues(typedArray)
instead. Also, do not linearly cycle the options, as this reduces the randomness again; instead just make sure that each option is used once and afterwards, select the option randomly. Then, there's another small issue in your first check, as options could also contain{ numbers: false }
.Now we can rest ye merry, gentlemen, as Santa's secrets are very secure.
Nice one!
What's the bad thing about using Math.random?
Do like your super randomness function, smart to use the remained for that.
I think I hit a use-case when trying that where a option would not be added.
So thanks for this solution, great idea to make sure at least the options are set once and then re-shuffle for extra randomness.
Math.random is not random enough, it's just a seeded pseudo-random number generator. So if an attacker knows your code, they can reduce a lot of the possible combinations they would need to try. The same goes for cycling the options, just even worse so, because instead of 72 possible characters for each positions, there would now be only 26 or even 10 left.
They should really rename that function 😂
Yeah I've taken in account it would not be the most "random" version because of the looping method always being the same.
Thanks Alex 👏