Published Jun 17, 202413 min read
Next.js 2FA with Google Authenticator: Setup Guide

Next.js 2FA with Google Authenticator: Setup Guide

Two-factor authentication (2FA) adds an extra layer of security to your Next.js application by requiring users to provide a one-time code from an authenticator app like Google Authenticator, in addition to their password. This prevents unauthorized access even if a password is compromised.

Benefits of Using Google Authenticator 2FA:

  • Enhanced Security: Protect user accounts and sensitive data from password-based attacks

  • Improved User Experience: Provide a seamless 2FA experience without relying on SMS or hardware tokens

  • Increased Trust: Demonstrate your commitment to security, boosting user confidence and adoption

Setting Up Google Authenticator 2FA in Next.js:

  1. Generate a unique 2FA secret for each user

  2. Store the encrypted 2FA secrets securely in your database

  3. Create a QR code for users to set up Google Authenticator

  4. Verify the user-provided 2FA token against the stored secret

  5. Update your login flow to require 2FA verification

Key Steps:

  • Install required packages: speakeasy and qrcode

  • Generate a unique 2FA secret for each user using speakeasy

  • Encrypt and store the 2FA secret in your database

  • Create a QR code for Google Authenticator setup using qrcode

  • Verify the user-provided 2FA token against the stored secret with speakeasy

  • Update your login flow to require 2FA verification for sensitive areas

  • Allow users to enable or disable 2FA from their account page

  • Thoroughly test the 2FA flow and deployment process

Pros and Cons of Google Authenticator:

Pros Cons
Free and easy to set up Requires user setup and device management
Time-based one-time passwords Potential for lost or compromised devices
Cross-platform compatibility Reliance on a single authenticator app
No need for SMS or voice calls Limited customization options

Implementing 2FA with Google Authenticator is a valuable step in fortifying your Next.js application's security. However, balance robust security measures with a seamless user experience, and continuously evaluate and update your strategies to meet evolving user needs and threats.

Requirements

To set up Google Authenticator 2FA in your Next.js application, you'll need:

  1. Node.js (v12 or later) and npm (Node Package Manager) installed. Download Node.js from https://nodejs.org.

  2. Next.js (v12 or later) installed globally or as a local dependency. Create a new Next.js project with:

npx create-next-app my-app
  1. speakeasy package for generating and verifying 2FA secrets and tokens:
npm install speakeasy
  1. qrcode package for generating QR codes for the Google Authenticator app:
npm install qrcode
  1. A database to store user information and 2FA secrets (e.g., MongoDB, PostgreSQL, MySQL).

  2. Environment variables for storing sensitive data securely. Create a .env.local file in the project root and access variables with process.env.

With these prerequisites, you'll configure your Next.js app to integrate Google Authenticator 2FA. This involves:

  • Generating and storing 2FA secrets

  • Creating QR codes

  • Verifying user-provided tokens

  • Updating the login flow for 2FA verification

Setting Up the Project

Create a New Project

To get started, you can either create a new Next.js project or use an existing one. If you're starting fresh, run this command to create a new project:

npx create-next-app my-app

This will set up a new Next.js project in the my-app directory with the necessary files and dependencies.

If you already have a Next.js project, you can skip this step.

Install Required Packages

Next, install the required packages for implementing 2FA with Google Authenticator:

npm install speakeasy qrcode
  • speakeasy provides utilities for generating and verifying 2FA secrets and tokens.

  • qrcode allows you to generate QR codes for the Google Authenticator app.

With your project set up and the necessary packages installed, you're ready to integrate Google Authenticator 2FA into your Next.js application.

Creating a Unique 2FA Secret

What is a 2FA Secret?

Two-factor authentication (2FA) adds an extra security step to the login process. It requires a second form of verification, like a one-time code, in addition to your password. This code is generated by an authenticator app like Google Authenticator.

The core of this 2FA process is a secret key shared between the application and the authenticator app. This secret is used to generate the one-time codes that you must provide during login. Keeping this secret secure is crucial, as anyone with access to it can generate valid codes and potentially gain unauthorized access.

Generating a Unique Secret for Each User

For maximum security, each user should have a unique 2FA secret. This prevents a compromised secret from affecting multiple accounts. In Next.js, you can use the speakeasy library to generate a unique secret for each user:

import speakeasy from 'speakeasy';

// Generate a new secret for the user
const secret = speakeasy.generateSecret({ length: 20 });

// The secret.base32 property contains the generated secret key
console.log(secret.base32);

The speakeasy.generateSecret() function generates a new random secret key. The length option specifies the desired length of the secret (20 characters in this example). The generated secret is returned as an object, with the base32 property containing the secret key in Base32 format, which is compatible with Google Authenticator.

Important
To ensure maximum security, it's crucial to store this secret safely, as discussed in the next section.

Storing the 2FA Secret Securely

Keeping Secrets Safe

Protecting 2FA secrets is crucial for maintaining a secure authentication system. If a secret is compromised, the entire 2FA process becomes ineffective. Follow these best practices to safeguard these sensitive keys:

  1. Encrypt Secrets: Before storing 2FA secrets in your database, encrypt them using strong encryption standards like AES (Advanced Encryption Standard). This ensures that even if a data breach occurs, the encrypted secrets remain unreadable without the decryption key.

  2. Secure Key Storage: Store the decryption keys securely in your environment variables, separate from your codebase. Consider using a secrets manager, especially when deploying across multiple servers or environments, to handle these crucial keys.

  3. Database Security: Fortify your database where the encrypted 2FA secrets reside. Implement network isolation, firewalls, and regular security audits to keep your data store secure. Regularly update your database and its management tools to patch any vulnerabilities.

  4. Isolated Microservice (Optional): For an added layer of security, consider creating an isolated microservice dedicated solely to handling 2FA secrets. This service can generate, encrypt, and store the secrets in a separate key-value database, accessible only via an API. This way, even if your main application is compromised, the 2FA secrets remain isolated and secure.

Update Database Schema

To store the 2FA secret and the user's 2FA status, you'll need to update your database schema. Here's an example of how you might structure the relevant fields in a PostgreSQL database:

Column Type Description
id SERIAL PRIMARY KEY Unique identifier for each user
username VARCHAR(255) UNIQUE NOT NULL User's unique username
password VARCHAR(255) NOT NULL User's password (encrypted)
two_factor_secret VARCHAR(255) Encrypted 2FA secret for the user
two_factor_enabled BOOLEAN DEFAULT FALSE Indicates if 2FA is enabled for the user

When a user enables 2FA, generate a unique secret and store it in the two_factor_secret column after encryption. Update the two_factor_enabled column to TRUE to indicate that 2FA is active for that user.

Generating the QR Code

What is a QR Code?

A QR (Quick Response) code is a type of barcode that can store information like website URLs, text, or other data. When scanned with a smartphone camera or QR code reader, the encoded information is quickly accessed and displayed.

Using QR Codes with Google Authenticator

Google Authenticator

Google Authenticator is an app that generates one-time passwords (OTPs) for two-factor authentication (2FA). To set up an account in Google Authenticator, you need to scan a QR code containing your account's unique secret key. This QR code links your Next.js app with the user's Authenticator app, enabling seamless 2FA integration.

Creating the QR Code

To generate a QR code for Google Authenticator, you'll use the qrcode library along with the 2FA secret you generated earlier. Here's how:

1. Install the qrcode package:

npm install qrcode

2. Create a new API route file, e.g., /app/api/2fa/qrcode/route.ts, and import the required libraries:

import QRCode from "qrcode";
import speakeasy from "speakeasy";

3. Define a GET route handler that generates a new 2FA secret and creates a QR code from it:

export async function GET(): Promise<Response> {
  const secret = speakeasy.generateSecret({ name: "My Next.js App" });
  const data = await QRCode.toDataURL(secret.otpauth_url as string);

  return Response.json({
    data,
    secret: secret.base32,
  });
}

