How to Implement Two-Factor Authentication using Node.js: Step-by-Step Guide

Posted by

Ever worried about hackers cracking your online accounts? Two-factor authentication (2FA) is your secret weapon for an extra layer of security. This step-by-step guide explains how to easily implement 2FA in your Node.js applications using Google Authenticator, adding powerful protection for your users and giving you peace of mind. Get ready to boost your app’s security and user confidence!

Two-factor-authentication-in-node.js
Two-factor-authentication-in-node.js

While this post delves deep into Node.js, the core concepts of 2FA apply across all programming languages. Many popular authenticator apps are available, like Twilio Authy, Microsoft Authenticator, and Google Authenticator. For the sake of this tutorial, we will use Google Authenticator and go over what 2FA is, how it secures your applications and step-by-step instructions on how to implement it.


What is Two-Factor Authentication (2FA)?

Two-factor authentication, or 2FA, is a security mechanism that requires users to provide two different factors to verify their identity. These factors typically fall into three categories:

  1. Something you know (e.g. Password)
  2. Something you have (e.g. Mobile device)
  3. Something you are (e.g. Biometrics)

Beyond the usual username and password combinations, 2FA adds another level of security. Even if an attacker can get your password or guess it, they won’t be able to access your account without the second factor.


Implementing 2FA with Google Authenticator in Node.js

Let’s discuss how to integrate 2FA in your Node.js application using the Google Authenticator app. Building a sturdy foundation is key! Before crafting the code, let’s explore the crucial steps involved in 2FA.

  1. Generate a Secret Key: Generate a unique secret key for each user during user registration.
  2. Create a QR Code: Use the secret key to create a QR code that users can scan with their Google Authenticator app.
  3. User Setup: In your application, provide users with the QR code or the secret key. Users should scan the QR code with the Google Authenticator app.
  4. Generate OTP Codes: On the server, use the secret key to generate Time-Based One-Time Passwords (TOTP). These codes change every 30 seconds.
  5. User Authentication: During login, users must enter the current OTP generated by their Google Authenticator app.
  6. Verification: Verify the user-entered OTP with the server-generated OTP. If they match, the user is authenticated.

Now comes the fun part 🙂 we are going to convert all the above steps into code.

Prerequisites

Before you start implementing two-way authentication, make sure you have the following prerequisites:

  1. Basic Knowledge of Javascript or Typescript.
  2. Basic Understanding of API Design and CRUD Pattern.
  3. The latest version of Node Js is installed on the system.
  4. Basic understanding of Node.js and Express.

Step 1: Setting Up Your Node.js Project

To begin, Create a new directory for your project, navigate to it in your terminal, initialize the new node js project, and install the necessary packages. Try the following commands in the command line.

mkdir node_2fa
cd node_2fa
npm init -y
npm install express otpauth hi-base32
  • express – A Node.js web framework
  • otpauth – A library for generating and validating TOTP
  • hi-base32 – This library provides encoding and decoding functions for converting data to and from the Base32 format. We’ll use the base32-encoded string to generate the TOTP in an Authenticator app.

Step 2: Setting Up Express Server

Create an index.jsfile in your project and set up a basic express server.

onst express = require('express');
const app = express();
const port = 3000;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  res.send('Two Factor Authentication Example');
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Step 3: User Registration

In this step, we’ll implement user registration and securely store user information. While databases are typically used for this purpose, I’ll use an in-memory array in this example for simplicity. For a comprehensive example using MongoDB, check out my GitHub repo. https://github.com/Nik720/2fa-nodejs

const users = [];

// Endpoint to register a new user
app.post('/register', (req, res) => {
  const { username, password } = req.body;
  const id = users.length + 1;

  // Store user information securely, including the user's password
  users.push({ id, username, password });
  res.status(201).send({
    status: "success",
    message: "User created successfully"
  });
});

Step 4: Enable Two-Way Authentication

Let’s now use the otpauth library to construct two-way authentication. Creating a secret key, showing a QR code that the user may scan with an authenticator app, and validating the one-time code are the steps involved in this process.

const OTPAuth = require("otpauth");
const encode = require("hi-base32");
const QRCode = require('qrcode');

