code examples
code examples
Plivo Next.js OTP & 2FA Implementation Guide – Complete Tutorial
Learn how to implement OTP verification and Two-Factor Authentication in Next.js using Plivo Verify API. Step-by-step guide with code examples, security best practices, and error handling.
Plivo Next.js OTP & 2FA Implementation Guide
Learn how to implement One-Time Password (OTP) verification and Two-Factor Authentication (2FA) in your Next.js application using Plivo's Verify API. This step-by-step tutorial covers OTP generation, SMS delivery, phone number validation, and security best practices for production applications.
We will build a simple Next.js application featuring:
- A UI to input a phone number.
- An API endpoint to trigger an OTP request via Plivo Verify (using SMS).
- A UI to input the received OTP.
- An API endpoint to validate the OTP using Plivo Verify.
- Basic state management, error handling, and security considerations.
This implementation solves the critical need for verifying user phone numbers, enhancing security against fake accounts and unauthorized access, while leveraging Plivo's global reach, high deliverability, and built-in fraud protection.
Technologies Used:
- Next.js: A React framework for building server-side rendered and statically generated web applications. Chosen for its developer experience, performance features, and integrated API routes.
- Plivo Verify API: A service for sending and validating OTPs via SMS, Voice, and WhatsApp. Chosen for its simple API, competitive pricing, global coverage, and features like Fraud Shield.
- Node.js: The runtime environment for Next.js and the Plivo SDK.
- React: For building the user interface components.
- Tailwind CSS (Optional): For styling the UI (examples will use basic HTML/CSS for clarity, but Tailwind is common in Next.js).
Prerequisites:
- Node.js: v22 (Active LTS, recommended for new projects), v20 (Maintenance LTS), or v18 minimum. Note: Node.js v18 reaches end-of-life in April 2025; migration to v20 or v22 is recommended for long-term projects.
- Next.js: This guide uses Pages Router for simplicity. Next.js 15 (latest as of 2025) includes stable App Router support, but both routing systems are fully supported.
- A Plivo account (Sign up at plivo.com).
- Basic understanding of React and Next.js concepts (components, hooks, API routes).
- Access to a terminal or command prompt.
System Architecture:
The typical flow involves the user's browser interacting with Next.js API routes, which in turn communicate with the Plivo Verify API to send an OTP via SMS to the user's phone and later validate the OTP entered by the user.
Final Outcome:
By the end of this guide, you will have a functional Next.js page where a user can enter their phone number, receive an OTP via SMS managed by Plivo, enter that OTP, and have it validated, with UI feedback indicating success or failure. You will also understand how to securely handle API credentials, implement basic error handling, and consider security measures like rate limiting.
1. Setting Up Your Next.js OTP Project with Plivo
Create a new Next.js project and install the Plivo SDK to begin building your OTP verification system.
-
Create a Next.js App: Open your terminal and run the following command. Replace
plivo-nextjs-otpwith your preferred project name. Follow the prompts (we recommend using TypeScript and App Router if prompted, but this guide uses Pages Router for simplicity in API routes).bashnpx create-next-app@latest plivo-nextjs-otp # Follow the prompts (e.g., choose Pages Router for this guide) cd plivo-nextjs-otp -
Install Plivo Node.js SDK: This package provides convenient methods for interacting with the Plivo API.
bashnpm install plivo # or yarn add plivo -
Set Up Environment Variables: Sensitive information like API keys should never be hardcoded. We'll use environment variables.
-
Create a file named
.env.localin the root of your project. -
Add the following lines, replacing the placeholder values later:
Code# Plivo Credentials - Get from Plivo Console > API > Keys & Credentials PLIVO_AUTH_ID=YOUR_PLIVO_AUTH_ID PLIVO_AUTH_TOKEN=YOUR_PLIVO_AUTH_TOKEN -
Important: Add
.env.localto your.gitignorefile (it should be there by default in Next.js projects) to prevent accidentally committing your secrets.
Why
.env.local? Next.js automatically loads variables from this file for local development. For production deployment, you'll need to configure these environment variables directly in your hosting provider's settings (e.g., Vercel, Netlify). -
-
Project Structure: Your basic structure (using Pages Router) will look like this:
plivo-nextjs-otp/ ├── .env.local ├── .gitignore ├── node_modules/ ├── package.json ├── pages/ │ ├── api/ # API Routes live here │ │ └── otp/ │ │ ├── send.js │ │ └── verify.js │ ├── _app.js │ └── index.js # Our main page component ├── public/ └── styles/ -
Run the Development Server: Verify the basic setup is working.
bashnpm run dev # or yarn devOpen your browser to
http://localhost:3000. You should see the default Next.js welcome page.
2. Building Next.js API Routes for OTP Verification
Build the backend API routes within Next.js to handle sending and verifying OTPs using the Plivo Verify API and Node.js SDK.
2.1 Create API Route to Send OTP via Plivo (/api/otp/send)
This endpoint will receive a phone number, initiate the Plivo Verify session, and return the session_uuid to the client.
-
Create the file:
pages/api/otp/send.js -
Add the following code:
javascript// pages/api/otp/send.js import { Client } from 'plivo'; // Ensure environment variables are loaded (Next.js handles this automatically) const authId = process.env.PLIVO_AUTH_ID; const authToken = process.env.PLIVO_AUTH_TOKEN; if (!authId || !authToken) { console.error("Plivo Auth ID or Auth Token is missing in environment variables."); // In a real app, you might throw an error or handle this more gracefully } const client = new Client(authId, authToken); export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ message: 'Method Not Allowed' }); } const { recipient } = req.body; // Basic validation - enhance as needed (e.g., regex for phone format) if (!recipient) { return res.status(400).json({ message: 'Recipient phone number is required' }); } console.log(`Sending OTP request to: ${recipient}`); try { const response = await client.verify.session.create({ recipient: recipient, channel: 'sms', // Specify channel (sms, voice, whatsapp) // Add other parameters like 'locale', 'url' for callbacks if needed // See Plivo Verify docs: https://www.plivo.com/docs/verify/api/session#create-a-session }); console.log("Plivo Create Session Response:", response); // Return the session UUID to the client for the verification step res.status(200).json({ session_uuid: response.sessionUuid }); } catch (error) { console.error('Plivo API Error creating session:', error); // Provide a generic error message to the client // Log the specific error details on the server let errorMessage = 'Failed to send OTP. Please try again later.'; let statusCode = 500; // Check for specific Plivo error structures if available from the SDK // Example (structure might vary based on SDK/API version): if (error.message) { // You might parse error.message for specific Plivo error codes/reasons // e.g., invalid number format, insufficient balance, etc. // For now, we log the raw error server-side console.error("Detailed Plivo Error:", error.message); } // Based on Plivo error codes, you might set different status codes (e.g., 400 for bad input) // if (error.statusCode === 400) { // statusCode = 400; // errorMessage = 'Invalid phone number format provided.'; // } res.status(statusCode).json({ message: errorMessage }); } }Explanation:
- We import the Plivo
Client. - We initialize the client using credentials from environment variables. Crucially, we check if they exist.
- The handler only accepts
POSTrequests. - We extract the
recipientphone number from the request body. Basic validation is included. client.verify.session.create()is called with the recipient number andchannel: 'sms'. This tells Plivo to generate an OTP and send it via SMS.- On success, Plivo returns a response containing the
sessionUuid. We extract and send this back to the client in the JSON response. - Error handling uses a
try...catchblock. We log the detailed error server-side and return a generic error message to the client.
- We import the Plivo
2.2 Create API Route to Validate OTP Codes (/api/otp/verify)
This endpoint receives the session_uuid (obtained from the previous step) and the otp entered by the user, then validates them with Plivo.
-
Create the file:
pages/api/otp/verify.js -
Add the following code:
javascript// pages/api/otp/verify.js import { Client } from 'plivo'; const authId = process.env.PLIVO_AUTH_ID; const authToken = process.env.PLIVO_AUTH_TOKEN; if (!authId || !authToken) { console.error("Plivo Auth ID or Auth Token is missing in environment variables."); } const client = new Client(authId, authToken); export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ message: 'Method Not Allowed' }); } const { session_uuid, otp } = req.body; if (!session_uuid || !otp) { return res.status(400).json({ message: 'Session UUID and OTP are required' }); } // *** OTP Length Assumption *** // This guide assumes a 6-digit OTP. Plivo might be configured differently. // Adjust the regex below and the `maxLength` attribute in the frontend UI // if your OTP length is different. const otpRegex = /^\d{6}$/; // Adjust '6' if needed // Basic OTP format validation if (!otpRegex.test(otp)) { return res.status(400).json({ message: 'Invalid OTP format. Expecting 6 digits.' }); // Adjust message if length changes } console.log(`Verifying OTP for session: ${session_uuid}`); try { const response = await client.verify.session.validate( session_uuid, // The session_uuid received from the create step { otp: otp } // The OTP entered by the user // See Plivo Verify docs: https://www.plivo.com/docs/verify/api/session#validate-a-session ); console.log("Plivo Validate Session Response:", response); // *** Validation Check *** // Checking the response message string is functional but might be brittle // if Plivo changes the exact wording. // Ideally, check Plivo's documentation for a more robust success indicator, // such as a specific status code within the success response body or a boolean flag, // if available. Using the message is a fallback. if (response && response.message === "Session validated successfully.") { res.status(200).json({ verified: true, message: response.message }); } else { // Handle cases where validation fails (e.g., wrong OTP, expired session) // Plivo might return a specific message or status code here console.warn("OTP Validation Failed:", response); res.status(400).json({ verified: false, message: response.message || 'Invalid or expired OTP.' }); } } catch (error) { console.error('Plivo API Error validating session:', error); let errorMessage = 'Failed to verify OTP. Please try again later.'; let statusCode = 500; // Plivo might throw specific errors for invalid session UUID, expired session, etc. // Check error structure and provide more specific feedback if possible. // Example: A 404 might indicate an invalid or expired session UUID. if (error.statusCode === 404) { statusCode = 404; errorMessage = 'Invalid or expired session. Please request a new OTP.'; } else if (error.statusCode === 400) { // This might occur for incorrect OTP format even if basic check passed, or other issues statusCode = 400; errorMessage = 'Invalid OTP provided.'; // Check error.message for more details if needed console.error("Detailed Plivo 400 Error:", error.message); } else { console.error("Detailed Plivo Error:", error.message || error); } res.status(statusCode).json({ verified: false, message: errorMessage }); } }Explanation:
- Similar setup for Plivo client initialization and
POSTmethod check. - Extracts
session_uuidandotpfrom the request body. Includes validation for presence and basic OTP format. - OTP Length: A comment highlights the 6-digit assumption and advises checking Plivo settings, adjusting the regex (
/^\d{6}$/) and frontend accordingly. client.verify.session.validate()is called with thesession_uuidand theotp.- Validation Logic: The code checks the success message string, but a comment clarifies this might be brittle and recommends checking Plivo documentation for potentially more robust validation methods (status codes, boolean flags).
- If validation fails (wrong OTP, expired session), Plivo might return a non-error response with a failure message, or throw a specific error (like 404 for invalid session). The code handles both possibilities, returning
{ verified: false }. - The
catchblock handles API communication errors or unexpected Plivo errors during validation, logging details server-side and returning appropriate client-side messages (e.g., specific messages for 404 or 400 errors).
- Similar setup for Plivo client initialization and
3. Building the OTP Verification UI with React
Create the React component for the user interface to handle phone number input and OTP verification.
-
Edit the main page:
pages/index.js -
Replace its content with the following:
javascript// pages/index.js import { useState } from 'react'; import Head from 'next/head'; // Define OTP length constant (adjust if needed based on Plivo config) const OTP_LENGTH = 6; export default function Home() { const [phoneNumber, setPhoneNumber] = useState(''); const [otp, setOtp] = useState(''); const [sessionUuid, setSessionUuid] = useState(''); const [message, setMessage] = useState(''); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); const [isOtpSent, setIsOtpSent] = useState(false); const [isVerified, setIsVerified] = useState(false); // Handler to request OTP const handleSendOtp = async (e) => { e.preventDefault(); setIsLoading(true); setMessage(''); setError(''); setOtp(''); // Clear previous OTP input if any setIsVerified(false); // Reset verification status // Basic client-side validation (improve with libraries like react-phone-number-input if needed) if (!phoneNumber || !/^\+?[1-9]\d{1,14}$/.test(phoneNumber)) { setError('Please enter a valid phone number in E.164 format (e.g., +14155552671).'); setIsLoading(false); return; } try { const res = await fetch('/api/otp/send', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ recipient: phoneNumber }), }); const data = await res.json(); if (!res.ok) { // Use error message from API response if available throw new Error(data.message || `Error: ${res.status}`); } setSessionUuid(data.session_uuid); setMessage('OTP sent successfully! Please check your phone.'); setIsOtpSent(true); // Show OTP input field } catch (err) { console.error("Send OTP Error:", err); setError(err.message || 'Failed to send OTP. Please try again.'); setIsOtpSent(false); // Hide OTP input on error } finally { setIsLoading(false); } }; // Handler to verify OTP const handleVerifyOtp = async (e) => { e.preventDefault(); setIsLoading(true); setMessage(''); setError(''); setIsVerified(false); // Reset verification status // Use the OTP_LENGTH constant for validation if (!otp || !new RegExp(`^\\d{${OTP_LENGTH}}$`).test(otp)) { setError(`Please enter the ${OTP_LENGTH}-digit OTP.`); setIsLoading(false); return; } try { const res = await fetch('/api/otp/verify', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ session_uuid: sessionUuid, otp: otp }), }); const data = await res.json(); if (!res.ok || !data.verified) { // Use error message from API response if available throw new Error(data.message || `Error: ${res.status}`); } setMessage('Phone number verified successfully!'); setIsVerified(true); // Indicate success // In a real app: redirect, update user profile, grant access, etc. // setSessionUuid(''); // Optional: Clear session UUID after successful verification // setIsOtpSent(false); // Optional: Hide OTP form after success } catch (err) { console.error("Verify OTP Error:", err); setError(err.message || 'Failed to verify OTP. Please check the code or request a new one.'); setIsVerified(false); } finally { setIsLoading(false); } }; return ( <div style={styles.container}> <Head> <title>Plivo OTP Verification</title> <meta name="description" content="Next.js Plivo OTP Verification Example" /> <link rel="icon" href="/favicon.ico" /> </Head> <main style={styles.main}> <h1 style={styles.title}>Phone Verification</h1> {!isVerified ? ( <> {/* Phone Number Input Form */} <form onSubmit={handleSendOtp} style={styles.form}> <label htmlFor="phone" style={styles.label}>Enter Phone Number (E.164 format):</label> <input type="tel" id="phone" value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} placeholder="+14155552671" required disabled={isLoading || isOtpSent} // Disable while loading or after OTP sent initially style={styles.input} /> <button type="submit" disabled={isLoading || !phoneNumber} style={styles.button}> {isLoading && !isOtpSent ? 'Sending...' : 'Send OTP'} </button> </form> {/* OTP Input Form - Shown only after OTP is requested */} {isOtpSent && ( <form onSubmit={handleVerifyOtp} style={styles.form}> <label htmlFor="otp" style={styles.label}>Enter OTP:</label> <input type="text" // Use "text" and pattern for better mobile input handling inputMode="numeric" // Hint for numeric keyboard pattern={`\\d{${OTP_LENGTH}}`} // Enforce OTP_LENGTH digits maxLength={OTP_LENGTH} // Use constant for max length id="otp" value={otp} onChange={(e) => setOtp(e.target.value)} required disabled={isLoading} style={styles.input} /> <button type="submit" disabled={isLoading || !otp || otp.length < OTP_LENGTH} style={styles.button}> {isLoading ? 'Verifying...' : 'Verify OTP'} </button> {/* Optional: Add a "Resend OTP" button here */} </form> )} </> ) : ( <div style={styles.successMessage}> Phone Number Verified Successfully! </div> )} {/* Display Messages and Errors */} {message && !error && <p style={styles.message}>{message}</p>} {error && <p style={styles.error}>{error}</p>} </main> </div> ); } // Basic inline styles for demonstration const styles = { container: { minHeight: '100vh', padding: '0 0.5rem', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', fontFamily: 'Arial, sans-serif', }, main: { padding: '2rem', border: '1px solid #eaeaea', borderRadius: '10px', maxWidth: '400px', width: '100%', textAlign: 'center', }, title: { margin: '0 0 1.5rem 0', lineHeight: '1.15', fontSize: '2rem', }, form: { display: 'flex', flexDirection: 'column', gap: '1rem', marginBottom: '1rem', }, label: { fontWeight: 'bold', textAlign: 'left', fontSize: '0.9rem', }, input: { padding: '0.8rem', border: '1px solid #ccc', borderRadius: '4px', fontSize: '1rem', }, button: { padding: '0.8rem 1.5rem', backgroundColor: '#0070f3', color: 'white', border: 'none', borderRadius: '4px', fontSize: '1rem', cursor: 'pointer', transition: 'background-color 0.2s ease', // Use conditional styling or CSS classes for disabled state }, // Example conditional style for disabled button (apply via style prop) // buttonDisabled: { // backgroundColor: '#ccc', // cursor: 'not-allowed', // }, message: { color: 'green', marginTop: '1rem', }, error: { color: 'red', marginTop: '1rem', }, successMessage: { color: 'green', marginTop: '1rem', fontSize: '1.2rem', fontWeight: 'bold', } };Explanation:
- We use
useStatehooks to manage the phone number input, OTP input, session UUID, loading state, UI messages, errors, and whether the OTP form should be visible (isOtpSent), and verification status (isVerified). - An
OTP_LENGTHconstant is defined (defaulting to 6) for easier adjustment and used in validation and input attributes. handleSendOtp:- Performs basic client-side validation on the phone number (E.164 format check). Note: Use a dedicated library like
react-phone-number-inputfor robust international phone number validation in production. - Sends a
POSTrequest to/api/otp/sendwith the phone number. - Handles the response: stores the
session_uuid, shows a success message, and setsisOtpSentto true to display the OTP input form. - Handles errors: displays an error message from the API or a generic one.
- Performs basic client-side validation on the phone number (E.164 format check). Note: Use a dedicated library like
handleVerifyOtp:- Performs client-side validation on the OTP using the
OTP_LENGTHconstant. - Sends a
POSTrequest to/api/otp/verifywith thesession_uuidandotp. - Handles the response: If
data.verifiedis true, shows a success message and setsisVerified. Otherwise, throws an error. - Handles errors: displays an error message.
- Performs client-side validation on the OTP using the
- The JSX renders the forms conditionally based on
isOtpSentandisVerified. InputmaxLength,pattern, and validation logic now use theOTP_LENGTHconstant. - Basic inline styles are provided. Use CSS Modules, Tailwind CSS, or conditional styling for disabled states in real applications.
- We use
4. Configuring Plivo API Credentials for Authentication
Set up your Plivo API credentials to enable OTP and 2FA functionality in your Next.js application.
-
Log in to your Plivo Console: Go to console.plivo.com.
-
Navigate to API Keys: On the main dashboard or via the navigation menu, find the section related to "API" or "Account" and look for "Keys & Credentials" or similar.
-
Copy Auth ID and Auth Token: You will see your
AUTH IDandAUTH TOKEN. Copy these values. -
Update
.env.local: Paste the copied values into your.env.localfile:CodePLIVO_AUTH_ID=YOUR_ACTUAL_AUTH_ID_FROM_PLIVO PLIVO_AUTH_TOKEN=YOUR_ACTUAL_AUTH_TOKEN_FROM_PLIVOCRITICAL: You must replace the placeholder text above with your actual credentials obtained from the Plivo console for the application to function.
-
Restart your Next.js server: Environment variables in
.env.localare only loaded at build time or server start. Stop your development server (Ctrl+C) and restart it:bashnpm run dev # or yarn dev
Secure Handling:
- Never commit
.env.localor your actual credentials to version control (Git). - Use your hosting provider's mechanism for setting environment variables in staging and production environments (e.g., Vercel Environment Variables, Netlify Build environment variables).
5. Implementing Error Handling and Retry Logic for OTP
Implement robust error handling, logging, and retry mechanisms to improve the reliability of your OTP verification system.
- Consistent Error Strategy: Both API routes and the frontend catch errors. API routes log detailed errors server-side and return user-friendly messages. The frontend displays these messages.
- Logging:
- We are using
console.logandconsole.errorin the API routes. For production, use a structured logging library likepinoorwinston. This enables better log parsing, filtering, and integration with log management services (e.g., Datadog, Logtail). - Log crucial events: OTP request initiation, Plivo API success/failure responses, validation attempts, and final verification status.
- We are using
- Retry Mechanisms:
- Client-Side (User Initiated): The current setup allows the user to manually retry sending the OTP or verifying it if an error occurs. Consider adding an explicit "Resend OTP" button. This button should likely call the
/api/otp/sendendpoint again. Implement a cooldown period (e.g., disable the button for 30-60 seconds after a request) to prevent abuse. - Server-Side (API Call Retries): For transient network errors when calling the Plivo API, you could implement automatic retries with exponential backoff within the API route (using libraries like
async-retry). However, for OTP, be cautious: retrying acreatecall might result in multiple OTPs being sent (and billed). Retrying avalidatecall is generally safer if the initial attempt failed due to a network issue. Start simple; add server-side retries only if transient Plivo API call failures become a noticeable problem.
- Client-Side (User Initiated): The current setup allows the user to manually retry sending the OTP or verifying it if an error occurs. Consider adding an explicit "Resend OTP" button. This button should likely call the
Testing Error Scenarios:
- Enter an incorrectly formatted phone number.
- Enter a valid number but an incorrect OTP.
- Wait for the OTP session to expire (Plivo sessions typically last a few minutes) and then try to verify.
- Temporarily put incorrect Plivo credentials in
.env.localto simulate auth errors. - Simulate network errors (e.g., using browser developer tools network throttling) when the frontend calls the API routes.
6. Integrating OTP Verification with User Database Schema
Integrate your OTP verification system with a user database for production 2FA and phone number validation workflows.
- User Schema: Your user table/document would likely have fields like:
idemail/usernamepassword_hashphone_number(string, store in E.164 format)is_phone_verified(boolean, defaults tofalse)two_factor_enabled(boolean, defaults tofalse)created_at,updated_at
- Workflow Integration:
- Sign-up: User provides phone number -> Send OTP -> Verify OTP -> If successful, set
is_phone_verified = truefor the newly created user record. - Login (2FA): User logs in with password -> If
two_factor_enabled = true, prompt for OTP -> Send OTP (using the user's verifiedphone_number) -> Verify OTP -> If successful, grant access (create session/JWT).
- Sign-up: User provides phone number -> Send OTP -> Verify OTP -> If successful, set
- Data Layer: Use an ORM (like Prisma, TypeORM, Mongoose) or a query builder to interact with your database securely and efficiently.
For this example, the state (isVerified) is temporary and managed in the React component. A real app needs persistence.
7. Securing Your OTP System: Rate Limiting and Input Validation
Implement security features like rate limiting, input validation, and sanitization to protect your OTP authentication system from abuse and attacks.
- Input Validation and Sanitization:
- Server-Side: The API routes have basic checks (
!recipient,!session_uuid,!otp). Enhance phone number validation using a robust library on the server (e.g., Google'slibphonenumbervia a Node.js port) to ensure format correctness before calling Plivo. Validatesession_uuidformat (it's typically a UUID). Ensure OTP is numeric and matches expected length (as implemented in 2.2). - Client-Side: Basic checks help user experience but should always be duplicated server-side.
- Server-Side: The API routes have basic checks (
- Rate Limiting: This is critical to prevent abuse (attackers spamming phone numbers with OTPs, incurring costs) and brute-forcing OTPs.
-
Apply rate limiting to both
/api/otp/sendand/api/otp/verifyendpoints. -
Implement limits based on IP address, user ID (if logged in), or phone number.
-
Examples:
- Limit
/api/otp/sendto X requests per phone number per hour. - Limit
/api/otp/verifyto Y attempts persession_uuid. - Limit overall requests per IP address.
- Limit
-
Use libraries like
rate-limiter-flexibleorexpress-rate-limit(if using Express middleware with Next.js, or adapt the logic). Vercel and Netlify also offer built-in or add-on solutions for edge rate limiting. -
Example using
rate-limiter-flexible(conceptual):javascript// pages/api/otp/send.js (or verify.js) - Simplified Example import { Client } from 'plivo'; import { RateLimiterMemory } from 'rate-limiter-flexible'; // Configure rate limiter (e.g., 5 requests per IP per minute) // Store this limiter instance appropriately (e.g., outside handler for reuse) const rateLimiter = new RateLimiterMemory({ points: 5, // Number of points duration: 60, // Per second(s) }); // ... (Plivo client setup) ... export default async function handler(req, res) { if (req.method !== 'POST') { /* ... */ } const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress; try { await rateLimiter.consume(ip); // Consume 1 point per request for this IP } catch (rejRes) { console.warn(`Rate limit exceeded for IP: ${ip}`); return res.status(429).json({ message: 'Too Many Requests' }); } // --- Proceed with OTP logic if rate limit not exceeded --- const { recipient } = req.body; // ... validation ... try { // ... Plivo API call ... res.status(200).json({ /* ... */ }); } catch (error) { // ... error handling ... res.status(500).json({ message: 'Failed to process request' }); } }
-
Source Citations
Node.js Version Information:
- Node.js Release Schedule: https://nodejs.org/en/about/previous-releases
- Node.js v22 LTS announcement: https://nodesource.com/blog/Node.js-v22-Long-Term-Support-LTS
- Node.js End-of-Life Dates: https://endoflife.date/nodejs
Next.js Framework:
- Next.js 15 Release Notes: https://nextjs.org/blog/next-15
- Next.js App Router Documentation: https://nextjs.org/docs/app
- Next.js Pages Router Documentation: https://nextjs.org/docs/pages
Plivo SDK and Verify API:
- Plivo Node.js SDK Documentation: https://www.plivo.com/docs/sdk/server/node-sdk
- Plivo Node.js SDK (npm): https://www.npmjs.com/package/plivo
- Plivo Verify API Overview: https://www.plivo.com/verify/
- Plivo Session API Reference: https://www.plivo.com/docs/verify/api/session
- Create a Session: https://www.plivo.com/docs/verify/api/session/create-a-session
- Send and Validate Code Guide: https://www.plivo.com/docs/verify/concepts/send-and-validate-code