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 pinId
s 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 pinId
s 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:
.env
fileSystem 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
pinId
back, 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-otp
and/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.json
file.3. Install Dependencies: Install Express for the web server,
dotenv
for environment variable management, andaxios
for 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_modules
and.env
to your.gitignore
file to prevent committing sensitive information and dependencies.6. Basic Express Server Setup: Add the following initial code to
index.js
to set up a basic Express server.You can run this basic server to ensure setup is correct:
You should see
Server running on port 3000
in 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.env
file and add your Infobip credentials.Replace
YOUR_COPIED_API_KEY
andYOUR_BASE_URL
with your actual values. Do not commit the.env
file to version control.3. Create Infobip 2FA Application: An Infobip
Application
defines the behavior and rules for your 2FA flow (like PIN attempts, validity time). Create one using the Infobip API.YOUR_BASE_URL
andYOUR_API_KEY
with your actual credentials in the command belowApp
, followed by a space, then your API key:-H 'Authorization: App YOUR_API_KEY'
curl
command: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 copiedapplicationId
to your.env
file: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_ID
in the command belowsenderId
is 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_KEY
formatcurl
command: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 copiedmessageId
to your.env
file: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
pinId
associated with the phone number (temporarily in memory for this example).applicationId
andmessageId
to
) as input. Ensure it's in the correct format (usually E.164, e.g.,+14155552671
or+442071838750
). Infobip is generally flexible but E.164 is standardPOST
request to/2fa/2/pin
pinId
from the successful response. ThispinId
is essential for the verification steppinId
in the simpleactivePinIds
object, 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
pinId
from the temporary store, and calls the Infobip API to verify the PIN.pinId
from theactivePinIds
store using the phone number. Handles the case where nopinId
is found (e.g., expired or never sent)POST
request to/2fa/2/pin/{pinId}/verify
, including thepinId
in the URL and the user's submittedpin
in the bodyverified
property in the response (true/false)pinId
from storage upon successful verification to prevent reuse. This relies on the temporary storeTOO_MANY_ATTEMPTS
or404 Not Found
(which usually indicates an expired/invalidpinId
)4. Creating Express API Endpoints for OTP
Now, expose the
sendOtp
andverifyOtp
functions through Express API endpoints.1. Define API Routes: Add the following route handlers in
index.js
before theapp.listen
call./send-otp
(POST): ExpectsphoneNumber
in the JSON body. CallssendOtp
. Returns a success message. Explicitly states the security reason for not returning thepinId
to the client. Basic phone number format validation added/verify-otp
(POST): ExpectsphoneNumber
and thepin
code in the JSON body. CallsverifyOtp
(which currently depends on the temporaryactivePinIds
store). Returns success (200) or failure (401) based on the result. Basic PIN and phone number validation added2. Testing with
curl
or Postman:Start the server:
node index.js
Send OTP: (Replace
+1XXXXXXXXXX
with 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
+1XXXXXXXXXX
with the same phone number and123456
with 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...catch
blocks and logging. Refine this approach for production.console.log
andconsole.error
. For production, use a dedicated logging library (like Winston or Pino) to:allowMultiplePinVerifications
is true, but it uses up an attempt)/send-otp
endpoint 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-retry
if 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 activepinId
s. 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
activePinIds
object with a persistent and potentially shared data store before deploying. Common choices include:Using Redis for OTP Storage (Recommended)
pinId
s because it has built-in support for Time-To-Live (TTL). Set the TTL to match thepinTimeToLive
configured 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
users
table or create a separateotp_attempts
table. This requires manual cleanup of expired entries (e.g., via a scheduled job)users
table 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_attempts
table):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-otp
than/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
.env
is 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
pinTimeToLive
setting.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.,+14155552671
for US,+442071838750
for UK). Use thelibphonenumber-js
library 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.