DEV Community

fawazsullia
fawazsullia

Posted on • Originally published at blog.fawazsullia.com

Build a Password Generator API on Nodejs

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:

  1. The password by default should have only lower case letters.
  2. Users can select if they want to add uppercase letters, numbers and characters to it.
  3. Users can select the password length
  4. 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
}
Enter fullscreen mode Exit fullscreen mode

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();
};
Enter fullscreen mode Exit fullscreen mode
//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]);
};
Enter fullscreen mode Exit fullscreen mode
//generate capital letters

let generateCaps = ()=>{
return String.fromCharCode(Math.floor(25 * Math.random() + 65));
};
Enter fullscreen mode Exit fullscreen mode
//generate small case letters

let generateSMall = ()=>{
return String.fromCharCode(Math.floor(25 * Math.random() + 97))
};
Enter fullscreen mode Exit fullscreen mode

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;

};
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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();
})
Enter fullscreen mode Exit fullscreen mode

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!

Oldest comments (3)

Collapse
 
sqlrob profile image
Robert Myers

NEVER EVER use Math.random for anything secure.

Collapse
 
fawazsullia profile image
fawazsullia

Hey. Thanks for the insights. What other method should I be using?

Collapse
 
sqlrob profile image
Robert Myers

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.