Frequently Asked Questions
Implement 2FA by integrating the Infobip 2FA API into your Node.js Express app. Create API endpoints to send OTPs via SMS to the user's phone number and then verify the code they enter. This enhances security by adding an extra layer of verification beyond passwords.
The Infobip 2FA API is the core service used for generating, sending, and verifying one-time passwords (OTPs) through various channels like SMS, voice calls, or email. This guide focuses specifically on using SMS for delivering OTPs.
Infobip simplifies OTP implementation by handling the complexities of generation, delivery, and verification. It allows developers to focus on their application logic rather than managing SMS gateways and other infrastructure.
To send an OTP, make a POST request to the /2fa/2/pin endpoint of the Infobip API. Provide the application ID, message ID, and the user's phone number in the request body.
The pinId should be removed from your data store immediately after successful verification. This prevents its reuse and enhances security. It's important to use a persistent data store like Redis or a database for managing pinIds in production.
No, using in-memory storage like the example's activePinIds is unsuitable for production. This is because data is lost on server restarts, and it doesn't work with multiple server instances. Use Redis or a database instead.
Verify the OTP by making a POST request to /2fa/2/pin/{pinId}/verify, providing the pinId (received from the send OTP request) and user-entered OTP. The response will indicate whether verification was successful.
The recommended format for phone numbers when using Infobip is E.164. This international standard format ensures consistent and reliable delivery. An example is +14155552671.
Improve error handling by using specific HTTP status codes (400 for bad input, 500 for server errors, 401 for invalid OTPs) and logging detailed errors server-side while providing generic messages to the client.
Use rate limiting to prevent brute-force attacks, validate inputs thoroughly, secure your API credentials, and ensure that the pinId is not exposed to the client in responses.
Returning the pinId to the client is a security risk. It's a server-side identifier. Exposing it unnecessarily could lead to potential misuse or information leakage if not carefully handled client-side.
Redis is recommended for storing pinIds due to its speed and built-in support for Time-To-Live (TTL), which automatically expires entries. A relational database can also be used but requires manual cleanup of expired entries.
Set up Infobip by creating a 2FA application and a message template in your Infobip account. You then store the application ID, message ID, API key, and base URL securely in your application's environment variables.
This guide utilizes Node.js with Express.js for the web server, the Infobip 2FA API for OTP services, Axios for HTTP requests, and dotenv for managing environment variables.
How to Implement SMS OTP Verification in Node.js with Express and Infobip
Two-factor authentication (2FA) adds a crucial layer of security to user accounts by requiring a second verification step beyond just a password. One common and user-friendly method for 2FA uses One-Time Passwords (OTP) sent via SMS.
This guide provides a complete walkthrough for implementing SMS-based OTP verification in a Node.js application using the Express framework and the Infobip 2FA API. You'll build a simple API that sends an OTP to a user's phone number and verifies the code they enter.
Project Goals:
Technologies Used:
.envfileSystem Architecture:
The basic flow involves your Node.js application interacting with the Infobip API. Your Node.js app receives requests from the client, forwards OTP generation requests to Infobip, receives the
pinIdback, responds to the client that the OTP is sent, and later receives verification requests from the client to validate against Infobip. Infobip handles sending the SMS to the user after your Node.js app requests it.Prerequisites:
Final Outcome:
By the end of this guide, you'll have a functional Node.js Express API with two endpoints:
/send-otpand/verify-otp. This API leverages Infobip to handle the complexities of OTP generation, delivery via SMS, and verification, ready to integrate into a larger application's authentication flow.1. Setting Up Your Node.js OTP Project
Start by creating your Node.js project and installing the necessary dependencies.
1. Create Project Directory: Open your terminal and create a new directory for the project.
2. Initialize Node.js Project: Initialize the project using npm (or yarn). This creates a
package.jsonfile.3. Install Dependencies: Install Express for the web server,
dotenvfor environment variable management, andaxiosfor making HTTP requests to the Infobip API.4. Project Structure: Create the basic files and folders.
Your initial structure should look like this:
5. Configure
.gitignore: Addnode_modulesand.envto your.gitignorefile to prevent committing sensitive information and dependencies.6. Basic Express Server Setup: Add the following initial code to
index.jsto set up a basic Express server.You can run this basic server to ensure setup is correct:
You should see
Server running on port 3000in your console. You can stop the server withCtrl+C.2. Configuring Infobip 2FA API Credentials
Before interacting with the Infobip API, configure it within Infobip and store the necessary credentials securely in your application.
1. Obtain Infobip API Key and Base URL:
your-account.api.infobip.com). The specific Base URL usually appears near your API keys or in the general API documentation entry point for your account2. Store Credentials in
.env: Open the.envfile and add your Infobip credentials.Replace
YOUR_COPIED_API_KEYandYOUR_BASE_URLwith your actual values. Do not commit the.envfile to version control.3. Create Infobip 2FA Application: An Infobip
Applicationdefines the behavior and rules for your 2FA flow (like PIN attempts, validity time). Create one using the Infobip API.YOUR_BASE_URLandYOUR_API_KEYwith your actual credentials in the command belowApp, followed by a space, then your API key:-H 'Authorization: App YOUR_API_KEY'curlcommand:name: A descriptive name for this 2FA configurationpinAttempts: Maximum number of verification attempts allowed for a single PINallowMultiplePinVerifications: Whether you can verify the same PIN multiple times (useful for testing, potentially disable in production)pinTimeToLive: How long the generated PIN remains valid (e.g., "10m" for 10 minutes)verifyPinLimit: Rate limit for verification attempts per PIN IDsendPinPerApplicationLimit: Rate limit for sending PINs across the entire applicationsendPinPerPhoneNumberLimit: Rate limit for sending PINs to a single phone numberenabled: Whether this configuration is activeapplicationId: The API response contains anapplicationId. Copy this value..env: Add the copiedapplicationIdto your.envfile:4. Create Infobip Message Template: This template defines the content of the SMS message sent to the user_ including the placeholder for the OTP code.
YOUR_BASE_URL_YOUR_API_KEY_ andYOUR_2FA_APP_IDin the command belowsenderIdis what appears as the sender on the user's phone. For trial accounts_ this might be restricted. For paid accounts_ you can often register a custom alphanumeric sender ID or use a purchased phone number. Check Infobip documentation for specifics in your region. Replace "InfoSMS" with your desired/allowed sender IDApp YOUR_API_KEYformatcurlcommand:messageText: The SMS body.{{pin}}is the mandatory placeholder for the OTPpinLength: The number of digits for the OTP (e.g._ 6)pinType: The type of PIN (NUMERIC_ALPHA_HEX_ALPHANUMERIC)senderId: The sender ID displayed to the userlanguage: Helps with potential future localization or specific character encoding needsmessageId: The API response contains amessageId. Copy this value..env: Add the copiedmessageIdto your.envfile:Now your application has the necessary credentials and configuration IDs stored securely.
3. Implementing SMS OTP Send and Verify Functions
Write the functions that interact with the Infobip API. You'll create helper functions for sending and verifying OTPs.
1. Setup Axios Instance: Create a pre-configured Axios instance for interacting with the Infobip API. Add this near the top of
index.js:> WARNING: PRODUCTION UNSUITABLE – DO NOT USE IN PRODUCTION >
2. Send OTP Function: This function takes a phone number, calls the Infobip API to send the PIN, and stores the returned
pinIdassociated with the phone number (temporarily in memory for this example).applicationIdandmessageIdto) as input. Ensure it's in the correct format (usually E.164, e.g.,+14155552671or+442071838750). Infobip is generally flexible but E.164 is standardPOSTrequest to/2fa/2/pinpinIdfrom the successful response. ThispinIdis essential for the verification steppinIdin the simpleactivePinIdsobject, mapping the phone number to its latestpinId. Replace this for production3. Verify OTP Function: This function takes the phone number, the user-submitted PIN, retrieves the corresponding
pinIdfrom the temporary store, and calls the Infobip API to verify the PIN.pinIdfrom theactivePinIdsstore using the phone number. Handles the case where nopinIdis found (e.g., expired or never sent)POSTrequest to/2fa/2/pin/{pinId}/verify, including thepinIdin the URL and the user's submittedpinin the bodyverifiedproperty in the response (true/false)pinIdfrom storage upon successful verification to prevent reuse. This relies on the temporary storeTOO_MANY_ATTEMPTSor404 Not Found(which usually indicates an expired/invalidpinId)4. Creating Express API Endpoints for OTP
Now, expose the
sendOtpandverifyOtpfunctions through Express API endpoints.1. Define API Routes: Add the following route handlers in
index.jsbefore theapp.listencall./send-otp(POST): ExpectsphoneNumberin the JSON body. CallssendOtp. Returns a success message. Explicitly states the security reason for not returning thepinIdto the client. Basic phone number format validation added/verify-otp(POST): ExpectsphoneNumberand thepincode in the JSON body. CallsverifyOtp(which currently depends on the temporaryactivePinIdsstore). Returns success (200) or failure (401) based on the result. Basic PIN and phone number validation added2. Testing with
curlor Postman:Start the server:
node index.jsSend OTP: (Replace
+1XXXXXXXXXXwith a valid phone number, preferably one associated with your Infobip trial account if applicable)Expected Response (200 OK):
You should receive an SMS on the target phone number.
Verify OTP: (Replace
+1XXXXXXXXXXwith the same phone number and123456with the actual code you received)Expected Response (Success – 200 OK):
Expected Response (Incorrect PIN – 401 Unauthorized):
5. Error Handling and Logging Best Practices
You've added basic
try...catchblocks and logging. Refine this approach for production.console.logandconsole.error. For production, use a dedicated logging library (like Winston or Pino) to:allowMultiplePinVerificationsis true, but it uses up an attempt)/send-otpendpoint from the server-side, as it could lead to multiple unwanted SMS messages and costs. If a send request fails, return an error to the client and let the user retry the action. For/verify-otp, a limited retry (e.g., 1 retry on a 5xx error or timeout from Infobip) might be acceptable, but be cautious. Implement retries with exponential backoff (wait longer between each retry) using libraries likeaxios-retryif deemed necessaryExample (Conceptual Logging Enhancement):
6. Production Database Integration for OTP Storage
The current implementation uses an in-memory object (
activePinIds) to store the mapping between phone numbers and activepinIds. This is explicitly unsuitable for production for the reasons highlighted in the warning block in Section 3 (data loss on restart, inability to scale).Production Approach: Replacing In-Memory Storage
You must replace the
activePinIdsobject with a persistent and potentially shared data store before deploying. Common choices include:Using Redis for OTP Storage (Recommended)
pinIds because it has built-in support for Time-To-Live (TTL). Set the TTL to match thepinTimeToLiveconfigured in Infobip (e.g., 10 minutes), and Redis automatically expires the entryotp:phoneNumber:+1XXXXXXXXXX, Value:pinId:YYYYYYYYYY, TTL: 600 seconds (for 10 min expiry)Using SQL Database for OTP Storage
userstable or create a separateotp_attemptstable. This requires manual cleanup of expired entries (e.g., via a scheduled job)userstable enhancement):otp_pin_id(VARCHAR, nullable): Stores the latest active pinIdotp_pin_expires_at(TIMESTAMP, nullable): Stores when the pinId expires (requires logic to check expiry)otp_attemptstable):id(PK)user_id(FK to users, optional)phone_number(VARCHAR, indexed)pin_id(VARCHAR, unique)expires_at(TIMESTAMP, indexed)verified_at(TIMESTAMP, nullable)created_at(TIMESTAMP) (requires cleanup job)Implementation Sketch (using Redis with
ioredis):Choose the approach that best fits your application's architecture and scale. Redis with TTL is generally the preferred method for this use case due to its performance and built-in expiration handling, simplifying the application logic.
7. SMS Authentication Security Best Practices
Security is paramount for authentication flows.
Rate Limiting for OTP Endpoints
Prevent brute-force attacks on both sending and verification. Infobip has built-in limits (
verifyPinLimit,sendPinPerPhoneNumberLimit), but you should also implement limits in your API layer using middleware likeexpress-rate-limit. Apply stricter limits to/verify-otpthan/send-otp.Input Validation and Phone Number Formatting
You've added basic checks. For production, use robust libraries:
express-validator: For validating request bodies, params, and queries structure and typeslibphonenumber-js: For parsing, validating, and normalizing phone numbers thoroughly across different regionsSecure Credential Storage
.envis good for local development. In production, use secrets management systems provided by your cloud provider (AWS Secrets Manager, Google Secret Manager, Azure Key Vault) or tools like HashiCorp Vault. Never commit API keys or other secrets to Git.Frequently Asked Questions (FAQ)
What is SMS OTP verification?
SMS OTP (One-Time Password) verification is a security method where a unique, temporary code is sent to a user's phone via text message. The user must enter this code to verify their identity, providing an additional authentication factor beyond just a password.
How does Infobip 2FA API work with Node.js?
Infobip 2FA API provides endpoints to generate and verify OTPs. Your Node.js application makes HTTP requests to Infobip's API to send SMS messages containing OTPs and later verify the codes users submit. Infobip handles the SMS delivery infrastructure and PIN management.
What is the difference between OTP and 2FA?
OTP (One-Time Password) is a single-use code valid for one login session or transaction. 2FA (Two-Factor Authentication) is a security process that requires two different authentication factors. SMS OTP is one common implementation of 2FA, where the password is the first factor and the OTP code is the second factor.
How long should an OTP code be valid?
OTP codes typically remain valid for 5-10 minutes. This balance provides enough time for legitimate users to receive and enter the code while minimizing the window for potential attacks. In this tutorial, we configure a 10-minute validity period via Infobip's
pinTimeToLivesetting.Is SMS OTP secure for authentication?
SMS OTP provides significantly better security than password-only authentication and is widely used. However, SMS has known vulnerabilities (SIM swapping, interception). For high-security applications, consider additional measures like app-based authenticators (TOTP), hardware tokens, or biometric authentication alongside or instead of SMS OTP.
How do I prevent OTP brute force attacks?
Implement multiple layers of protection: rate limiting on your API endpoints (using
express-rate-limit), Infobip's built-in attempt limits (pinAttempts), IP-based throttling, and temporary account lockouts after repeated failed attempts. Always log suspicious activity for monitoring.What phone number format does Infobip require?
Infobip accepts phone numbers in E.164 format, which includes the country code with a
+prefix (e.g.,+14155552671for US,+442071838750for UK). Use thelibphonenumber-jslibrary to parse and validate phone numbers from user input before sending to Infobip.Can I use Infobip for email or voice OTP instead of SMS?
Yes, Infobip 2FA API supports SMS, Voice, and Email channels. This tutorial focuses on SMS, but you can modify the message template and API calls to use voice calls or email delivery. Check Infobip's documentation for channel-specific configuration options.
How much does Infobip SMS OTP cost?
Infobip offers a free trial with credits to test the service. Production pricing varies by destination country, message volume, and whether you use shared or dedicated sender IDs. Check Infobip's pricing page or contact their sales team for specific rates for your use case.
What should I store in production instead of in-memory storage?
Use Redis (recommended) or a relational database (PostgreSQL, MySQL) to store the mapping between phone numbers and PIN IDs. Redis is preferred because it offers built-in TTL (time-to-live) for automatic expiration, matching your OTP validity period. This ensures persistence across server restarts and enables horizontal scaling.
Next Steps and Advanced Features
Now that you have a working SMS OTP verification system, consider these enhancements:
This Node.js Express OTP implementation provides a solid foundation for secure user authentication using Infobip's 2FA API.