DEV Community

Cover image for SET UP 2FA IN NODE.JS WITH SPEAKEASY
Candie
Candie

Posted on

SET UP 2FA IN NODE.JS WITH SPEAKEASY

Adding 2FA to your application create an extra layer of security for your users, to protect their detail from scammers and hackers.

Passwords do not completely secure your application, infact, if the only layer of security on your application is password, your application is very vulnerable to scammers and hackers, because 6 in 10 people use the same password across all their accounts and platform, so access to the password, places the user at extreme risk if there's absence of an extra layer of security.

2FA is an extra layer of security to make sure only authorized personnel gain access to certain resources.

2FA might be in the form of security questions & answers, access token, biometric authentication and tokens.

So quickly we are going to be walking through 2FA in node.js using the speakeasy library

REQUIREMENTS

Basic understanding of Javascript

Speakeasy is a onetime passcode generator that supports Google authenticator and other two factor devices. more info

STEP 1

initialize your project by running

npm init
Enter fullscreen mode Exit fullscreen mode

STEP 2

Run this command to install needed dependencies

npm install express mongoose dotenv body-parser speakeasy 
Enter fullscreen mode Exit fullscreen mode

STEP 3

Create an app.js file and then create an express app

const express = require("express");
const dotenv = require("dotenv").config();
const bodyParser = require("body-parser");

const app = express();

app.use(bodyParser.json());
app.use(express.json());

const port = process.env.PORT || 3000;

app.listen(port, () => console.log(`server up and running at port ${port}`));
Enter fullscreen mode Exit fullscreen mode

STEP 4

Connect your express app to mongodb using the mongoose package installed earlier.

NOTE: Add your database connection string and password to the .env file

const express = require("express");
const dotenv = require("dotenv").config();
const bodyParser = require("body-parser");
const mongoose = require("mongoose");

const app = express();

// middlewares
app.use(bodyParser);
app.use(express.json());

// connect to database
const db = process.env.DATABASE;

mongoose
  .connect(db, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => console.log("database connection successful"));

const port = process.env.PORT || 3000;

app.listen(port, () => console.log(`server up and running at port ${port}`));
Enter fullscreen mode Exit fullscreen mode

STEP 5

install nodemon globally to monitor changes in the app.js file

npm install -g nodemon
Enter fullscreen mode Exit fullscreen mode

then add this command to the scripts object inside your package.json file

  "scripts": {
    "start": "nodemon app.js"
  },
Enter fullscreen mode Exit fullscreen mode

STEP 6

Open your terminal and npm start

npm start
Enter fullscreen mode Exit fullscreen mode

if you get something like this, then you're good to go, if not go through the codes again, probably you are making a mistake somewhere

