惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google Online Security Blog
Google Online Security Blog
S
Security @ Cisco Blogs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
人人都是产品经理
人人都是产品经理
The Hacker News
The Hacker News
W
WeLiveSecurity
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
博客园 - 司徒正美
雷峰网
雷峰网
L
LINUX DO - 最新话题
博客园 - 叶小钗
云风的 BLOG
云风的 BLOG
The Last Watchdog
The Last Watchdog
V2EX - 技术
V2EX - 技术
S
Security Affairs
有赞技术团队
有赞技术团队
月光博客
月光博客
T
Threatpost
T
Tor Project blog
O
OpenAI News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
V
V2EX
Know Your Adversary
Know Your Adversary
Project Zero
Project Zero
博客园 - 三生石上(FineUI控件)
D
Docker
AWS News Blog
AWS News Blog
AI
AI
P
Proofpoint News Feed
K
Kaspersky official blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
www.infosecurity-magazine.com
www.infosecurity-magazine.com
S
Securelist
F
Fortinet All Blogs
F
Full Disclosure
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
量子位
Hacker News - Newest:
Hacker News - Newest: "LLM"
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Palo Alto Networks Blog
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
美团技术团队
N
News | PayPal Newsroom
T
The Blog of Author Tim Ferriss
MyScale Blog
MyScale Blog

Clerk Blog

Going to production with Clerk Deploy Clerk Init: The fastest way to start a new project Introducing Clerk CLI Middleware-based route protection bypass Postmortem: Clerk System Outage (March 10, 2026) Clerk for the AI era Add API Key support to your SaaS in minutes Postmortem: Clerk System Outage (February 19, 2026) Using Clerk in a React Native app Postmortem: DNS Provider Outage (February 10, 2026) How do I implement passkeys in Next.js? Clerk ranked #4 fastest-growing software vendor on Ramp’s December 2025 list How do I handle JWT verification in Next.js? Committing to Agent Identity: Clerk raises $50m Series C from Menlo and Anthropic’s Anthology Fund What is the best way to handle authentication in Next.js App Router? Postmortem: Database Incident (September 14–18, 2025) How do I add authentication to a Next.js app? Introducing Free Trials in Clerk Billing Postmortem: August 28, 2025 - elevated API latency and errors Introducing Mosaic: Bring Your Brand to Every Authentication Flow Multi-tenant authentication: What you need to know (and how Clerk helps) What are the risks and challenges of multi-tenancy? Resilience in Practice: Regional Failover at Clerk Build a Cross-Platform B2B App with Clerk, Expo, and Supabase Highlights from the MiduDev/Clerk Hackathon Add multi-tenancy to an app built with Clerk, Lovable, and Supabase How to build an AI coding rules app with Clerk, Lovable, and Supabase How to Build Multi-Tenant Authentication with Clerk Choosing the right SaaS architecture: Multi-Tenant vs. Single-Tenant Postmortem: June 26, 2025 service outage How to Design a Multi-Tenant SaaS Architecture What is multi-tenancy and why it matters for B2B SaaS How OAuth Works Synchronize user data from Clerk to Supabase Add subscriptions to your SaaS with Clerk Billing Getting started with Clerk Billing Multi-tenant analytics with Tinybird and Clerk How Huntr Migrated 250K Users to Clerk: A Scalable Auth Solution for Startups How to take Clerk to Production How to take your Clerk application to production A practical guide to testing Clerk Next.js applications Implementing multi-tenancy into a Supabase app with Clerk How Clerk integrates with a Next.js application using Supabase How Clerk integrates with Supabase Build a blog with tRPC, Prisma, Next.js and Clerk How to enrich PostHog events with Clerk user data How to build a secure project management platform with Next.js, Clerk, and Neon Validate your SaaS idea while building an audience Postmortem: February 6, 2025 service outage Implement Role-Based Access Control in Next.js 15 Build a Next.js sign-up form with React Hook Form Build a Next.js login page template How to implement Google authentication in Next.js 15 What is middleware in Next.js? How to customize Next.js metadata How to set environment variables in Node.js Building a React Login Page Template How to implement per-user OAuth scopes with Clerk Using Clerk SSO to access Google Calendar and other service data Streamline enterprise customer onboarding with SAML and Clerk Clerk launches EASIE SSO and eliminates SSO fees How to secure Liveblocks Rooms with Clerk in Next.js Securing Node.js Express APIs with Clerk and React Combining the benefits of session tokens and JWTs Build a task manager with Next.js, Supabase, and Clerk Comparing Clerk Webhooks vs Backend API Automate Neon schema changes with Drizzle and GitHub Actions A guide to reading authenticated user data from Clerk Role based access control with Clerk Organizations Mitigating OAuth’s recently discovered Open Response Type vulnerability Per-user B2B monetization with Stripe and Clerk Organizations Build a team-based task manager with Next.js, Neon, and Clerk Building a Hybrid Sign-Up/Subscribe Form with Stripe Elements Welcoming Colin from Zod as our inaugural Open Source Fellow Build a modern authenticated chat application with Next.js, Ably, and Clerk Build a waitlist with Clerk user metadata How to use Clerk with PostHog Identify in Next.js How to secure API Gateway using JWT and Lambda Authorizers with Clerk What are passkeys and how do they work? Comparing Authentication in React.js vs. Next.js How to Add an Onboarding Flow for your Application with Clerk Create Your Own Custom User Menu with Radix - Part 2 Introducing Webhook Workflows with Inngest & Svix Clerk raises $30M Series B from CRV and Stripe Clerk in 2023: A Year in Review Build a Movie Emoji Quiz App with Remix, Fauna, and Clerk Ultimate Guide to Magic Link Authentication Create Your Own Custom User Menu with Radix Introducing has(), protect(), and <Protect> Updated Pricing: 10,000 MAUs Free, and a new “Pro Plan” Next.js Authentication with Clerk: Streamlined SSR Handling Clerk Webhooks: Data Sync with Convex Exploring Clerk Metadata with Stripe Webhooks The Ultimate Guide to Next.js Authentication Empower Your Support Team With User Impersonation Clerk Webhooks: Getting Started A Complete Guide to Session Management in Next.js The Advanced Guide to Passwordless Authentication in Next.js How We Roll – Chapter 10: Roundup How We Roll – Chapter 9: Infrastructure
Exploring the Intricacies of OTP Authentication in Next.js
Nick Parsons · 2023-07-24 · via Clerk Blog

