Improve Your Multifactor Auth With Verify and SIM Swap APIs

Amanda Cavallaro - Aug 23 - - Dev Community

Introduction

SIM Swap attacks impose many risks, including identity theft and identity takeover, to name a few. This tutorial will showcase how to improve security when resetting passwords by checking whether your SIM card was swapped from your mobile phone and sending an SMS to recover the account. The flow uses the Verify API to verify a token and the SIM Swap API to detect if a SIM card was swapped in the last few days. If so, the verification code won’t be sent.

Note: The Network APIs are available in the following countries and Communication Service Providers (CSPs)

Source Code

The application's source code can be found on our GitHub repo.

Prerequisites

Application Architecture

The application follows a client-server architecture. The client handles user interactions through HTML and JavaScript, including entering phone numbers and submitting verification codes. The client and the server communicate via GET/POST requests. The server, developed in Node.js with Express, manages backend logic, including interfacing with Vonage’s SIM Swap and Verify v2 APIs for security checks and authentication.

Create a New Project

Create a new project and change into its directory.
mkdir improve-your-auth && cd improve-your-auth

Start a New Node Project

In your newly created project directory, initialize a new node project. This will create a new package.json file. The -y flag automatically fills in the defaults without asking for the details.

npm init -y

Install the Dependencies

To install all the dependencies in one go, run the following command. It will automatically add them to your package.json file and install them in your project's node_modules directory.

npm install @vonage/server-sdk @vonage/auth axios dotenv express

Create a New Vonage Application

Now that we have specified the folders and dependencies we will use for the project, it's time to create a Vonage Application with Verify and SIM swap capabilities.

After signing up/in for a Vonage account, you can create a new application in the dashboard by navigating to 'Your Applications', clicking to create a new application, giving it a name, such as 'multifactor authentication,' and toggling Verify V2 and Network APIs.

Note: Register your application with Vonage and the Communication Service Providers (CSP) before using the SIM Swap API. If your registration is already approved, you can start building your solution right away. If not, check out our guide on Vonage Network Registration for more details.

Create a public/private key pair for API authentication; this will download a private.key. Add that file to your node project folder.

Server Side Implementation

SIM Swap Overview

The important aspect of the SIM Swap API we will use for this demonstration is the ability to check whether the swap happened in the last few days to let the user know there could have been a potential fraud in case the swap wasn't made by them.

The SIM Swap API strengthens SIM-based authentication methods, including SMS One-Time Passwords and silent authentication. It keeps track of the last SIM swap on a specific mobile number and checks the activation date of a SIM card in real time.

Create the Server File

The server.js file is our main server script that integrates with the Vonage SIM Swap and Verify v2 APIs to authenticate users securely. It also serves the web pages for our banking dashboard and handles user login and verification.

Authentication Function

We need to authenticate our requests to communicate with the SIM Swap API. Here's how we can set up an authentication function using OAuth2. It requires two POST requests, one to retrieve the auth_req_id and the other to get a new access token.

async function authenticate(phone, scope) {
  try {
    const authReqResponse = await axios.post(
      authReqUrl,
      {
        login_hint: phone,
        scope: scope,
      },
      {
        headers: {
          Authorization: `Bearer ${process.env.JWT}`,
          "Content-Type": "application/x-www-form-urlencoded",
        },
      }
    );
    const authReqId = authReqResponse.data.auth_req_id;

    const tokenResponse = await axios.post(
      tokenUrl,
      {
        auth_req_id: authReqId,
        grant_type: "urn:openid:params:grant-type:ciba",
      },
      {
        headers: {
          Authorization: `Bearer ${process.env.JWT}`,
          "Content-Type": "application/x-www-form-urlencoded",
        },
      }
    );
    return tokenResponse.data.access_token;
  } catch (error) {
    console.error(
      "Error during authentication:",
      error.response?.data || error.message
    );
    throw error;
  }
}

Enter fullscreen mode Exit fullscreen mode

SIM Swap Check

The checkSim function checks if the phone number has been swapped recently. It uses the authenticate function to get an access token and then queries the SIM Swap API. I've set a MAX_AGE environment variable, which you can set to the number of hours ago you want to check if the SIM swap happened.

async function checkSim(phoneNumber) {
  try {
    const accessToken = await authenticate(scope);
    const response = await axios.post(simSwapApiUrl, {
      phoneNumber: phoneNumber,
      maxAge: process.env.MAX_AGE,
    }, {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "Content-Type": "application/json",
      },
    });
    return response.data.swapped;
  } catch (error) {
    console.error("Error checking SIM swap:", error.response?.data || error.message);
    throw error;
  }
}

Enter fullscreen mode Exit fullscreen mode

Add Verify V2 API

The Verify V2 API in our use case allows us to send a verification code to a user's phone number if no recent SIM swap has been detected.

