Sending Feedback to Twilio Verify Fraud Guard in Auth0

Auth0 supports Twilio out of the box as a phone provider however in many instances Twilio Verify provides a better experience. The out of the box Twilio integration requires the sender to manage all of the phone numbers and Sender IDs that they need. This can be simple if you only operate in a single country that doesn’t have onerous regulatory requirements, such as Australia. However if you are operating across a large number of countries or in markets where Sender ID regulations are onerous then it makes sense to leverage a service like Twilio Verify which not only will deliver the message for you but also manage the pool of senders used. Auth0 has provided guidance on how to configure a custom phone provider with Verify, unfortunately there is no guidance today on how to provide feedback to Twilio. This limits the power and functionality of Verify, in particular the fraud guard functionality will not work without providing real time feedback of successful verifications. This post will demonstrate how to send feedback via actions. The high level process to send feedback using event streams is outlined here.

Setup the Custom Phone Provider Action with Twilio Verify

The first step required to leverage Twilio Verify is to enable the custom phone provider action and configure the integration. I will not detail that here but rather I suggest that you look at the following documentation.

Send Feedback via Actions

Sending feedback via actions is the simpler of the two methods available today. It does however require that all MFA actions are triggered via actions and not using the MFA slider when risky/always options. The steps we are going to follow here are as follows.

  1. Create an action to send feedback for a passwordless login OTP
  2. Create an action that triggers MFA.
  3. Create an action to send feedback for OTP MFA.
  4. Setup the Post Login Trigger to invoke the actions.

Note, the actions assume that the phone number is stored in the user metadata under user.phone_number if this is not the case please update accordingly. 

Actions Pre-requisites

When creating the two actions that call Twilio you’ll need to complete the following steps first in each action that is being created.

  1. Navigate to Actions -> Library and select Create Action -> Build from scratch.
  1. Add Twilio dependency, you can leave the version blank and it will use the latest version.
  1. Add your Twilio Account SID, Authentication Token and Verification Service SID as secrets for more details on where to obtain these values refer back to the Twilio blog. For additional security consider using restricted API Keys for additional security simply use the Key SID in place of the account SID and the secret in place of the Auth Token.

Passwordless Login OTP Action

This post login action checks to see which connection was used and then once it confirms it was passwordless SMS it calls the Twilio Verify API with the phone number which validates the outstanding Verification for that phone number. To deploy this you’ll need to complete the prerequisites above and then copy and paste the below into your action and click deploy.

/**
 * Handler that will be called during the execution of a PostLogin flow.
 *
 * @param {Event} event - Details about the user and the context in which they are logging in.
 * @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login flow.
 */
exports.onExecutePostLogin = async (event, api) => {
  // Check if the connection strategy is 'sms'
  const isPasswordlessSmsLogin = event.connection && event.connection.strategy === 'sms';
  console.log(`Connection: ${event.connection.name} Strategy: ${event.connection.strategy}`)

  if (isPasswordlessSmsLogin) {
    console.log(`User ${event.user.email || event.user.phone_number || event.user.user_id} logged in using Passwordless SMS.`);

    //Send Feedback to Twilio that the Authentication Attempt was successful
    const { TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_VERIFY_SID } = event.secrets;
    const client = require('twilio')(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

    try {
      console.log(`Sending Feedback`)
      const verification = await client.verify.v2.services(TWILIO_VERIFY_SID)
        .verifications(event.user.phone_number)
        .update({
          status: "approved"
        });
    } catch (error) {
      console.error('Error sending feedback to:', error);
      // Handle error (e.g., block the login, provide a user-friendly message)
//      api.access.deny('Failed to send verification code.');
    }

  } else {
    console.log(`User ${event.user.email || event.user.phone_number || event.user.user_id} logged in using a different method. Strategy: ${event.connection ? event.connection.strategy : 'N/A'}`);
  }
};

Action to Send Feedback for SMS MFA

This post login action checks to see what type of MFA was completed and if the phone factor was used it calls Twilio Verify API with the phone number which validates the outstanding Verification for that phone number. To deploy this you’ll need to complete the prerequisites above and then copy and paste the below into your action and click deploy.

/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/


exports.onExecutePostLogin = async (event, api) => {
  console.log(`Phone MFA Feedback`)
  // Check if MFA was performed and identify the factor
  const mfaMethod = event.authentication?.methods.find(
    (method) => method.name === 'mfa'
  );

  if (mfaMethod) {
    const mfaFactorType = mfaMethod.type; // This will be 'phone', 'otp', 'push-notification', etc.
    console.log(`User ${event.user.email || event.user.user_id} completed MFA using factor: ${mfaFactorType}`);

    // You can add conditional logic here based on the MFA factor type:
    if (mfaFactorType === 'phone') {
      console.log('MFA was completed using SMS or Voice.');
      // Trigger specific logic for phone MFA
      const { TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_VERIFY_SID } = event.secrets;
      const client = require('twilio')(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

      try {
        const verification = await client.verify.v2.services(TWILIO_VERIFY_SID)
          .verifications(event.user.phone_number)
          .update({
            status: "approved"
          });
      } catch (error) {
        console.error('Error sending feedback to:', error);
      }
    } 

  } else {
    console.log(`User ${event.user.email || event.user.user_id} did NOT complete MFA in this session.`);
  }
};

Action to Trigger SMS MFA

This action checks uses the  api.authentication.challengeWith command to trigger phone based MFA for the user. It is important to use this rather than the older method event event.authentication.method as this does not interrupt the actions pipeline and will not trigger additional actions. This is outlined in more detail in this community post.

/**
* Handler that will be called during the execution of a PostLogin flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {

 if (!event.user?.enrolledFactors?.length) {
    // not enrolled; choose a factor to enroll now
    console.log("No factors enrolled")
    api.authentication.enrollWith({type: 'phone'});
    return;
  }
    console.log(`Triggering MFA`)
    api.authentication.challengeWith({type: 'phone'});
};

In addition it performs a simple check to confirm if a user has enrolled for MFA and if not triggers MFA enrolment. This test should be expanded to ensure the user has a phone factor enrolled if being deployed into production. For this action there is no need to add the Twilio dependency and additional secrets. Once you copy the below into a new action ensure that you deploy it.

Customize the post login trigger

The final step to is to customize the post login trigger with all of your actions. This is easily done with the following steps.

  1. Navigate to Actions -> Trigger and select post-login to start editing your post login flow.
  2. On the right hand side under the Add Action header select Custom
  3. Drag your three actions out and place them in order. This should be the action for the SMS login, followed by the action to trigger MFA and then the action to provide feedback for MFA. Once you have them in the correct order be sure to click the Apply button.

Testing the Actions

Now that the actions have been deployed and applied it is time to test the actions. Recently Auth0 introduced real time actions logs this makes it much easier to test our actions and integrations. Wait until the status shows as connected before conducting your tests. Below is an example trace for SMS based login. 

Additionally, if you navigate to Monitor -> Logs -> Verify in your Twilio console you should see that the status for the matching verification is marked as approved. If this has not been marked as approved then the call to the Verify API is not working as expected.

Leave a Reply