Blogs
Feb 21, 2025
Easy email two-factor authentication in Auth.js with credentials (user/pass) provider. Complete step by step guide with code samples and example github repo.
Motivation
While Auth.js handles 2FA for OAuth providers, you need to build it yourself when using username/password authentication. This involves secure code generation, email delivery, session management, and a verification UI.
This guide shows you how to implement email-based 2FA that's maintainable and secure.
Prerequisites
An Auth.js project with Credentials provider
A NotificationAPI account (or another email service)
Prisma or another database ORM
Basic understanding of Next.js/React
Implementation
For the 2FA code (token), we're going to randomly generate a 6-digit number and store it with an expiration in the database. This approach is not the most secure, but we're doing it this way to keep this blog as brief as possible.
The complete code is available in our example repository.
1. Set Up Your Database
First, let's update your user objects to add fields for the token and its expiry:
2. Setup An Email Service
We need an Email or SMS service to send out the 2FA token to the user. In this example, we will be using NotificationAPI, which comes free with a ton of emails and SMS messages.
Install NotificationAPI in your codebase:
In the NotificationAPI dashboard, configure a 2fa_token
notification and include a {{code}}
parameter somewhere in its template designs.
Then, update your environment variables:
3. Modify Auth.js to Support 2FA
There are a couple of things we are doing in our Auth back-end code:
Have a
send2FACode
function to send the verification codeAdd
code
(the 2FA token) to the credentials along with username/passwordModify our
authorize
function to handle the first sign-in (username+password) attempt:When not given a token, generate, store and send the token, and respond with: 2FA_REQUIRED
Modify the same function to handle the second sign-in attempt (username+password+2fa):
When given a token, match it with the database and sign the user in
That is all the back-end you need!
4. Modify the Front-End
First, on the sign-in page, you want to modify the logic so that:
After the initial sign-in attempt with username+password, if we are challenged with "2FA_REQUIRED", to hide the username/password fields and display the token input field
After the token is submitted, remember to submit all of the username+password+2fa
That's really it. Pretty simple, right?
Below is a simplified view of the front-end:
Quality of Life
You may have noticed that nowhere did we write email HTML/CSS. NotificationAPI gives us:
A visual editor to design the notification content to keep your code clean
SMS option out of the box without Twilio
Monitoring and analytics

Security Best Practices
Rate Limiting: Limit 2FA attempts per IP and user
Code Expiry: Keep codes valid for 5 minutes maximum
Code Generation: Use a hashing algorithm to generate the codes
Secure Storage: Hash codes before storing, just like passwords
Recovery Methods: Consider having recovery methods if users lose access to their email/phone for 2FA
The complete code is available in our example repository.