Some sites auto generates complex passwords during user signup.
I found this cool and wanted to try building a similar feature.
Instead of turning it into an app, I decided to create a public API, so that anyone could use this in their applications.
Features:
- The password by default should have only lower case letters.
- Users can select if they want to add uppercase letters, numbers and characters to it.
- Users can select the password length
- Password should be random with no predictable pattern
So grab your popcorns, this is going to be a detailed one.
For you all super curious folks out there, here's the link :)
Now, on with the tutorial.
Tech requirements
- Nodejs installed
- Express js
- Cors
- dotenv
The usual server stuff
- Create an empty package.json file inside the directory with npm init
- Next, install dependencies.
We need express to handle the routes, cors to allow cross-origin access and dotenv to access our environment variables.
npm i express dotenv cors
Require all the dependencies and your index.js should look like this:
const express = require('express');
const app = express();
const cors = require('cors');
dotenv.configure();
//Defining port
const PORT = process.env.PORT | 5000;
//listen to the server event on PORT
app.listen(PORT)
//Add routes
app.get('/generate', (req, res) => {
//we will come back here later
}
The password generating algorithm
To avoid cluttering, we'll write the main algorithm in a separate file.
Go ahead. Create a file and name it whatever you want.
I’ll create a wrapper function ( generate-password ) and then export it. This function takes in 4 parameters and will return us the password.
Parameters:
- Length of password (default 8)
- Caps required (default false)
- Number required (default false)
- Special character required ( default false )
Inside this function, I’ll create 4 different functions that generates lower case, upper case, number and a special character respectively. We will use these functions later.
//generate number
let generateNumber = () => {
let number = Math.floor(9*Math.random());
return number.toString();
};
//generate a special character
let generateChar = () => {
const charArray = [64, 38, 35, 37, 36, 42, 43];
let index = Math.floor(7*Math.random());
return String.fromCharCode(charArray[index]);
};
//generate capital letters
let generateCaps = ()=>{
return String.fromCharCode(Math.floor(25 * Math.random() + 65));
};
//generate small case letters
let generateSMall = ()=>{
return String.fromCharCode(Math.floor(25 * Math.random() + 97))
};
Now, to generate the whole password, we create a new function that takes in the length of the password required as the parameter.
Next, create a global array and set it to empty. We will push the password into this.
We will set the max-number of upper-case letters or special characters or numbers to (password-length)/3. The rest would be small case alphabets.
The password will contain numbers, uppercase letters or special chars only if the users specify it, if not we go with the default false.
Let's create 3 if statements. These will push number, sp.char or uppercase to the password array, depending on the wrapper function parameters.
//generate the whole pass
let generate = (len) => {
let turn = Math.floor(len/3);
var rem = len;
if(charRequired){
let temp = Math.floor(turn * Math.random() + 1);
for(i=0; i<temp; i++){
passwordArray.push(generateChar());
}
rem = rem - temp;
}
if(numRequired){
let temp = Math.floor(turn * Math.random() + 1);
for(i=0; i<temp; i++){
passwordArray.push(generateNumber());
}
rem = rem - temp;
}
if(capsRequired){
let temp = Math.floor(turn * Math.random() + 1);
for(i=0; i<temp; i++){
passwordArray.push(generateCaps());
}
rem = rem - temp;
}
for(i=0; i<rem; i++){
passwordArray.push(generateSMall());
}
return passwordArray;
};
Note that, we just only set the upper limit of the type of character pushed to the password array. The actual value is decided by the Math.random() method.
This pretty much gives us the password, but it's not random enough. Look closely, there's a pattern. The password will always have sp.char, number, uppercase and lowercase in the same order.
Let’s add one more layer of randomness to it.
I made use of a standard shuffling algo to jumble the array.
//shuffle Array
function shuffleArray(arr){
for (let i = arr.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
We then convert this into string with array.join()
and return it.
Back to index.js
Let’s add a basic routing.
I’m using a get method on the “/generate” route.
Remember that we had 4 parameters for the password generating function? We get values to that using query parameters.
Destructure caps, num, char, len from req.parameters object.
Invoke the function with the above password and send the password generated.
app.get('/generate', cors(), (req, res)=>{
let {caps, num, char, len} = req.query;
let passtoSend = password(num, char, len, caps);
res.status(200).json({ data : passtoSend}).end();
})
That’s all.
The API we just built is accessible at basehost/generate
You can add query parameters for additional complexity.
What next?
You can either build a similar backend API or a front end that fetches this API to generate passwords for users.
You can read about accessing the API here.
If you found this useful, consider connecting with me on twitter.
See you in the next one!
Top comments (3)
NEVER EVER use Math.random for anything secure.
Hey. Thanks for the insights. What other method should I be using?
It really depends on what version of node you're using.
crypto.randomInt
is safest, but only on a few versions.Most portable is
crypto.randomBytes
, but there's some subtleties on using that you have to be careful of. When coercing that to your index, you must not use the modulo operator, since you'll bias the results. If something is too large, just toss it out. Probably the most efficient is to mask it down to the nearest set of all 1s and then toss out anything too large (i.e. for the numbers use& 0xf
, then throw out anything over 9 and try again)The shuffle probably isn't secure either, but I have trouble convincing myself it'll actually be a problem. They'll probably be somewhat easier to crack, but long enough passwords and it won't matter. The secure way is to make the password, and if it doesn't meet the qualifications, throw it out, or change some of the characters (picked at a cryptographicaly random position). One thing you learn from doing security enough though, if you have a hunch that something isn't secure, you're probably right.