Passwords aren’t great. They’re often weak, reused, and need to be stored indefinitely making them susceptible to attacks and leaks. have i been pwned? tells me passwords associated with just one of my email addresses have been leaked 18 times, and I have over 800 individual passwords stored in my password manager.

This is why magic links, SSO, and one-time passwords (OTPs) are becoming standard authentication methods. They provide better or additional security for your users.

Here we’re going to look at OTPs. One-time passwords are a great option for improving the security of your application. Let’s go through exactly what one-time passwords are, how they can improve the security of your application, the best practices for using them in authentication, and how you can implement OTPs in Next.js.

What is a one-time password?

OTP or One-Time Password authentication is a method in which a unique code is sent to a user's device and the user enters this code into an application to verify their identity. It’s valid for only one login session and usually time-limited.

There are two ways OTPs are implemented:

  1. As the main factor for logging in a user. If you don’t want to use any username/password combinations in your authentication flow, you can send a one-time password for log in. You can also use it as an alternative to username/password. This is not particularly common, but can be seen on sites such as local social networking site Nextdoor.
  2. As an additional step in multi-factor authentication (MFA). After a user has logged in with their username/password or another authentication provider (such as Google, Twitter, or GitHub), they are then sent an OTP to a registered email address or phone number as a secondary layer of security for applications. This second option is much more common.

You’d think that one-time passwords would be long and complicated like the suggested passwords from a password manager. But because they are transient in nature and rarely subject to dictionary or brute force attacks, they can be much simpler. OTPs can have different formats, but they usually consist of a series of alphanumeric characters or purely numeric characters. The length of OTPs can vary, but a common format is a 6 or 8-digit numeric code.

The choice between numeric and alphanumeric OTPs depends on the context and requirements. Numeric OTPs can be easier to enter, especially on mobile devices, which makes them a popular choice for SMS-based OTPs. However, alphanumeric OTPs provide a larger possible combination set for the same number of characters, which can be more secure against brute-force attacks.

There are six main steps in the OTP flow:

Otp Authentication Nextjs tutorial illustration

  1. Trigger. The user initiates the OTP process. This could be a login attempt, a transaction verification, or any other scenario where identity needs to be confirmed.
  2. OTP generation. The server generates an OTP. This is typically a random string or number that is time-limited.
  3. OTP delivery. The generated OTP is sent to the user through a predetermined method. This could be an SMS to their phone, an email to their registered email address, or generated in a hardware token or software app, such as Google Authenticator.
  4. User input. The user receives the OTP and enters it into the application.
  5. OTP verification. The server then verifies the OTP. The server also verifies that the OTP is used within the allowed time period, and hasn't been used before.
  6. Authentication: If the OTP is verified, the server authenticates the user and allows them to proceed. If the OTP is incorrect or expired, the server rejects the request.

Though this is only six steps, there are a lot of moving parts in OTP use. You need extra frontend UI design and logic to handle the OTP inputs; you need to integrate a delivery mechanism such as SMS, email, or an authenticator app; you need extra authentication logic to handle OTP errors; and you need the OTP generation and verification logic as well.

There are several ways to generate and verify an OTP:

  • Time-Synchronized: In this method, the OTP is generated by applying a cryptographic hash function to a shared secret and the current time, typically measured in intervals of 30 seconds. The Google Authenticator app uses this method.
  • Counter-Based: Here, the OTP is generated by hashing a shared secret with a counter which increments with each new OTP.
  • Algorithm-Based: In this method, a mathematical algorithm is applied to the previous password to generate the new one.

If a time-based or counter-based OTP was used, the server repeats the same OTP generation process during verification and checks if the received OTP matches the one it generated.

These algorithms aren’t easy to implement. As you are dealing with an authentication factor, there are specific designs that you must use and RFCs that you must follow, such as this one for Time-Based OTPs. This is the biggest challenge around building your own one-time password (or any authentication) system–implementation.

The security benefits (and challenges) of OTPs

You can quickly see the complexity involved in setting up OTP authentication. But doing so is worth it as they provide two key security benefits.

Risk mitigation

The mitigation of risk with OTPs comes from two different avenues.

The first is mitigating the risk associated with static passwords. Traditional static passwords, if stolen, provide ongoing access until they are changed. OTPs are dynamic and expire after a single use or after a short period of time, limiting the potential damage if they are intercepted or stolen. Another problem is users reusing the same password across multiple services. If one service is compromised, all accounts using the same password are at risk. OTPs eliminate this risk because they are unique for each login session.

The second is the reduction of attack vectors. Because OTPs are typically time-limited, it makes brute-force attacks infeasible. An attacker doesn't have the time to try all possible combinations before the password expires. They also help with phishing. Even if a user is tricked into entering their OTP into a phishing site, the attacker can't reuse that OTP to gain future access to the account.

Additionally, since an OTP is valid for only one login session or transaction, it cannot be reused, preventing replay attacks. In a replay attack, an attacker tries to reuse a password that was intercepted in a previous session. However, if there's a flaw in the system's design where the OTP doesn't expire immediately after use or isn't time-bound, there's a possibility for replay attacks.

Identity security and verification

OTPs also play a significant role in enhancing identity security and verification processes. They provide an additional layer of protection beyond traditional static passwords, aiding in the confirmation of a user's identity in several ways:

  • OTPs are often used as part of a two-factor authentication process. In addition to a traditional username and password (something the user knows), the user must enter an OTP (something the user has, typically their mobile device). This confirms that the user has access to a specific device (like a phone) that is associated with the account, verifying the identity of the user.
  • OTPs are commonly used in financial transactions or account changes to verify the identity of the user making the transaction. For example, when conducting a bank transfer or changing account details, an OTP might be sent to the registered mobile number or email address. The user enters the OTP to confirm the transaction, verifying that they are the account holder.
  • OTPs are often used in password recovery processes to verify the user's identity. The service sends an OTP to the user's registered email address or mobile number, which the user then enters to verify their identity and proceed with resetting their password.
  • When a user logs in from a new device, an OTP might be sent to their registered contact information. The user must enter the OTP to verify they have access to the registered device, confirming their identity and that the new device is trusted.

By integrating OTPs into authentication and verification processes, services can add an extra level of security and significantly reduce the risk of unauthorized access or identity theft.

Best practices for using one-time passwords

OTPs aren’t infallible, though. But the associated risks are generally around poor implementation rather than inherent to the method. To ensure OTP effectiveness and avoid some of the above potential vulnerabilities, you can follow some best practices.

Basic security practices

These practices are the principles of any good system:

  • Secure delivery channel. Use a secure delivery channel for sending the OTP. If you're sending the OTP via email or SMS, ensure the communication channel is secure.
  • Encrypt communication. Ensure all communications between the client and the server are done over HTTPS to prevent any interception of the OTP.
  • Use secure storage. When storing OTPs on the server side, consider hashing them. This ensures that even if someone gains access to your storage, they cannot obtain the actual OTPs.

Good OTP design

These relate to how well you design your OTP algorithm and logic:

  • Use a strong OTP. Use an OTP that is long and complex enough to resist brute-force attacks. An OTP with at least 6 digits is usually recommended.
  • Time-Bound OTP. Make sure the OTP is valid only for a short period of time. This reduces the window an attacker has to use a stolen OTP. Usually, OTPs are valid for about 2-10 minutes.
  • Limit OTP attempts. Implement rate limiting on your OTP endpoints to protect against brute force attacks. After a certain number of incorrect attempts, either block the user or implement a cool-down period.
  • Expiry after use. The OTP should expire immediately after it has been used once, to prevent replay attacks.