Verification Request

Below, we handle a verification request. If no SIM swap is detected, we proceed with sending a verification code using Verify V2 API:

app.post("/sendcode", async (req, res) => {
  const phone = req.body.phone; // phone number to verify
  if (!await checkSim(phone)) {
    try {
      const response = await vonage.verify2.newRequest({
        brand: "Vonage Bank",
        workflow: [{ channel: Channels.SMS, to: phone }]
      });
      res.json({ message: "Verification code sent.", request_id: response.requestId });
    } catch (error) {
      console.error("Error during verification:", error);
      res.status(500).json({ message: "Error processing request." });
    }
  } else {
    console.log("SIM swap detected. Verification code not sent.");
    res.status(403).json({ message: "Verification denied due to recent SIM swap." });
  }
});
Enter fullscreen mode Exit fullscreen mode

PIN Submission

After receiving the verification code, the user can submit it through our service. Here's how we verify the submitted PIN:

app.post("/verify", async (req, res) => {
  const { pin, requestId } = req.body; // pin and request ID from user input
  try {
    const result = await vonage.verify2.checkCode(requestId, pin);
    if (result === "completed") {
      res.json({ message: "Verification successful." });
    } else {
      res.json({ message: "Invalid PIN. Please try again." });
    }
  } catch (error) {
    console.error("Error during PIN verification:", error);
    res.status(500).json({ message: "Error during PIN verification." });
  }
});

Enter fullscreen mode Exit fullscreen mode

Create the Environment Variables File

Create a .env file for your project and add the environment variables found in the code snippet below. Refer to Michael's blog post for a wonderful explanation of using environment variables in Node.js.

The VONAGE_API_SECRET and VONAGE_API_SECRET can be found on the Vonage Dashboard.

Vonage Dashboard

# Your Vonage application 
VONAGE_API_KEY=your_api_key
VONAGE_API_SECRET=your_api_secret
VONAGE_APPLICATION_ID=your_application_id
VONAGE_APPLICATION_PRIVATE_KEY=/path/to/your/private.key

# Use this URL to generate a new JWT: https://developer.vonage.com/en/jwt
JWT=your_jwt_token

# Number of hours to check SIM Swap events
MAX_AGE=72
Enter fullscreen mode Exit fullscreen mode

Client Side Implementation

Add the Content and Styling

In the index.html file, we will add two forms: one for entering a phone number and another for logging in with a PIN. This layout enables the user to authenticate their identity securely. For simplicity and clarity, I am leaving the password in plain text. Of course, in real-life/ production scenarios, you’ll need to encrypt the password and eventually add a Salt value to strengthen the security.

In the main.html file, we will have our bank dashboard, which is accessible once the user successfully logs in.

The style.css file contains the styling for both HTML files. You can use the simple styles I've added for this project, or be creative and create your own!

Add the client.js File

In the client.js file, we handle what happens when users fill out forms on our website. It manages the steps where users type in their phone numbers and get a verification code if their SIM card hasn't been swapped out recently.

The script quickly lets users know if they can't be authenticated because of a recent SIM swap or if they will receive a code to proceed with logging in. This ensures that only users with secure accounts can access sensitive features.

Test It Out

To see everything in action, start up the application by running:
node server.js

Then, open your web browser and navigate to:
http://localhost:3000/

Here’s what you should expect: imagine you've forgotten your password. Click on 'Forgot Password' to start the recovery. You'll be prompted to enter your phone number and request verification.

If the application detects a recent SIM swap, a warning pops up: Warning! A recent SIM pairing change related to the User’s mobile account occurred. Proceed?" If you didn't initiate this swap, and you choose 'No, it wasn't me', it's a sign there might be fraud happening with your number. This is your cue to check in with your mobile provider.

Note: In the demonstration, you're prompted to confirm that you've swapped the SIM card, in a real-life scenario likely the user would be invited to directly contact the service provider for additional identity checks.

Similarly, this approach could be applied to multifactor authentication on log in, where a check would occur before sending the one-time token, and if a recent SIM swap is detected, user would be invited to directly contact the service provider for additional identity checks.

On the other hand, if you swapped your SIM and selected 'Yes, it was me,' the app uses the Verify API to send a verification code to your phone. Enter this code on the next screen and set a new password. Once that's done, you can use your new credentials to log into your simulated bank account.

Conclusion

Congratulations, you've reached the end of this tutorial! You've learned to use the Verify API to verify a token and the SIM Swap API to detect if the phone number was swapped in the last few days.

If you have further questions, contact Vonage Community Slack or message us on X.

Read Further

Check out the blog post: Check Verification Code.

You can also learn more about the SIM Swap API in our Developer Documentation.

Learn about Getting Started with The SIM Swap API.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .