Frequently Asked Questions
Implement SMS OTP 2FA using Express.js, the Infobip 2FA API, and environment variables for API keys. This setup allows you to send and verify one-time passwords, enhancing security for actions like login or registration by requiring a second verification step beyond a password.
The Infobip 2FA API is used for sending and verifying one-time passwords (OTPs) via various channels like SMS, voice calls, or email. This guide focuses on SMS OTPs, adding an extra layer of security to user accounts.
The express-rate-limit middleware helps protect your Node.js application from abuse by limiting the number of OTP requests and verification attempts from a single IP address within a timeframe, enhancing security.
Always use environment variables for sensitive information like API keys. The dotenv module in Node.js helps manage environment variables, ensuring your credentials are not exposed in your source code.
Send a POST request to Infobip's /2fa/2/pin endpoint using a library like axios. Provide your application ID, message ID, and user's phone number. The response contains a pinId, which should be stored securely on the server.
Use the pinId received from the send OTP request, store it securely on the server, and the user-provided OTP to make a POST request to Infobip's /2fa/2/pin/{pinId}/verify endpoint. This process confirms if the OTP is valid.
The pinId is a unique identifier for each OTP sent by Infobip's 2FA API. It's crucial for server-side tracking and verifying the correct OTP against the user's attempt, maintaining security and preventing unauthorized access.
Rate limiting protects against brute-force attacks and prevents abuse. It limits how many OTP requests can be made within a certain timeframe, enhancing security.
Clean up the stored pinId and other OTP-related data immediately after successful verification or after a defined number of failed attempts, or upon expiry. This is important for security best practices.
No, storing pinId in an in-memory object like otpStore is NOT suitable for production. Use a database (like Redis, PostgreSQL, MongoDB) or proper session management (like express-session) with a persistent store instead.
Essential libraries include express for the web framework, dotenv for managing environment variables, axios for making API requests, and express-rate-limit for security. Optionally, a database connector or session management library is needed for production.
Handle errors by checking the error response from the Infobip API. For expected errors like WRONG_PIN, return false. For other errors, log details and return generic error messages to the client, protecting sensitive information.
The project includes server.js for the main application, infobipService.js for API interaction logic, .env for configuration, .gitignore for version control, node_modules, package.json, and package-lock.json. This structure organizes the core components of the application.
Two-Factor Authentication (2FA) adds a crucial layer of security to user accounts by requiring a second form of verification beyond just a password. One of the most common 2FA methods is One-Time Passwords (OTP) delivered via SMS.
This guide shows you how to implement SMS-based OTP verification in a Node.js application using the Express framework and the Infobip 2FA API. You'll build a simple Express application with endpoints to request an OTP via SMS and verify the OTP entered by the user.
What You'll Learn:
Project Goals:
Technologies Used:
.envfile.pinId. This guide uses a simple in-memory store for demonstration, but provides schema guidance for a real database.System Architecture:
Prerequisites:
curl).1. Setting Up Your Node.js OTP Project
Initialize your Node.js project and install the necessary dependencies for SMS authentication.
1.1 Create Project Directory
Open your terminal and create a new directory for the project:
1.2 Initialize npm
This creates a
package.jsonfile.1.3 Install Dependencies
express: The web framework.dotenv: Manages environment variables for API keys and configurations.axios: Makes HTTP requests to the Infobip API.express-rate-limit: Prevents abuse of OTP endpoints.1.4 Project Structure
Create the following basic structure:
1.5 Configure
.gitignoreCreate a
.gitignorefile in the root directory and addnode_modulesand.envto prevent committing them to version control:1.6 Create
.envFileCreate a
.envfile in the root directory. Populate this with your Infobip credentials and configuration obtained in Section 4.Purpose of Configuration: Using environment variables (
dotenv) is crucial for security and flexibility. It keeps sensitive credentials like API keys out of your source code and allows different configurations for development, staging, and production environments.2. Building the Infobip SMS OTP Service
Create functions to interact with the Infobip 2FA API: sending the OTP and verifying it. Encapsulating third-party API interactions follows best practices.
Create a new file,
infobipService.js:Explanation:
axiosand load necessary environment variables usingdotenv.config().axiosinstance (infobipAxios) is created with the base URL and default headers (Authorization, Content-Type, Accept) required by Infobip. The API key is included in theAuthorizationheader.sendOtp:phoneNumberas input./2fa/2/pinendpoint.applicationId,messageId, andto(phone number).pinIdfrom the response. ThispinIdmust be kept server-side and associated securely with the user's session or verification attempt.verifyOtp:pinId(retrieved from server-side storage) and theotpCodeentered by the user./2fa/2/pin/{pinId}/verifyendpoint.pin(user's code) in the request body.trueifresponse.data.verifiedis true.WRONG_PIN,PIN_EXPIRED,TOO_MANY_ATTEMPTSand returnsfalseinstead of throwing an error, as these are expected verification outcomes.3. Creating Express API Endpoints for OTP Verification
Set up the Express server and create the API endpoints for sending and verifying SMS codes.
Update
server.js:Explanation:
helmet()adds basic security headers.express.json()parses JSON bodies.express-rate-limitinstances (otpSendLimiter,otpVerifyLimiter) are applied to the respective routes. This is crucial for security. Adjust limits as needed.otpStore): An in-memory objectotpStoreis used for demonstration only. This is NOT production-ready. It linkspinIdtophoneNumber.express-sessionwould be used in a real application. The key steps are:express-sessionwith a secret and persistent store.sendOtpsucceeds, storepinId, associatedphoneNumber, andtimestampinreq.session./verify-otp, retrieve these details fromreq.session, perform necessary checks (e.g., doesreq.body.phoneNumbermatchreq.session.otpPhoneNumber?), and then callverifyOtp./api/send-otpRoute (POST):otpSendLimiter.phoneNumber(emphasizing need for better validation).infobipService.sendOtp.pinIdserver-side (usingotpStoredemo orreq.session).pinId./api/verify-otpRoute (POST):otpVerifyLimiter.phoneNumberandotpCode.pinIdfrom server-side storage (otpStoredemo orreq.session).infobipService.verifyOtpwith the retrievedpinIdand the user-providedotpCode.isVerifiedis true, cleans up storage and returns success. This is the point to grant access/complete the action.isVerifiedis false (wrong code, expired on Infobip), returns a400error..env, starts the Express server, logs essential information, including a warning if Infobip variables aren't set.module.exports = app;is added to allow importing theappinstance for automated testing (see Section 13).4. Configuring Infobip API Credentials for SMS OTP
Get your credentials from Infobip and set up the necessary 2FA Application and Message Template.
4.1 Obtain Infobip API Key and Base URL
Log in to your Infobip Portal.
Your Base URL is usually displayed prominently on the homepage after login, often within an ""API Key Management"" or ""Developer Tools"" section, or mentioned in the API documentation landing page specific to your account. It will look something like
xxxxxx.api.infobip.com. Find the correct URL for your account region.Navigate to the API Key management section (typically found under your account settings, developer tools, or a dedicated ""API"" menu item).
Create a new API key. Give it a descriptive name (e.g., ""Node OTP App Key"").
Securely copy the generated API Key immediately. You will not be able to view it again after closing the creation dialog. Store it safely.
Update your
.envfile (created in Section 1.6) with these values:4.2 Create Infobip 2FA Application and Message Template
You need an Application and a Message Template within Infobip to define the behavior (PIN length, expiry, attempts) and content of your OTP messages. You can usually do this via the Infobip API or potentially through their web portal (the availability and location of UI configuration for 2FA might change, check their documentation or portal interface). We outline the API approach here.
Using Infobip Portal (If Available): Explore the portal for sections like ""Apps"", ""Channels"", ""Verify"", or ""2FA"". Look for options to create a new ""Application"" (specifically for 2FA/Verify) and associated ""Message Templates"". Configure settings like PIN type, PIN length, validity time (
pinTimeToLive), allowed attempts (pinAttempts), and the message text (critically, include the{{pin}}placeholder where the code should appear). If you create them via the UI, carefully note down the generated Application ID and Message ID.Using API (Recommended for Automation/Consistency): Use
curl, Postman, or an HTTP client in your preferred language with the Base URL and API Key obtained in Section 4.1. Note: Always refer to the latest Infobip 2FA API Documentation for the most current endpoints and request/response structures.a) Create 2FA Application: Send a POST request to
https://<YOUR_BASE_URL>/2fa/2/applications. Replace<YOUR_BASE_URL>with the value you added to your.envfile.Request (
curlexample - replace placeholders!):Adjust configuration values (like
pinAttempts,pinTimeToLive, rate limits) based on your security requirements.Example Successful Response:
Copy the
applicationIdfrom the response.b) Create 2FA Message Template: Send a POST request to
https://<YOUR_BASE_URL>/2fa/2/applications/<YOUR_APPLICATION_ID>/messages. Replace<YOUR_BASE_URL>and<YOUR_APPLICATION_ID>with your Base URL and the Application ID you just received.Request (
curlexample - replace placeholders!):Ensure
{{pin}}is included inmessageText. AdjustpinLength,pinTypeas needed.senderIdmight need configuration/approval depending on the destination country.Example Successful Response:
Copy the
messageIdfrom the response.4.3 Update
.envFileAdd the obtained
applicationIdandmessageIdto your.envfile:Explanation of Variables:
INFOBIP_BASE_URL: The unique API endpoint URL provided by Infobip for your account.INFOBIP_API_KEY: Your secret key for authenticating API requests. Treat this like a password.INFOBIP_2FA_APP_ID: Identifies the specific 2FA application configuration (rate limits, attempts, expiry) to use.INFOBIP_2FA_MSG_ID: Identifies the specific message template (text, PIN length, sender ID) to use when sending the OTP SMS.Now your application is configured to communicate with the correct Infobip resources using the settings you defined.
5. OTP Error Handling and Security Best Practices
Error Handling Strategy:
infobipService.jsfunctions now attempt to catch specific API errors from Infobip (inspectingerror.response.data). Expected failures likeWRONG_PINorPIN_EXPIREDduring verification result inverifyOtpreturningfalse, which the route handler translates into a user-friendly 400 response. Other API errors (config, auth, network) are caught and logged server-side, resulting in a 500 response with a generic message.try...catchblocks are used in route handlers and service functions to catch unexpected JavaScript errors or issues within the application logic. A basic global error handler is added toserver.jsas a fallback.messageorerrorfields suitable for client-side display (e.g., ""Invalid or expired OTP code."", ""Too many requests...""). Sensitive internal details are not exposed to the client.(Code in
infobipService.jsandserver.jsincludes improved error handling.)Logging:
Use a dedicated logging library in production (like
winstonorpino) for structured, leveled logging (info, warn, error) and easy integration with log management systems. For this guide, we useconsole.log,console.warn, andconsole.error.pinIdserver-side only), verification attempt, verification result (success/failure).