Frequently Asked Questions
Axios, a promise-based HTTP client, is used for making API calls from both the frontend (React) to the backend (Node.js) and the backend to the Infobip API.
Implement Infobip 2FA by setting up a Vite/React frontend, a Node.js/Express backend, and configuring Infobip's OTP service. The frontend sends API requests to the backend, which interacts with Infobip to send and verify OTPs via SMS. This guide provides step-by-step instructions and code examples for a complete setup.
Infobip provides a reliable and global communication platform with robust 2FA APIs, enabling secure user accounts and critical actions by verifying possession of a phone number through OTP, mitigating risks associated with compromised passwords.
The article recommends a monorepo with separate 'client' and 'server' directories. This helps organize the frontend (Vite/React) and backend (Node.js) code cleanly within a single project.
Configure environment variables before running the application. Store sensitive credentials like Infobip API keys, base URL, Application ID, and Message Template ID in a '.env' file on the backend (server-side). Load these via the 'dotenv' library.
Send a POST request to the correct Infobip API endpoint (e.g., your_instance.api.infobip.com/2fa/2/pin) with required data including 'applicationId,' 'messageId,' and the user's phone number in E.164 format, using appropriate authorization headers.
Send a POST request to the Infobip verification API endpoint (e.g., .../2fa/2/pin/PIN_ID/verify) containing the OTP entered by the user. In a production environment, retrieve the 'pinId' from server-side session storage.
The 'pinId' links the OTP request to the verification attempt. In production, store it securely on the server-side, associating it with the user's session. NEVER expose it client-side, unlike the demo code, as this introduces vulnerabilities.
The project uses a Vite (React) frontend for the user interface, a Node.js/Express backend to handle logic and API calls, and Infobip as the OTP provider. The user interacts with the frontend, which communicates with the backend, which in turn interacts with Infobip.
While Axios is recommended for its ease of use, other HTTP clients like 'node-fetch' or the built-in 'http' module can be used by adjusting the request-handling code on the backend (Node.js).
Configure the proxy in the 'vite.config.js' file to forward '/api' requests to your backend server address, like 'http://localhost:3001', to simplify development and avoid CORS issues.
Error codes include 401 (Unauthorized), 400 (Bad Request—often validation errors), 403 (Forbidden), and 500 (Internal Server Error). The article details these and potential causes, such as incorrect API key format, bad phone number format, or invalid application/message IDs.
CORS (Cross-Origin Resource Sharing) configuration is necessary to allow the frontend running on one port (e.g., 5173) to access the backend API running on a different port (e.g., 3001), which is common in development.
Key security practices include protecting your API keys, implementing rate limiting, using HTTPS, securing the 'pinId' server-side, and validating all user inputs.
OTP expiry is defined in the Infobip 2FA Application settings (PIN Time To Live or TTL). Set this to a short duration, like 5 minutes (5m), to enhance security.
Enhance your application's security by adding a robust Two-Factor Authentication (2FA) layer using Infobip's OTP service. This guide provides a complete walkthrough for integrating Infobip 2FA into a system with a Vite (React) frontend and a Node.js backend.
We'll build a simple application where a user can enter their phone number, receive an OTP via SMS powered by Infobip, and verify that OTP to gain access or confirm an action. This guide focuses on the core OTP request and verification flow.
Project Overview and Goals
Problem Solved: Secure user accounts and critical actions by verifying possession of a phone number through OTP, mitigating risks associated with compromised passwords.
Technologies Used:
Architecture:
Outcome: A functional demonstration featuring a React frontend allowing users to request and verify OTPs sent via Infobip_ orchestrated by a Node.js backend API.
Prerequisites:
1. Setting Up the Project
We'll create a monorepo structure with separate directories for the client (Vite/React) and server (Node.js).
1. Create Project Directory:
2. Set Up Backend (Node.js/Express):
express
: Web framework for Node.js.dotenv
: Loads environment variables from a.env
file.cors
: Enables Cross-Origin Resource Sharing (necessary for frontend interaction).axios
: Used to make HTTP requests to the Infobip API.Project Structure (Server):
3. Set Up Frontend (Vite/React):
Navigate back to the root directory (
infobip-2fa-app
):Project Structure (Client):
4. Configure Environment Variables (Backend):
Open the
server/.env
file and add placeholders for your Infobip credentials and configuration. You'll obtain these values later.dotenv
makes these accessible viaprocess.env
.2. Building the Backend API Layer
We'll create two endpoints in our Express server: one to request an OTP and another to verify it.
Edit
server/server.js
:axios
for Infobip API calls due to its simplicity and promise-based nature.Authorization
,Content-Type
,Accept
) as required by Infobip./api/otp/request
endpoint in this demonstration returns thepinId
directly to the client. DO NOT DO THIS IN A PRODUCTION ENVIRONMENT. ThepinId
must be stored securely on the server (e.g., associated with the user's session) and never exposed client-side. This example code prioritizes simplicity for demonstration over production security practices regardingpinId
handling. See the code comments for more details.3. Integrating with Infobip
This is the core of the 2FA process. You need to configure Infobip and retrieve the necessary credentials.
1. Sign Up/Log In to Infobip:
2. Obtain Base URL and API Key:
your_unique_id.api.infobip.com
) and generate an API Key..env
: Paste these values intoserver/.env
forINFOBIP_BASE_URL
andINFOBIP_API_KEY
. Treat the API Key like a password—keep it secret!3. Create a 2FA Application:
5m
for 5 minutes).sendPinPerPhoneNumberLimit: 5/1d
- 5 pins per number per day).applicationId
)..env
: Paste this ID intoserver/.env
forINFOBIP_APP_ID
.4. Create a 2FA Message Template:
NUMERIC
(most common).{{pin}}
placeholder where Infobip will insert the generated OTP. Example:Your verification code for My App is: {{pin}}. It expires in 5 minutes.
messageId
)..env
: Paste this ID intoserver/.env
forINFOBIP_MSG_ID
.Now your
server/.env
file should have all the necessary values filled in.4. Implementing the Frontend (Vite/React)
Let's create a simple React component to interact with our backend API.
1. Configure Vite Proxy (Optional but Recommended for Development):
To avoid CORS issues during development without complex server configuration, you can proxy API requests through Vite's dev server.
Edit
client/vite.config.js
:http://localhost:5173
).2. Create the Authentication Component:
Replace the contents of
client/src/App.jsx
with the following:3. Basic Styling (Optional):
Add some simple styles to
client/src/App.css
:useState
to manage component state (phone number, OTP, messages, loading status, UI visibility).handleRequestOtp
sends the phone number to the backend/api/otp/request
endpoint. Crucially, in this demo, it receives and stores thepinId
from the backend, which is insecure. On success, it shows the OTP input field.handleVerifyOtp
sends the (insecurely obtained)pinId
and the entered OTP to the backend/api/otp/verify
endpoint.axios
to make the API calls. The URLs (/api/...
) are relative because Vite's proxy handles forwarding them to the backend.showOtpInput
.inputMode=""numeric""
andautoComplete=""one-time-code""
to the OTP input for better UX.maxLength
to Infobip configuration.5. Error Handling and Logging
server.js
):try...catch
blocks aroundaxios
calls to Infobip.console.error
). For production, use a structured logging library (like Winston or Pino) to log to files or external services, including request IDs for tracing.error.response.data
). Added specific checks for commonmessageId
values likePIN_NOT_FOUND
,WRONG_PIN
,MAX_ATTEMPTS_EXCEEDED
.{ success: false, message: '...' }
) with appropriate HTTP status codes (400, 500, etc.).App.jsx
):try...catch
blocks aroundaxios
calls to the backend API.error.response?.data?.message
).info
,error
,success
) for message styling.axios-retry
or manually within thecatch
block. However, retrying OTP verification itself is usually handled by letting the user re-enter the code or request a new one. Infobip handles rate limiting on their end based on your application settings.6. Security Considerations
INFOBIP_API_KEY
or other secrets to version control. Use.env
and ensure.env
is in your.gitignore
. In production, use secure environment variable management provided by your hosting platform.sendPinPerApplicationLimit
,sendPinPerPhoneNumberLimit
,verifyPinLimit
) to prevent abuse and control costs./api/otp/request
,/api/otp/verify
) using libraries likeexpress-rate-limit
to protect your own server resources from brute-force attempts.joi
orexpress-validator
offer more robust solutions.pinId
Handling (CRITICAL REMINDER): As stressed multiple times, do not exposepinId
to the client in production. This example does so for simplicity only. The correct approach involves storing thepinId
securely on the server-side, linked to the user's session, and retrieving it during verification based on that session.pinAttempts
setting helps mitigate OTP brute-forcing. Your backend rate limiting adds another layer.7. Troubleshooting and Caveats
401 Unauthorized
: IncorrectINFOBIP_API_KEY
or Base URL. Check.env
and Infobip portal. Ensure theAuthorization
header format isApp YOUR_API_KEY
.400 Bad Request
(on Request OTP): Often invalid phone number format (needs E.164), incorrectapplicationId
ormessageId
, or missing required fields. Check Infobip API docs and your request payload. Also check Infobip rate limits.400 Bad Request
(on Verify OTP):PIN_NOT_FOUND
(invalidpinId
provided by client (due to demo flaw) or expired session),WRONG_PIN
(incorrect OTP entered),MAX_ATTEMPTS_EXCEEDED
(configured limit reached). CheckpinId
handling (server-side in production!), user input, and Infobip settings.403 Forbidden
: Sometimes related to sender ID issues, geographic restrictions, or account permissions. Check Infobip configuration and account status.500 Internal Server Error
: Issue on Infobip's side. Retry might be appropriate depending on the context.INFOBIP_APP_ID
andINFOBIP_MSG_ID
in.env
exactly match the IDs created in the Infobip portal. Ensure thePIN Length
configured in the Infobip Message Template matches themaxLength
attribute on the OTP input field in the frontend (App.jsx
).cors
middleware on the backend (server.js
) correctly allows the origin of your frontend (e.g.,http://localhost:5173
). Check the browser's developer console for specific CORS error messages.447123456789
for a UK number,14155552671
for a US number). Ensure users input numbers correctly or implement frontend/backend formatting/validation.pinId
in this example (passing it to the client) is insecure and for demonstration purposes only. Implement server-side storage forpinId
in any real application.