[nodemon] 2.0.20
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node app.js`
server up and running at port 5000
database connection successful

Enter fullscreen mode Exit fullscreen mode

STEP 7

Create a model folder, and inside create a token-model.js file

This file will be used to structure and store the secret string gotten from speakeasy

inside the token-model.js file, paste in the following codes

const mongoose = require("mongoose");

const tokenSchema = new mongoose.Schema(
  {
    secret: String,
    authIsSet: {
      type: Boolean,
      default: false,
    },
  },
  {
    toJSON: { virtuals: true },
    toObject: { virtuals: true },
  }
);

module.exports = mongoose.model("Token", tokenSchema);
Enter fullscreen mode Exit fullscreen mode

STEP 8

Create a controller folder, and inside create an authentication-controller.js file

Inside this file, import the Token model from the token-model.js file, import speakeasy and create a simple controller function

const Token = require("../model/token-model");
const speakeasy = require("speakeasy");

exports.generateSecret = async (req, res, next) => {};
Enter fullscreen mode Exit fullscreen mode

STEP 9

Our 2FA process follows 3 flow

  • Generate a secret key

  • Generate a set-up key and paste in google authenticator app

  • Authenticate the token for the first time

GENERATE SECRET KEY

Run this command to generate a secret key, and save it to mongodb

const Token = require("../model/token-model");
const speakeasy = require("speakeasy");

exports.generateSecret = async (req, res, next) => {
  const secretToken = speakeasy.generateSecret();
  try {
    const token = await Token.create({ secret: secretToken.base32 });
    return res.status(201).json({
      status: "success",
      message: "secret token generated",
      token: secretToken.base32
      tokenId: token.id,
    });
  } catch (error) {
    return res.status(400).json({
      status: "error",
      message: error.message || "something went wrong",
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

GENERATE A SET-UP KEY

Generate a set-up key by pasting the secretToken.base32 returned from calling the speakeasy.generateSecret() command string inside your authenticator app

STEP 10

Create a router folder and inside, create a token-router.js file and import express and create a simple router.

const express = require("express");
const authenticationController = require("../controller/authentication-controller");

const router = express.Router();

router.post("/generate-secret", authenticationController.generateSecret);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

import the router into your app.js file and register the route

app.use("/auth", authRouter);
Enter fullscreen mode Exit fullscreen mode

STEP 11

Now go to postman and fire this endpoint
http://localhost:5000/auth/generate-secret

You should get this response

{
    "status": "success",
    "message": "secret token generated",
    "token": "H4XE22DMGZCD6NCHGJKC6JDSMFJV2T3EHF4DCUTJNNTGGYKWKNIQ"
    "tokenId": "68r77hdfbnnmdnmfdju8",
}
Enter fullscreen mode Exit fullscreen mode

STEP 12

Now copy and paste the token inside your google authenticator app, then you are ready to proceed to the final step, which is verifying the token for the first time.

VERIFY THE TOKEN

Now we want to make sure the token on the client and server match.
After entering your set-up key, then enter the token you get to verify against the secret string that was generated in step 9.

Let us create a verifyToken function inside our controller and start the verification process

exports.verifyToken = async (req, res, next) => {
  const secretToken = await Token.findById(req.params.id);
  const enteredToken = req.body.token;

  const verified = speakeasy.totp.verify({
    secret: secretToken.token.secret,
    encoding: "base32",
    token: enteredToken,
  });
  if (!verified) {
    return res.status(403).json({
      message: "verification failed",
    });
  }
await secretToken.authIsSet = true

  return res.status(200).json({
    status: "success",
    message: "verification successful",
    verified: verified,
  });
};
Enter fullscreen mode Exit fullscreen mode

STEP 13

Create a verify-token route to fire this request

router.post("/verify-token/:id", authenticationController.verifyToken);
Enter fullscreen mode Exit fullscreen mode

if after firing this endpoint, you get this response, then you have successfully added 2FA to your application!.
CONGRATULATIONS

{
    "status": "success",
    "message": "verification successful",
    "verified": true
}
Enter fullscreen mode Exit fullscreen mode

I hope you found this write up helpful!.
Checkout my codes here github

Let me know your thoughts in the comment section

Top comments (4)

Collapse
 
eskabore profile image
Jean-Luc KABORE-TURQUIN

Hello @Candi,

As a DEV.to moderator on #React and #Node, I wanted to take a moment to welcome you to our community.
I've noticed that you've been writing articles for a while now, and I wanted to acknowledge your progress and commitment to sharing your knowledge with others.
Your contributions to the community have not gone unnoticed, and we are grateful for your efforts.

Please continue to share your expertise with us, and don't hesitate to reach out if you have any questions or concerns.
We're all here to support and help each other grow as developers.

Thanks again for being a part of our community.

Jean-Luc

Collapse
 
candie_code profile image
Candie

Thank you very much.
The gesture is much appreciated

Collapse
 
thomasbnt profile image
Thomas Bnt ☕

Hello ! Don't hesitate to put colors on your codeblock like this example for have to have a better understanding of your code 😎

console.log('Hello world!');
Enter fullscreen mode Exit fullscreen mode

Example of how to add colors and syntax in codeblocks

Collapse
 
candie_code profile image
Candie

Thank you very much