In this two-part series, we’ll build a mobile application and backend service that allows users to authenticate using their phone numbers. To achieve this, we’ll use the following APIs and tools from Vonage:
Number Verification API: To verify that the phone attempting to authenticate belongs to the intended user.
Network Enablement API: To facilitate and optimize the authentication process.
Vonage Client SDK: To ensure the application uses mobile data instead of a WiFi connection for authentication.
The first part of the series will focus on the backend implementation, while the second part will cover the development of the mobile application.
Note: Number Verification API is currently in beta, and some features may change.
Use Case
Imagine you want to improve the security of your platform or service to protect your users and prevent fraud. The standard username and password combination is no longer enough in today’s digital landscape, where data breaches and phishing attacks are increasingly common.
To address this, you decide to implement a two-factor authentication (2FA) process to provide your users with an additional layer of protection. Instead of relying on traditional SMS-based 2FA, which can be vulnerable to SIM-swapping and other attacks, let’s choose a different method: authentication based on your users' phone numbers.
Let’s dive in and see how the Network Verification API can help you implement this!
Source Code
The complete source code for this tutorial can be found in this GitHub repository.
Prerequisites
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
You'll also need an application set up in the Vonage Dashboard with the Network Registry capability enabled.
The tutorial will make use of the Sandbox for Network APIs. That means you can build and test the application without costs, even in countries where Network APIs are not yet available. To enable the Sandbox, enable the Sandbox option when you create the application in the Dashboard:
Architecture
The application will use:
Node.js with Express backend implementation.
Android mobile application.
Backend Implementation
The backend will handle the incoming requests from the mobile application and implement the corresponding API calls.
Our backend will expose two endpoints:
login- The mobile application will call this endpoint via a POST request to initiate the user authentication process. In this method, we’ll call the Network Enablement API to bootstrap the authentication flow.
callback- The authentication server will trigger this endpoint as a callback. It will complete the authentication flow and call the Number Verification API to validate the user.
Login
This method initializes the authentication flow by calling the Network Enablement API. This API improves the existing authentication flow, particularly those requiring client authentication, such as the Number Verification API, by increasing performance, privacy, and security.
The login method handles a POST request from the mobile application. The request body should contain the following structure:
{
"phone": "+9901234567"
}
Helper Function for API Calls
To streamline interactions with various APIs, we’ll define a reusable helper function, makeFetchRequest, to handle API calls:
const makeFetchRequest = async (url, options) => {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.text();
throw new Error(`HTTP Error: ${response.status} - ${error}`);
}
return response.json();
};
Network Enablement API call
To call the Network Enablement API, the backend prepares a POST request with the following body:
const data = {
phone_number: phone,
scopes: [
"dpv:FraudPreventionAndDetection#number-verification-verify-read"
],
state: generateRandomString(20),
};
Where:
phone_number, corresponds with the phone number provided by the mobile app.
scopes contains a list of scopes. In our example, the list includes the scope for Number Verification API.
state is a 20-digit random string.
The request must include two headers: JWT for authentication and the appropriate content type.
const headers = {
Authorization: `Bearer ${jwt}`,
"Content-Type": "application/json",
};
Using makeFetchRequest, the backend sends a POST request to the Network Enablement API. The API’s JSON response includes an auth_url field, which is returned to the mobile application to proceed with the authentication flow:
// Network Enablement API call
const response = await makeFetchRequest(ne_uri, {
method: "POST",
headers: headers,
body: JSON.stringify(data),
});
auth_url = { url: response.scopes[fraud_scope].auth_url }
res.json(auth_url);
Response to the Mobile Application
The response sent back to the mobile application will contain a body similar to this example:
{
"url": "https://api-eu.vonage.com/oauth2/auth?client_id=auth_test&scope=dpv%3AFraudPreventionAndDetection%23number-verification-verify-read&state=v2_abdc1234-aaa-bbb-cccc&redirect_uri=https%3A%2F%2Fui.idp.vonage.com%2Fcamara%2Foauth%2Fcallback&response_type=code"
}
The mobile application will use this URL to complete the authentication process.
Callback Endpoint
This endpoint will handle the callback from the authentication server once the mobile application has requested the URL provided by our login endpoint.
Set up the Number Verification Redirect
Before we continue with the next step, let’s open the Dashboard and go to the application you created in the first step of this tutorial. Click on Edit and scroll down to the list of capabilities. Let’s modify the value of the Number Verification Redirect input text.
The Number Verification Redirect field specifies the host and endpoint that will handle the callbacks from the Vonage authentication server. If you’re developing your backend locally, we recommend using tools like ngrok or pinggy to expose your locally running backend to the Internet.
Remember to include the /callback path after the hostname. For example: https://myhost.com/callback
Authorization Code exchange
The callback from the authentication server will contain the authorization code and the same state value we set in the Network Enablement API call. We are ready to exchange this authorization code for an access token.
To do so, we need to send a POST request with a URL-encoded body containing the authorization code, the redirect URI, which should be the same as the value we entered in the dashboard, and the grant type (always set to authorization_code):
// Exchange authorization code for an access token
const tokenResponse = await makeFetchRequest(camara_auth_uri, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Bearer ${jwt}`,
},
body: new URLSearchParams({
code,
redirect_uri: redirect_ui,
grant_type: "authorization_code",
}),
});
Number Verification API call
We can use the access token to call the Number Verification API. This API provides a method for verifying that the phone number is indeed linked to the SIM card in the device connected to the mobile data network, ensuring reliable user authentication.
To make the API call, send a POST request including:
Authorization header with the access token.
Request body, containing the phone number to be verified.
Here is the code of the API call:
// Call Number Verification API
const nvResponse = await makeFetchRequest(uri, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({ phoneNumber: phone }),
});
The Number Verification API will return a boolean value:
true: The request originated from the device associated with the specified phone number.
false: The request did not match the user's phone number.
Once we receive the response, we can relay the result to the frontend to inform the user whether their authentication was successful.
Test the Backend
Now, it’s time to test our backend implementation. Start by cloning the repository and setting up your environment:
Configure the environment variables. Go to the server folder and create (or copy) a .env file with the following variables:
JWT, with the value of a new token. This token must be generated using the application ID and private key from the application created on the first step of this tutorial. You can use this online tool to generate the token.
REDIRECT_URI, must point to the URI we introduced in the Network registry dashboard settings.
Run the server. Open a terminal and start the backend as follows:
node server.js
Since we haven’t implemented the mobile application yet, we can use a Python script as a mock client to interact with the backend and test its functionality. The script implements two requests
login: Sends a request to the /login backend endpoint to retrieve the authorization URL.
def login(phone):
data = { 'phone': phone }
response = requests.post("http://localhost:3000/login", json=data)
return json.loads(response.content)['url']
auth: Uses the authorization URL to continue with the authentication flow on backend side and receive the boolean response from the Number Verification API.
def auth(url):
response = requests.get(url, verify=False)
return json.loads(response.content)["devicePhoneNumberVerified"]
Finally, the main function of the Python script will call the previous functions and print out the result of the authentication process:
url = login(args["phone"])
if auth(url):
print("Client successfully authenticated!")
else:
print("Unable to verify client credentials")
With the server running in one terminal, open a second terminal and run the Python script named client.py. For testing, use a fake phone number (see example below) with the +990 prefix. According to the Sandbox documentation, all requests using the fake +990 prefix will be routed to the Virtual CSP.
The Virtual CSP determines the response of the Number Verification API based on the phone number:
True if the last digit of the phone number is even.
False if the last digit of the phone number is odd.
Example usage of the Python script:
$ python client.py +99012345678
Client successfully authenticated!
$ python client.py +99012345677
Unable to verify client credentials
Conclusions
If you have reached this point, congratulations! You should have a backend ready to be integrated with our mobile app.
Here are the key takeaways:
The Network Enablement API improves the authentication process by reducing latency. No more manually building the authorization URL.
The Number Verification API adds an extra layer of security to your service by ensuring that the phone number used for authentication belongs to the intended user, enhancing both security and reliability.
You’ve learned how to move beyond traditional authentication methods by leveraging phone-number-based 2FA, which offers better protection against fraud and unauthorized access.
Stay tuned for part two!
Get in Touch
Do you have questions? We are here to help! Join us on the Vonage Developer Community Slack or message us on X, and we will respond to you.
Alvaro is a developer advocate at Vonage, focusing on Network APIs. Passionate about Developer Experience, APIs, and Open Source. Outside work, you can often find him exploring comic shops, attending sci-fi and horror festivals or crafting stuff with those renowned tiny plastic building blocks.