In this code:

  • speakeasy.generateSecret() generates a new 2FA secret with a custom app name.

  • qrcode.toDataURL() converts the secret into a base64-encoded data URI representing the QR code image.

  • The data URI and the base32-encoded secret are returned to the client.

4. On the client-side, you can render the QR code using an <img> tag with the data URI as the source:

<img src={qrCodeDataUri} alt="Google Authenticator QR Code" />
Step Description
1 Install the qrcode package
2 Create a new API route file and import required libraries
3 Define a GET route handler to generate the 2FA secret and QR code
4 Render the QR code on the client-side using the data URI
sbb-itb-1aa3684

Displaying the QR Code to Users

QR Code Component

To show the QR code to users, create a new React component that fetches the QR code data from the API route:

import React, { useState, useEffect } from 'react';

const QRCodeDisplay = () => {
  const [qrCodeData, setQrCodeData] = useState(null);

  useEffect(() => {
    const fetchQRCode = async () => {
      const response = await fetch('/api/2fa/qrcode');
      const data = await response.json();
      setQrCodeData(data.data);
    };

    fetchQRCode();
  }, []);

  return (
    <div>
      {qrCodeData ? (
        <img src={qrCodeData} alt="Google Authenticator QR Code" />
      ) : (
        <p>Loading QR code...</p>
      )}
    </div>
  );
};

export default QRCodeDisplay;

This component uses hooks to fetch the QR code data from the /api/2fa/qrcode API route when it mounts. It then renders an <img> tag with the QR code data URI as the source.

You can import and use this component wherever you want to display the QR code to users.

User Setup Instructions

Provide clear instructions to guide users through setting up Google Authenticator:

  1. Download the App: Ask the user to download the Google Authenticator app on their mobile device from the app store.

  2. Open the App: After installing, the user should open the app and select the option to add a new account or scan a QR code.

  3. Scan the QR Code: Display the QR code component, and ask the user to scan the QR code using the Google Authenticator app.

  4. Verify Setup: Once the QR code is scanned, the app will display a six-digit code that changes every 30 seconds. Prompt the user to enter this code in your application to verify the setup.

  5. Enable 2FA: After successful verification, enable two-factor authentication for the user's account.

Present these steps as a numbered list or create a separate component with step-by-step instructions. You can also provide visual aids, such as screenshots or animated GIFs, to make the setup process more user-friendly.

Verifying the 2FA Token

To ensure secure access, your Next.js app needs to verify the 2FA token provided by the user. This process confirms that the user has the correct authenticator app or device generating the one-time passwords (OTPs) based on the shared secret.

Here's how it works:

  1. The user enters their username and password.

  2. The app prompts the user to enter the current 2FA token from their authenticator app (e.g., Google Authenticator).

  3. The app retrieves the user's stored 2FA secret from the database.

  4. Using the secret and the current time, the app generates an expected 2FA token.

  5. The app compares the user-provided token with the expected token.

  6. If the tokens match, the user is authenticated and granted access. If not, access is denied.

This verification step ensures that only users with physical possession of the authenticator device or app can successfully complete the 2FA authentication.

Using Speakeasy for Verification

Speakeasy

In Next.js, you can use the speakeasy library to verify the user-provided 2FA token against the stored secret:

import speakeasy from 'speakeasy';

// Assuming you have the user's stored secret and the user-provided token
const userSecret = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; // Replace with the actual stored secret
const userToken = '123456'; // Replace with the user-provided token

// Verify the token
const verified = speakeasy.totp.verify({
  secret: userSecret,
  encoding: 'base32',
  token: userToken,
  window: 2 // Increase the window size if necessary
});

if (verified) {
  // Token is valid, grant access
  console.log('2FA token verified successfully');
} else {
  // Token is invalid, deny access
  console.log('Invalid 2FA token');
}

The speakeasy.totp.verify() function takes the following arguments:

Argument Description
secret The user's stored 2FA secret.
encoding The encoding format of the secret (typically 'base32').
token The user-provided 2FA token.
window (optional) The allowed time window for token verification, in case the user's device time is slightly out of sync. A value of 2 means the token will be valid for the current time step and one time step before and after.

The function returns true if the provided token matches the expected token generated from the secret, and false otherwise.

You can integrate this verification step into your login flow, ensuring that users must provide a valid 2FA token in addition to their regular credentials to gain access to your application.

Testing and Deployment

Testing the 2FA Flow

1. Enable 2FA for a Test User

  • Log in with a test user account

  • Go to the 2FA setup page and turn on 2FA

  • Scan the QR code or enter the provided secret key into your authenticator app

2. Verify 2FA Code

  • Try to access an area that requires 2FA verification

  • Enter the 2FA code from your authenticator app

  • Confirm you gain access after successful verification

3. Test Failed Verification

  • Enter an incorrect 2FA code

  • Verify that access is denied with an error message

4. Disable and Re-enable 2FA

  • Turn off 2FA for the test user account

  • Try to access a protected area and confirm no 2FA is needed

  • Turn 2FA back on and repeat the verification process

5. Test Edge Cases

  • Verify behavior when the 2FA code expires or is outside the valid window

  • Test with multiple user accounts and different authentication scenarios

Deployment Considerations

1. Secure Storage of 2FA Secrets

Task Description
Encrypt 2FA Secrets Ensure 2FA secrets are encrypted before storing in the database.
Access Controls Implement strict controls for decrypting and accessing secrets.

2. Monitoring and Logging

  • Set up monitoring for 2FA-related events, such as failed verification attempts.

  • Implement logging for auditing and troubleshooting purposes.

3. Load Testing

  • Conduct load testing to ensure the 2FA verification process can handle high traffic.

  • Identify and address any performance issues.

4. Disaster Recovery

  • Regularly back up encrypted 2FA secrets.

  • Have a plan for handling security breaches or data loss.

5. User Communication

  • Provide clear documentation and support resources for users enabling 2FA.

  • Establish a process for handling lost or compromised 2FA devices.

Summary

Two-factor authentication (2FA) with Google Authenticator adds an extra security layer to your Next.js application, preventing unauthorized access. This guide covered the key steps:

  • Generating a unique secret key

  • Creating a QR code for user setup

  • Verifying one-time tokens

  • Updating the login flow

While 2FA greatly improves security, it's crucial to handle sensitive data carefully:

  • Encrypt 2FA secrets before storage

  • Implement strict access controls

  • Have a disaster recovery plan

Thoroughly test your implementation, including load testing, to ensure a smooth user experience and identify potential issues.

Moving forward, consider additional security measures like biometric authentication, single sign-on (SSO), and multi-factor authentication (MFA). Regularly review and update your security strategies to maintain user trust.

Google Authenticator Pros Google Authenticator Cons
Extra security with 2FA Requires user setup and device management
Time-based one-time passwords Potential for lost or compromised devices
No SMS or voice calls needed Reliance on a single authenticator app
Cross-platform compatibility Limited customization options

Implementing 2FA with Google Authenticator is a valuable step in fortifying your application's security. However, balance robust security measures with a seamless user experience. Continuously evaluate and update your security strategies to meet evolving user needs and threats.

Pros and Cons of Using Google Authenticator

Pros Cons
Adds an extra security layer with two-factor authentication (2FA) Requires users to set up and manage the authenticator app on their devices
Uses time-based one-time passwords, eliminating the need for SMS or voice calls Potential for lost or compromised devices, leading to account access issues
Compatible with both iOS and Android devices Relies on a single authenticator app, lacking redundancy options
Free and easy to set up Limited customization options compared to other authenticator apps
No need to share personal information like phone numbers Some users may find the setup process cumbersome or confusing
Offers backup and recovery options for migrating accounts Potential usability challenges for users with multiple accounts

Related posts