// Endpoint to enable two-way authentication
app.post('/enable-2fa', (req, res) => {
  const { username } = req.body;

  // Find the user by username (you should use a database here)
  const user = users.find((u) => u.username === username);

  if (!user) {
    return res.status(404).send('User not found');
  }

  // Generate a secret key for the user
  const base32_secret = generateBase32Secret();
  user.secret = base32_secret;

  // Generate a QR code URL for the user to scan
  let totp = new OTPAuth.TOTP({
      issuer: "YourSite.com",
      label: "YourSite",
      algorithm: "SHA1",
      digits: 6,
      secret: base32_secret,
  });

  let otpauth_url = totp.toString();

  // Generate and send the QR code as a response
  QRCode.toDataURL(otpauth_url, (err) => {
        if(err) {
            return res.status(500).json({
                status: 'fail',
                message: "Error while generating QR Code"
            })
        }
        res.json({
            status: "success",
            data: {
                qrCodeUrl: qrUrl,
                secret: base32_secret
            }
        })
    })
});

const generateBase32Secret = () => {
  const buffer = crypto.randomBytes(15);
  const base32 = encode(buffer).replace(/=/g, "").substring(0, 24);
  return base32;
};

GenerateBase32Secret() We developed the method in the above code snippet to produce a secret key encoded in base32. This key is an essential component of the TOTP creation mechanism. In addition, we assigned the OTPAuth.TOTP class to the totpvariable after instantiating it with the required arguments.

Next, we used the totp.toString() function to get the TOTP Auth URL. The encoded secrets and necessary configurations for creating a QR code and configuring TOTP authentication in an application are available at this URL. Additionally, to guarantee their accessibility in the future, we stored the base32 secret string in the user object.

The final step involved using the QRcode library to create a QR Code with otpauth_url and returning the QR Code URL and a base32 secret string in the JSON response. Returning these values gives the client flexibility and ease in the client-side implementation. It enables it to produce the relevant TOTP tokens using either the base32 secret string or by scanning the QR code using the authenticator application.

Before proceeding to the last phase of validating authentication, we must first configure an authenticator app to produce a 6-digit code that will be supplied as a token in the next step.

There are a few important steps to activating the Google Authenticator apps, whether on a mobile phone or via a Chrome extension. For convenience, I am using the Chrome extension here. It is necessary to manually enter the base64 secret given by the API or scan a QR code to add an account.

Step 5: Verify Two-Way Authentication

Finally, implement the endpoint to verify the one-time code provided by the user. In this step, you have to pass 6 6-digit code generated by the authenticator app to verify your identity.

// Endpoint to verify the two-way authentication code
app.post('/verify-2fa', (req, res) => {
  const { username, token } = req.body;

  // Find the user by username
  const user = users.find((u) => u.username === username);

  if (!user) {
    return res.status(404).send('User not found');
  }

  // Verify the token
  let totp = new OTPAuth.TOTP({
      issuer: "YourSite.com",
      label: "YourSite",
      algorithm: "SHA1",
      digits: 6,
      secret: user.secret!,
   });

  let delta = totp.validate({ token });

  if(delta) {
        res.json({
            status: "success",
            message: "Authentication successful"
        })
    } else {
        res.status(401).json({
            status: "fail",
            message: "Authentication failed"
        }) 
    }
});

This post covers the basic setup and usage of 2FA. Although I’ve demonstrated user existence checks here, keep in mind that practical 2FA systems typically verify login status before allowing sensitive actions.

I’ve built a comprehensive example using a MongoDB database, incorporating all necessary API endpoints and middleware. For a complete overview, please visit my GitHub repo. https://github.com/Nik720/2fa-nodejs

Conclusion

By applying the techniques outlined in this post, such as QR code-based token creation and secure OTP verification, you may create a powerful shield against unauthorized access and provide unparalleled protection to your Node.js users. Build your 2FA system today to take the first step towards a safer digital space!


Enjoyed this post?

Haven’t you read my Rate Limitting post yet? See how to Secure your app from attackers at https://codeshakti.com/lightweight-powerful-node-js-load-balancer/

If you found this article helpful, share it with others and subscribe for more insightful tech content straight to your inbox!

I appreciate your support. 💚 Thanks for reading! 🙏

Happy Coding… 😁

Discover more from code shakti

Subscribe now to keep reading and get access to the full archive.

Continue reading