Good UX

These help users use your OTP and make sure they don’t turn it off:

  • Backup codes. For applications using OTP as a second factor, provide backup codes that the user can write down and use if they lose access to their OTP delivery method (like losing their phone).
  • Fallback Options. In case the primary delivery channel fails (e.g., SMS not being delivered), have a secondary option like email or voice call.

Also consider educating users about using OTPs. One of the main threats to OTP use are physical–if an attacker steals a user's phone then they’ll have access to the SMS or email used with OTPs. Or a fraudster can fake a user’s identity to trick a telecoms company into assigning a new SIM with the user’s phone number to them (known as SIM swapping).

While OTPs can greatly enhance security, they are not foolproof. Implementation within a broader security strategy is key.

Setting up OTP Authentication in Next.js

Let’s walk through setting up a one time password system within Next.js. If you already have a Next.js app up and running you can add this code directly. Otherwise create a new app using:

We’ll also need to use a few modules to help us with our OTPs, namely:

  • Twilio. Twilio allows us to send SMS messages programmatically. Here we’re going to use it to send the OTP to the user's phone number via SMS.
  • bcryptjs. bcryptjs is a JavaScript library for hashing and comparing passwords. We’re going to use it to hash the OTP before storing it in the database for added security.
  • MongoDB. MongoDB is a NoSQL database that we’ll use to store hashed OTPs along with the associated phone numbers and expiry times.
  • Upstash. Upstash is a serverless data platform. We’re going to use it’s rate limiter and Redis functionality.

Install these with:

For Twilio and MongoDB, you’ll also need to sign up for accounts and then need your TWILIO_ACCOUNT_SID, your TWILIO_AUTH_TOKEN, and your MONGODB_URI. For Twilio, you’ll also need to buy a TWILIO_PHONE_NUMBER that will be used to send your SMS messages.

You’ll also need an account with Upstash, and then your UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN variables.

With that done, we’ll first create the API route that will generate our OTP:

With this code we initially import all our dependencies, then create a handler function for our POST endpoint. The body of the POST request will contain the phone number of the user that we’ll get from the frontend. Within the endpoint, we’re doing a few things:

  • Creating a six-digit random OTP
  • Hashing that OTP with bcryptjs for storage
  • Creating a Twilio client and then sending the OTP to the user’s phone number
  • Creating a MongoDB client and storing the OTP along with the user’s phone number and an expiry time for the password.

Is this best practice? Absolutely not. We are doing a few things right, such as setting an expiry time on the OTP and hashing them. But our OTP generating ‘algorithm’ is laughably simple.

Let’s quickly create a frontend for this now:

All this code will just show a single form on the page. On first load this form will ask for the user’s phone number.

Otp Authentication Nextjs tutorial illustration

When the user enters their phone number and hits submit, the above generateOTP endpoint will be called. This will send the OTP to the user’s phone number:

Otp Authentication Nextjs tutorial illustration

Hitting submit will also change the form to accept the OTP as the input. The user can then check their phone and enter the six-digit code: and hit submit again to send the OTP and phone number to a verifyOTP endpoint for verification:

When called, this API loads the OTP MongoDB database and finds the one associated with the phone number. It checks whether it has expired, and if not, matches the hashed OTPs. If it's valid, the OTP gets deleted from the database and a 200 code is returned to the frontend.

Otp Authentication Nextjs tutorial illustration

We could then work that response into any other authentication flow we had set up. We also have a basic rate limiter set into that will make sure a user can’t input more than 2 codes in 3 seconds, to try and prevent brute force attacks:

Otp Authentication Nextjs tutorial illustration

And that’s it. You have one-time passwords working in Next.js.

OTPs are intricate but worth implementing

This code gives you a one-time password option for Next.js authentication. But we’ve only scratched the surface. We haven’t implemented this within any other authentication flow–this just generates, sends, and verifies the OTP, it doesn’t use it to authenticate the user.

We’ve also generated the OTP in the most basic manner, not following the RFCs and guidelines. We do have some nice best practices–rate-limiting, time-bounding, and expiry–but again these are all basic implementations. We also had to buy a new phone number!

This is the intricacy of OTPs. They are easy to set up, but difficult to get right. Like most authentication methods, it is better to use a provider than trying to create your own. Check out Clerk’s OTP solution here to have these intricacies taken care of for you.