Frequently Asked Questions
Implement 2FA by integrating NextAuth.js for authentication, Plivo for SMS delivery, and Prisma for database management. This setup enables a secure login flow where users verify their identity with an OTP sent via SMS after entering their credentials, enhancing account security.
Plivo is the communications platform API used to send SMS messages containing the One-Time Passwords (OTPs) for two-factor authentication. It's a key component for delivering the verification codes to users' phones.
NextAuth.js uses JSON Web Tokens (JWT) to manage session state and store authentication information, including whether 2FA has been verified in the current session. This approach enhances security by encoding user data and verification status within the token itself.
Use SMS-based OTP authentication to enhance security when users log in with credentials. After initial login, if 2FA is enabled, an OTP is required before granting full access to protected resources like a dashboard.
Yes, Prisma supports multiple database providers including PostgreSQL, MySQL, and SQLite. While the example uses PostgreSQL, you can adapt the schema and database URL in `schema.prisma` and `.env` to your preferred database.
Configure the Plivo Node.js client by setting environment variables (`PLIVO_AUTH_ID`, `PLIVO_AUTH_TOKEN`, `PLIVO_PHONE_NUMBER`) with your Plivo credentials. The `sendSms` utility function in `plivo.ts` then handles sending SMS messages using these credentials.
Hashing the OTP with bcrypt before storing it in the database significantly enhances security by preventing the storage of sensitive information in plain text. Only the hash is stored, and the submitted OTP is compared against the hash for verification.
The `verifyOtp` utility function handles OTP expiry by comparing the current time to the stored `twoFactorExpiry` timestamp. If expired, it clears the OTP data and returns false, prompting the user to request a new code.
The provided code uses `next-auth@beta`, specifically for compatibility with the Next.js App Router and Server Actions. The beta version supports the latest features and conventions of the App Router.
The `middleware.ts` file configures route protection by checking the `twoFactorVerified` status within the session. If 2FA is enabled but not verified, the middleware redirects to the /verify-otp page.
The `twoFactorVerified` flag in both the session and JWT indicates whether a user has successfully completed 2FA for the current session. This ensures protected routes are only accessible after both credential and OTP verification.
The `generateOtp` utility function uses the `otp-generator` library to create secure, numeric OTPs of a specified length (default is 6 digits). This function ensures the randomness and format of the codes.
Two configurations (`auth.config.ts`, `auth.ts`) are used to accommodate Next.js middleware. `auth.config.ts` contains minimal callbacks and settings needed for early checks before full initialization in `auth.ts`.
If Plivo credentials are missing during development, the `sendSms` function logs a warning and simulates sending the SMS instead of throwing an error. This allows development to proceed without actual SMS delivery.
This guide provides a step-by-step walkthrough for adding robust SMS-based Two-Factor Authentication (2FA) or One-Time Password (OTP) verification to your Next.js application using NextAuth.js for session management and Plivo for reliable SMS delivery.
We'll build a secure authentication flow where users log in with credentials (email/password) and, if 2FA is enabled, must verify their identity using a code sent via SMS before gaining full access. This enhances account security significantly. We will cover setting up the project, integrating Plivo, modifying the NextAuth flow, handling OTP logic, managing user 2FA settings, and deploying the application.
Technologies Used:
create-next-app
).System Architecture:
Prerequisites:
shadcn/ui
for UI components, or adapt examples to your preferred library.Final Outcome:
A Next.js application where users can:
1. Setting up the Project
Let's initialize a new Next.js project and install the necessary dependencies.
1.1 Create Next.js App:
Open your terminal and run:
This command scaffolds a new Next.js project using the App Router, TypeScript, Tailwind CSS, and ESLint.
1.2 Install Dependencies:
Install NextAuth, Prisma, the Plivo SDK, and utility libraries:
next-auth@beta
: Core library for authentication. (Use beta for App Router compatibility).@auth/prisma-adapter
: NextAuth adapter for Prisma.prisma
: ORM for database interaction.plivo-node
: Official Plivo Node.js SDK.bcrypt
: Library for hashing passwords.zod
: Schema validation library.otp-generator
: Utility to generate secure OTPs.@types/*
: TypeScript definitions for development.1.3 Initialize Prisma:
Set up Prisma for database management:
This creates:
prisma/schema.prisma
: Your database schema definition file..env
: An environment file (Prisma addsDATABASE_URL
).1.4 Configure Environment Variables:
Open the
.env
file created in the root of your project and add the following variables. Obtain the Plivo credentials from your Plivo Console (Dashboard -> API Keys & Credentials).Explanation:
DATABASE_URL
: Connection string for your database. Adjust based on your provider.NEXTAUTH_SECRET
: A random string used to encrypt JWTs and session cookies. Crucial for security. Generate a strong one and keep it secret.NEXTAUTH_URL
: The canonical URL of your application. Required by NextAuth, especially for callbacks and redirects.PLIVO_AUTH_ID
/TOKEN
: Your Plivo API credentials. Keep these secret.PLIVO_PHONE_NUMBER
: The Plivo number used to send SMS.OTP_EXPIRY_MINUTES
: Defines the validity period for generated OTPs.1.5 Define Database Schema:
Update
prisma/schema.prisma
to include fields required by NextAuth, Prisma Adapter, and our custom 2FA logic.Key Changes:
Account
,Session
,User
,VerificationToken
).password
field toUser
for the Credentials provider.User
:phoneNumber
: To store the user's validated phone number for sending OTPs.twoFactorEnabled
: Boolean flag to track if 2FA is active for the user.twoFactorSecret
: To store the hashed OTP temporarily during verification. Do not store the plain OTP.twoFactorExpiry
: Timestamp indicating when the current OTP expires.twoFactorVerified
field toSession
: This flag within the active session indicates if the second factor has been successfully verified during the current login attempt.1.6 Apply Database Migrations:
Generate and apply the initial database migration:
This command:
schema.prisma
.1.7 Project Structure Overview:
Your
src
directory should look something like this:This structure separates concerns, placing authentication pages in an
(auth)
group and protected pages in a(protected)
group, managed by middleware and layouts.2. Implementing Core Functionality (Authentication Flow)
Now, let's configure NextAuth, set up the Plivo client, and implement the core login, registration, and OTP verification logic.
2.1 Configure Prisma Client:
Create a singleton instance of the Prisma client.
2.2 Configure Plivo Client:
Set up the Plivo client using credentials from environment variables.
2.3 Configure NextAuth.js:
We need two configuration files for NextAuth v5:
auth.config.ts
(used by middleware) andauth.ts
(main configuration).First, define the extended Session and JWT types using module augmentation. This is typically done in
auth.ts
or a dedicated types file.auth.config.ts
(for Middleware):This file contains configuration needed before the full NextAuth initialization, primarily for middleware checks.
auth.ts
(Main Configuration):This file includes providers and the main logic.
Key Points in
auth.ts
:PrismaAdapter
to sync NextAuth state with the database.Credentials
provider with email/password validation (LoginSchema
).bcrypt.compare
.user.twoFactorEnabled
.sendSms
.user
object. The JWT/Session/authorized callbacks handle the subsequent flow (settingtwoFactorVerified: false
and redirecting).signIn
catches asCallbackRouteError
.twoFactorVerified
).2.4 Configure Middleware:
Create the middleware file to protect routes based on the
authorized
callback inauth.config.ts
.2.5 Implement OTP Utilities:
Create utility functions for generating and verifying OTPs.