How to create a Time-based One-Time Password (TOTP) in Next.js
Learn how to implement a Next.js login page with Time-based One-Time Passwords (TOTPs), in this tutorial.
Vincent
Created: December 30, 2024
Updated: January 15, 2025
Our mission is to make the Internet a safer place, and the new login standard passkeys provides a superior solution to achieve that. That's why we want to help you understand passkeys and its characteristics better.
1. Introduction#
Next.js is a powerful framework that allows developers to build fast and user-friendly web applications. One of the most critical aspects of any web application is user authentication.
In this guide, we'll walk you through the process of implementing a login page in Next.js, with Time-based One-Time Password (TOTP) authentication.
If you want to see the complete code and check out other authenciation methods, use our Next.js login page repository on GitHub. There you will find information regarding:
TOTP, or Time-based One-Time Password, is a popular method for two-factor authentication (2FA). It enhances security by requiring users to enter a unique, time-sensitive code. This code changes every 30 seconds, making it highly secure against interception and replay attacks
In this section, we'll explore how to implement a TOTP authentication in your Next.js application.
The following steps cover the implementation of the TOTP-based authentication:
Set Up TOTP-Based Authentication Project
Installing the Relevant Dependencies
Defining the TOTP Schema
Implement API Route For Generating the TOTP Secret and QR Code
In this section, we'll explain how to set up TOTP-based authentication for your Next.js project. We'll break down the specific files and structure. Let's start with an overview of the relevant directory structure and the key files involved:
src/models/Totp.ts: This file defines the Mongoose schema for TOTP. It includes fields for email, secret, and TOTP-based authentication status.
src/pages/api/auth/totp/generate.ts: This API endpoint generates a TOTP secret and a corresponding QR code for the user to scan with their authenticator app.
src/pages/api/auth/totp/status.ts: This API endpoint checks whether TOTP-based authentication is enabled for a given user.
src/pages/api/auth/totp/verify.ts: This API endpoint verifies the TOTP entered by the user.
src/app/totp/page.tsx: This is the frontend component that handles the user interface for TOTP-based authentication. It allows users to login, generate a QR code, and verify their TOTP.
3.2 Installing the Relevant Dependencies#
To set up TOTP-based authentication in your Next.js project, you'll need a few essential dependencies. Let's go through the installation and purpose of each one.
speakeasy is a library for generating and verifying one-time passcodes, specifically TOTP in our case. It will be used to handle the generation and verification of TOTP codes.
qrcode is a library to generate QR codes. This will be used to create a QR code that users can scan with their authenticator app to set up TOTP.
You can install all the dependencies using the following command:
3.3 Defining the TOTP Schema#
File Location: src/models/Totp.ts
Purpose: This file defines the schema for storing TOTP-related data in your MongoDB database using Mongoose. This schema includes the email, secret, and two-factor authentication status of the user. It ensures that each user's TOTP information is stored securely and uniquely.
Explanation:
Email: Stores the user's email and ensures it's unique.
secret: Stores the TOTP secret key.
totpEnabled: Indicates whether TOTP is enabled for the user.
Here is a high-level description of the totps collection relevant for the TOTP-based authentication method:
Here's the Totp.ts file:
3.4 Implement API Route For Generating the TOTP Secret and QR Code#
Purpose: This file defines an API route in Next.js for generating a TOTP secret and a corresponding QR code. This API endpoint is called when a user sets up TOTP-based authentication, providing them with a secret key and QR code to scan with their authenticator app.
Explanation:
Connect to Database: Uses a helper function connectDb to connect to the MongoDB database.
Generate Secret: Uses speakeasy to generate a TOTP secret key.
Find User: Checks if the user already exists and if TOTP-based authentication is already enabled.
Generate QR Code: Uses qrcode to generate a QR code from the TOTP secret key's URL.
Update Database: Stores the TOTP secret and sets totpEnabled to false for the user in the database.
Response: Returns the TOTP secret and QR code as a JSON response if successful.
Here's the implementation of the generate.ts file:
3.5 Implement API Route For Checking the TOTP Authentication Status#
File Location: src/pages/api/auth/totp/status.ts
Purpose: This file defines an API route in Next.js for checking the status of TOTP for a user. This endpoint is used to determine whether TOTP is enabled for a given email address, providing necessary information to the frontend to guide user interactions.
Explanation:
Connect to Database: Uses a helper function connectDb to connect to the MongoDB database.
Extract Email: Retrieves the user's email from the request body.
Find User: Looks up the user in the Totp collection by email.
Check TOTP Status Determines if TOTP is enabled for the user.
Response: Returns the status of totpEnabled as a JSON response if the user is found, otherwise returns an error message.
Here's the implementation of the status.ts file:
3.6 Implement API Route For Verifying the TOTP#
File Location: src/pages/api/auth/totp/verify.ts
Purpose: This file defines an API route in Next.js for verifying the TOTP (Time-based One-Time Password) entered by the user. This endpoint is called when the user submits their TOTP code during the login or verification process, ensuring that the code is correct and enabling two-factor authentication if it is.
Explanation:
Connect to Database: Uses a helper function connectDb to connect to the MongoDB database.
Extract Email and Token: Retrieves the user's email and TOTP token from the request body.
Find User: Looks up the user in the TOTP collection by email.
Verify TOTP: Uses speakeasy to verify the TOTP token against the stored secret key.
Update TOTP Status: If the token is verified, updates the totpEnabled status to true for the user in the database.
Response: Returns the verification status as a JSON response.
Here's the implementation of the verify.ts file:
3.7 Creating the TOTP Component#
File Location: src/app/totp/page.tsx
Purpose: This file defines the component for managing TOTP-based authentication. This component handles user interactions for logging in, generating a TOTP QR code, and verifying the TOTP code. It ensures users can easily set up and verify their TOTP for enhanced security.
Explanation:
State Management: Uses useState to manage state variables such as email, QR code, token, verification status, errors, and TOTP status.
Effect Hook: Uses useEffect to check the TOTP status whenever the email changes.
handleLogin function: Validates email input and sets the logged-in state.
generateQrCode function: Fetches the QR code from the backend when the user opts to set up TOTP.
verifyToken function: Verifies the entered TOTP token by calling the backend API.
UI Component: Renders different UI elements based on the authentication status and interactions.
Here’s the complete code for the TOTP component:
3.8 Testing the TOTP-based authentication Flow#
In this section, we will walk through the process of testing the TOTP-based authentication flow in your Next.js application. We'll specify the routes, outline the steps involved, and provide detailed descriptions for each step along with relevant screenshots.
Route: http://localhost:3000/totp
Steps for Setting Up TOTP for a New User
Navigate to the TOTP Page
Open your browser and navigate to http://localhost:3000/totp
You should see the login page with an email input field and a Login with TOTP button.
Enter Your Email and Log In
Enter your email address in the email input field.
Click the Login with TOTP button.
Generate QR Code for TOTP Setup
Click the Generate QR Code button.
Scan the QR Code with an Authenticator App
A QR code will be displayed on the screen.
Open your authenticator app (e.g., Google Authenticator) and scan the QR code.
After scanning the QR code, your authenticator app will generate a TOTP code.
Enter this code into the input field provided on the page.
Click the Verify Code button.
Verification and Enabling TOTP
If the TOTP code entered is correct, you will see a message indicating that TOTP authentication is enabled. A record will be saved to the TOTP collection in MongoDB indicating that TOTP is enabled for the user.
A Logout button will be displayed.
Steps for Logging in with TOTP
Navigate to the TOTP Page
Enter the TOTP Code from the Authenticator App
If TOTP authentication is already enabled for this email, you will not see the option to generate a QR code. Instead, you will be prompted to enter the TOTP code directly.
Open your authenticator app and retrieve the current TOTP code.
Enter this code into the input field provided on the page.
Click the "Verify Code" button.
Verification
If the TOTP code entered is correct, you will see a message indicating successful verification. Otherwise, you will see an error message.
If the TOTP is verified, a Logout button will be displayed.
You have successfully set up TOTP-based authentication in your Next.js application.
4. Recommendation for Next.js Authentication & Login Pages#
Event though we just looked at how to roll out TOTP authencication, which is not the safest auth method, building a secure and user-friendly authentication system is crucial to protect user data and provide a great first impression of your app. Authentication is often the first interaction a user has with your product and the first thing they will complain about if something goes wrong. Besides the detailed steps laid out above, we also have some additional best practices for Next.js authentication and login pages:
Don’t Roll out Your Own Auth: Use libraries and packages for tested security solutions and to save implementation time. Libraries like NextAuth.js or Auth0 are excellent choices as they offer robust and secure authentication solutions out of the box.
Implement Multi-Factor Authentication (MFA): Combine authentication methods to achieve MFA, e.g., password plus TOTP to add an extra layer of security.
Validate Email Addresses: Use APIs to check if email addresses are valid and belong to real users. Avoid allowing one-time or disposable email addresses to prevent spam and ensure genuine user engagement.
Prevent SMS Pumping Attacks: Implement rate limits on SMS verifications and restrict access to certain areas to prevent SMS pumping attacks. Use services that can help identify and block fraudulent requests.
Limit Login Attempts: To prevent brute force attacks, implement rate limiting on authentication routes. Temporarily block IP addresses after a set number of failed login attempts to protect against automated attacks.
Never Store Passwords in Plain Text: Ensure that passwords are stored securely using hashing algorithms like bcrypt. Never store passwords in plain text.
Implement Proper Session Management: Authentication is only the first step. After successfully authenticating your users, ensure you have proper session management in place to prevent session hijacking.
Use HTTPS Everywhere: Always use HTTPS to encrypt data in transit. This protects user credentials and other sensitive data from being intercepted by attackers.
Monitor and Log Authentication Events: Keep track of authentication events and monitor them for suspicious activities. Implement logging and alerting to quickly detect and respond to potential security threats.
By following these practices, you'll keep your application robust against common threats and ensure a safe environment for your users. Secure authentication not only protects your users but also builds trust and credibility for your application.
5. Conclusion#
In this Next.js login page guide, we explored various authentication methods to secure your Next.js applications. Here’s a recap of what we covered:
TOTP (via authenticator app): We implemented TOTP-based authentication by generating and verifying TOTP secrets, creating QR codes for easy setup, and building front-end components to handle the TOTP process.
Choosing the right authentication method for your application depends on various factors, including security requirements, user convenience, and the nature of your application. Each method we covered has its strengths and can be used alone or in combination to provide a robust authentication system. Experiment with different methods, gather user feedback, and iterate on your implementation to achieve the best balance for your specific needs.
Table of Contents
Enjoyed this read?
🤝 Join our Passkeys Community
Share passkeys implementation tips and get support to free the world from passwords.