Frequently Asked Questions
Use the Infobip API with a Node.js Express server. Create two endpoints: `/request-otp` to send the OTP and `/verify-otp` to validate it. This guide provides a step-by-step implementation for enhancing your application's security.
The Infobip API is the core service for generating, sending, and verifying OTPs via SMS. It handles the communication with the SMS gateway and manages PIN generation, validation, and expiry.
SMS OTP adds a strong layer of security by verifying user possession of a registered phone number. It helps protect against unauthorized access even if passwords are compromised.
Implement SMS OTP for sensitive actions like user registration, login, or any operation involving personal data or financial transactions. It's crucial for increasing security and building user trust.
While this guide focuses on Infobip, the general principles apply to other providers. You'll need to adapt the API calls and configuration specific to your chosen provider's documentation.
Make a POST request to Infobip's `/2fa/2/pin` endpoint, providing your Application ID, Message ID, and the user's phone number. Infobip will generate the OTP, send the SMS, and return a `pinId` for verification.
The `pinId` uniquely identifies each OTP request. It's crucial to store this securely on the server-side and associate it with the user's phone number or session for proper verification.
Send a POST request to Infobip's `/2fa/2/pin/{pinId}/verify` endpoint, including the OTP code entered by the user. Infobip will respond with a 'verified' flag indicating whether the code is correct and valid.
You need Node.js and npm, an active Infobip account, a registered phone number for testing, and a basic understanding of Node.js, Express, APIs, and asynchronous JavaScript.
In-memory storage is not suitable for production as it's not persistent, not scalable, and prone to data loss if the server restarts. Use a persistent data store like Redis or a database with TTL (Time To Live) for storing OTP data.
Use a persistent and secure data store like Redis with key expiry or a database table with fields for `pinId`, `phoneNumber`, `expiresAt`, and `isVerified`. Implement proper cleanup mechanisms for expired entries.
You'll primarily need `express`, `axios`, and `dotenv`. Optionally, use `express-validator` for input validation and `express-rate-limit` for security against brute-force attacks.
Log into your Infobip account at https://portal.infobip.com/. Your base URL is often visible on the dashboard, and the API key can be found in the API Key Management section under account settings or Developer Tools. Treat your API key like a password; keep it secure.
The guide recommends a structure with folders for `node_modules` and `services`, files like `.env`, `.gitignore`, `package.json`, and your main server file (`server.js`), keeping the project organized and maintainable.
This guide provides a step-by-step walkthrough for adding SMS-based One-Time Password (OTP) verification, a common form of Two-Factor Authentication (2FA), to your Node.js Express application using the Infobip API. We'll cover everything from initial setup to production considerations.
Enhancing application security is crucial, and OTP via SMS provides a robust layer by verifying user possession of a registered phone number. This guide aims to equip you with the knowledge to implement this feature effectively.
Project Overview and Goals
What We'll Build:
We will build a simple Node.js Express application with two core API endpoints:
/request-otp
: Accepts a user's phone number, uses the Infobip API to generate and send an OTP code via SMS to that number, and stores necessary information for verification./verify-otp
: Accepts the user's phone number and the OTP code they received, then uses the Infobip API to verify if the code is correct and valid.Problem Solved:
This implementation adds a layer of security to user actions like registration, login, or sensitive operations by verifying that the user currently possesses the phone number associated with their account. It helps prevent unauthorized access even if account credentials (like passwords) are compromised.
Technologies Used:
axios
: A promise-based HTTP client for making requests to the Infobip API.dotenv
: A module to load environment variables from a.env
file.express-validator
: For robust request validation.express-rate-limit
: To protect endpoints from brute-force attacks.System Architecture:
(Note: The visual placement of arrows F and G in the diagram might seem slightly ambiguous relative to steps 5/6/7, but the text description G->F reflects the logical sequence of responses after step 7).
Prerequisites:
Final Outcome:
A functional Express API capable of sending OTPs via Infobip SMS and verifying them, ready to be integrated into a larger application for enhanced security.
1. Setting up the Project
Let's initialize our Node.js project and install the necessary dependencies.
Create Project Directory: Open your terminal and create a new directory for the project, then navigate into it.
Initialize npm:
This creates a
package.json
file.Install Dependencies: We need Express for the server,
axios
to call the Infobip API, anddotenv
to manage environment variables.Install Development Dependencies (Optional but Recommended):
nodemon
helps during development by automatically restarting the server on file changes.Configure
nodemon
(Optional): Add adev
script to yourpackage.json
scripts
section:Create Project Structure: Organize the project for clarity. Create the following files and folders:
Create
.gitignore
: Addnode_modules
and.env
to prevent committing them to version control.Create
server.js
(Initial Setup):Create
services/infobip.js
: This file will contain the logic for interacting with the Infobip API.Create
.env
File: Add your Infobip credentials and configuration here.How to find
INFOBIP_API_KEY
andINFOBIP_BASE_URL
:https://<your-unique-id>.api.infobip.com
..env
file. Treat this key like a password.2. Implementing Core Functionality
The core logic is implemented within
server.js
(route handlers) andservices/infobip.js
(API interaction).Key Implementation Details:
initialize
):INFOBIP_APPLICATION_ID
andINFOBIP_MESSAGE_ID
are present in the environment variables (.env
).services/infobip.js
..env
file. This manual step is necessary for this example setup to prevent creating new apps/templates on every restart, which is inefficient, costly, and can clutter your Infobip account. A more advanced setup might use a separate one-time setup script./request-otp
route,sendOtp
service function):phoneNumber
from the request body.infobipService.sendOtp
, passing the phone number.sendOtp
function makes aPOST
request to Infobip's/2fa/2/pin
endpoint, including theapplicationId
,messageId
, and recipient number (to
).messageId
.pinId
in the response. ThispinId
uniquely identifies this specific OTP request.server.js
stores thispinId
temporarily (in our demootpStore
), associating it with thephoneNumber
. This in-memory store is NOT SUITABLE FOR PRODUCTION. In a real application, use secure server-side storage like Redis (with TTL), a database table (with TTL and cleanup), or encrypted session data linked to the user attempting verification. This is vital to link the subsequent verification step back to the correct OTP request and user session. The simplesetTimeout
cleanup in the example is also not robust for production./verify-otp
route,verifyOtp
service function):phoneNumber
and the submittedotp
code.pinId
previously stored for thatphoneNumber
from the temporary store. If nopinId
is found (e.g., expired, cleaned up, never requested, or server restarted with in-memory store), it returns an error.infobipService.verifyOtp
, passing the retrievedpinId
and the user's submittedotp
code.verifyOtp
function makes aPOST
request to Infobip's/2fa/2/pin/{pinId}/verify
endpoint.otp
matches the one generated for thatpinId
and if it's still valid (not expired, within attempt limits defined in the Application config).verified
boolean flag.verified
is true, the route clears the storedpinId
(it's been successfully used) and returns success. This is where you would typically update the user's status in your database (e.g., markis_phone_verified = true
).verified
is false or an error occurs (like PIN expired, which the service function identifies and potentially re-throws), it returns an appropriate error message.Alternative Approaches:
axios
calls and might offer convenience features. However, understanding the underlying API calls remains beneficial.otpStore
. Redis with key expiry is a common, performant choice. A database table with columns forpinId
,phoneNumber
(oruserId
),expiresAt
, andisVerified
is another solid option, requiring a background job or query condition to handle cleanup of expired entries.3. Building a Complete API Layer
Our
server.js
already defines the basic API layer. Let's discuss enhancements for production./request-otp
endpoint might be public (e.g., during phone verification in registration) or require user authentication (e.g., if adding/verifying a phone for an existing logged-in user). Implement checks accordingly./verify-otp
endpoint implicitly relies on thepinId
being linked to the correct user session or initial request via your chosen state management. Ensure this link is secure and cannot be manipulated. If verifying a logged-in user, double-check the verification action is authorized for that specific user's session.express-validator
: