How to bring your own telephony to Okta with Twilio Verify

May 24, 2024
Written by
Reviewed by

Okta now requires Bring Your Own Telephony (BYOT), enabling you to integrate any telephony service provider with Okta's authentication and verification systems. This document will cover how to BYOT with the Twilio Verify API for SMS or Voice calls using Okta's telephony inline hook for its workforce identity cloud. The code for this project can also be found on GitHub .

Learn more about the benefits of using the Verify API.

↪️ If you're interested in using Twilio Verify for Auth0/Okta's customer identity (CIAM) products, this BYOT approach has been successfully implemented by many companies using Auth0 and Verify .

Why use Twilio Verify? 

Learn more about the benefits of using the Verify API.

Twilio Verify is the ultimate solution for secure and seamless omnichannel verification. This dedicated, fully managed, turn-key service offers unparalleled global reach and supports multiple channels for OTP delivery, ensuring reliability with built-in redundancy and multi-language support. By leveraging Verify's WhatsApp auto fallback to SMS feature, OTPs are delivered via the most effective channel. The Geo Permission feature allows you to block unwanted or high-risk countries, enhancing security. Manage the risk of SMS pumping fraud effortlessly with Twilio Verify Fraud Guard. Benefit from built-in service rate limits and advanced programmable rate limits for optimal control. Additionally, Twilio Verify provides the flexibility to perform crucial checks before sending OTPs, such as blocking numbers from high-risk carriers using the Twilio Lookup API. Twilio Verify ensures your verification process is robust, reliable, and secure, making it the perfect choice for your business.

 

The sequence of events for SMS/Voice one-time passcode (OTP) authentication includes:

  1. A user logs in to an application page using Okta credentials.
  2. [Optional] Okta sends the end user’s phone number to the Twilio Lookup API to utilize various Twilio lookup packages , including formatted number and line type intelligence. This step is optional and applies only if the Twilio lookup package is purchased.
  3. The Twilio Lookup API returns the correctly formatted “To Number,” if applicable.
  4. Okta sends a request to Twilio's Verify API with the custom code and the formatted phone number .
  5. The Twilio Verify API sends the custom code to the end user based on the selected channel in the API request
  6. The user enters this information onto a form on the application’s Okta login page.
  7. Okta performs a verification check using its own logic and redirects the user to the protected application.
  8. Okta provides feedback to Twilio indicating whether the user verified the code, enabling proactive monitoring of global routing and operational status.

Prerequisites for sending OTPs with Okta and Twilio Verify

This tutorial requires:

Step 1 - Create a Twilio Function to send verification codes

Twilio Functions is a serverless environment that empowers developers to quickly and easily create production-grade, event-driven Twilio applications that scale with their businesses. Twilio Functions provide a complete runtime environment for executing your Node.js scripts. Functions integrates popular package managers like NPM, and provides a low latency Twilio-hosted environment for your application.

If you want to host your own code, Twilio has SDKs in 7 popular languages. Learn more about the Verify API in the documentation .

To create a function, go to the Twilio Console :

exports.handler = async function(context, event, callback) {
  try {
    console.log(event.request.headers);

    if (context.auth_secret !== event.request.headers.auth_secret) {
      throw new Error("Authentication failed");
    }

    let client = context.getTwilioClient();

    // https://developer.okta.com/docs/reference/telephony-hook/#data-messageprofile
    let to = event.data.messageProfile.phoneNumber;
    let customCode = event.data.messageProfile.otpCode;
    let channel =
      event.data.messageProfile.deliveryChannel.toLowerCase() === "sms" ?
      "sms" :
      "call";

    let verification = await client.verify.v2
      .services(context.VERIFY_SID)
      .verifications.create({
        to,
        channel,
        customCode
      });

    console.log(verification);
    console.log(verification.sendCodeAttempts);

    let response = {
      commands: [{
        type: "com.okta.telephony.action",
        value: [{
          status: "SUCCESSFUL",
          provider: "Twilio Verify",
          transactionId: verification.sid,
          transactionMetadata: verification.sendCodeAttempts.at(-1).attempt_sid,
        }, ],
      }, ],
    };

    return callback(null, response);
  } catch (error) {
    console.error("Error: " + error);
    let errorResponse = {
      error: {
        errorSummary: error.message,
        errorCauses: [{
          errorSummary: error.status || error.message,
          reason: error.moreInfo || error.message,
        }, ],
      },
    };
    return callback(null, errorResponse);
  }
};

VERIFY_SID

VAxxxxx (the verify service SID that you created earlier in this step) 

auth_secret

A random string that will be used for authentication the API call from Okta

  • Save and Deploy
  • Take a note of your Twilio Function URL, in this example, it will be something like https://Okta-xxxx.twil.io/Okta_MFA . This is the URL that you will use when setting up the Okta telephony inline hook
  • Make sure Live Logs is toggled on for troubleshooting purposes.

 

Everything in Twilio has now been set up. Now head over to the Okta admin dashboard.

Step 2 - Configure Okta Inline Hook

If you don't have an Okta Account you can create one for free here .

Add a telephony inline hook

  • In the Admin dashboard, go to Workflow -> Inline Hooks.
  • Click Add Inline Hook, and then select Telephony.
  • Name - something like "Twilio"
  • URL - paste your Function URL
  • Add the Authentication field and Authentication secret values. This example uses HTTP Basic Authentication .

Authentication field = auth_secret {environment variable stored in Twilio function above}

Authentication secret = xxxxx { random string created and stored as auth_secret environment variable above)

Please note: the Authentication field and Authentication secret will be used to authenticate the API call from Okta. Make sure that these values are matched with the key/value pair created in Twilio Function above.

  • Optional. Add Custom Headers. In this blog I didn't add any custom headers.
  • Click Save. This activates the telephony inline hook.

Please note that there can only be one active telephony inline hook at a time in Okta.

Step 3 - Preview and test the telephony inline hook

If you haven't already, enable phone authentication in your admin dashboard under Security > Authenticators > Phone.

To test the integration, use the Inline Hooks Preview feature.

  • In Workflow > Inline Hooks, find the Active telephony inline hook and click ActionsPreview. The Preview tab of the inline hook opens.
  • In the tab, go to Configure inline hook request and enter a user's information for testing:
  • data.userProfile: Enter the name of a user who has the phone as a valid authenticator.
  • requestType: From the dropdown menu, select one of the following events to send the SMS text or voice call to the user: MFA enrollment, MFA verification, Account unlock, or Password reset.
  • In Preview example inline hook request, click Generate request. This generates the JSON request that Okta sends to your telephony provider.
  • Click Edit to edit the generated request. For example, you can edit the user profile or the phone number before sending the request. (replace the default phone number ( 9876543210) with a user’s mobile number in E164 format )
  • In the View service's response, click View response. This triggers the hook and displays the response from Twilio.

Please note that Okta will not generate an OTP if the connection between Okta and Twilio fails during the test. Successful completion of the Twilio Function will include logs with verification attempt details.

You also have the option to access fundamental metrics for this telephony inline hook, aiding in the monitoring of your telephony service provider's performance within Okta. More details can be found in Okta's documentation.

Step 4 Send Feedback to Twilio Verify

In order to benefit from Verify Fraud Guard protection and see metrics in Twilio Verify Insights dashboard, you will need to provide feedback using the Twilio Feedback API.  This can be done via an event hook in Okta for OTP related events.

Please note, the code example below uses Okta User API to get the user's phone number and then call Verify Feedback API using the phone number.  There are other ways to get a user phone number such as Okta Factor API. Please review your Okta setup and choose the proper API.

This event hook can be hosted in Okta WorkflowsTwilio Functions or anywhere. In the below example the event hook is using a Twilio function. 

Create an API Token in Okta

Follow Okta’s documentation to create an Okta API token.

Copy the token to a secure location, such as a password manager. The only time you can view and copy the token is during the creation process. You will use this value in the Twilio Function environment variable in the next step. 

Create Twilio Function

To create a function, go to the Twilio Console:

  • Add another new function to the function service that was created above in Step 1 and give it a name, for example, /OktaEventHook.
  • Change the function's visibility from protected to public.
  • Update dependencies: add @okta/okta-sdk-nodejs module and version (you can find latest release here)
  • Copy the following code or from this repo to your function /OktaEventHook and save it.
  • Add following additional  Environment variables

okta_auth_token

{Value of the API token created in Okta}

okta_org_baseurl

Please use your Okta org base url (without -admin)

  • Save and Deploy your function
  • Make sure live logs are turned on for troubleshooting purposes
exports.handler = async function(context, event, callback) {
try {
  
   if (context.auth_secret !== event.request.headers.auth_secret) {
     throw new Error("Authentication failed");
   }
   const oktaBaseUrl = context.okta_org_baseurl;
   const api_token= context.okta_auth_token;
   //one off check when enable Okta event inline hook, required by Okta
   const verificationValue = event.request?.headers ? event.request.headers['x-okta-verification-challenge'] : null;
   if (verificationValue) {
       console.log("Verifying");
       // Prepare response body
       const responseBody = { verification: verificationValue };
       // Return success response
       callback(null, JSON.stringify(responseBody));
   }
  
   console.log ("event data: ",event.data);
   let mfa_event= event.data?.events[0];
   console.log ("mfa_event: ", mfa_event);


   //check payload of "user.authentication.auth_via_mfa" and "user.mfa.factor.activate" for SMS OTP and CALL OTP
   if (mfa_event && mfa_event.outcome?.result === 'SUCCESS'  && (mfa_event.debugContext?.debugData?.factor === 'SMS_FACTOR' || mfa_event.debugContext?.debugData?.factor === 'CALL_FACTOR') || (mfa_event.outcome?.reason.includes("SMS_FACTOR") ||mfa_event.outcome?.reason.includes("CALL_FACTOR")))
   {
   //call Okta user API with user id to get the phone number, which will be used when calling Twilio Verify feedback API
   const userid=mfa_event.actor?.id;//grab user id
   const okta=require('@okta/okta-sdk-nodejs');
   const OktaClient= new okta.Client({ orgUrl: oktaBaseUrl, token: api_token });
   let user = await OktaClient.getUser(userid); //call Okta user API
   const phone_number=user.profile.mobilePhone; //grab phone number
   console.log ("user phone number: ", phone_number);
  //call Verify feedback API using phone number
   let client = context.getTwilioClient();
   let verification=await client.verify.v2.services(context.VERIFY_SID)
       .verifications(phone_number).update({status: 'approved'});
  
   console.log (verification);
   return callback (null,verification);
  
   }else {
       console.log ("not SMS OTP or Voice OTP MFA factor")
       return callback (null, "not SMS OTP or Voice OTP MFA factor")
   }


}catch (error){


   console.error(error);
   return callback(null, error);


}
};

The above example supports both SMS and Voice OTP. If you want to use Verify WhatsApp auto fallback to SMS feature, contact Twilio Sales. The feature can be enabled for your Twilio account, you do not need to change the code from your end.   

Create an Event Hook in Okta 

Follow the steps in Okta’s documentation to create an event hook. 

  • Sign in to your Okta org 
  • From the Admin Console, go to Workflow > Event Hooks.
  • Click Create Event Hook. The Add Event Hook Endpoint dialog box opens.
  • In the Name field, add a unique name for the hook (in this example, "Twilio Verify Feedback").
  • URL - paste your Function URL (e.g. https://okta-xxxx.twil.io/OktaEventHook)
  • Add the Authentication field and Authentication secret values. This example uses HTTP Basic Authentication.

Authentication field = auth_secret {environment variable stored in Twilio function above}

Authentication secret = xxxxx { random string created and stored as auth_secret environment variable above) 

  • In the REQUESTS section of the dialog box, subscribe to the event type you want to monitor. user.mfa.factor.activate  and  user.authentication.auth_via_mfa events should be added

  • Click Save & Continue.

  • You can complete the one-time verification Okta call now or verify the event hook later. If you're using the below example, proceed to verification.

Click Save & Continue then click Verify

The hook will be verified. Select Authentication events from the drop down.

Select the recent SMS event and click Deliver Request

The request delivery is successful. Now, head to the Twilio console to see the updated statistics of the Twilio Console Verify Logs after a successful request. Select a Verification ID to open its Verification details page and view more comprehensive information such as different actions and outcomes that happened during the Verification's lifecycle and any related error codes, if applicable.

Below you can see the Status is Approved and the Delivery status is Delivered.

Once you have the feedback API enabled, all Verify customers have access to the Verify SMS Fraud Insights dashboard on Twilio Console. The dashboard illustrates the impact fraud could have had without intervention, and also allows you to discover trends and insights that you can use to better optimize your product against fraud.

To view your dashboard, go to Twilio Console and navigate to Monitor > Insights > Verify > Fraud which will open the Overview tab. There, you'll find several sections relating to your Fraud metrics.

With the implementation described above, you have successfully developed a cohesive solution to integrate your own telephony system with Okta, while also sending successful authentication events to Twilio Verify for precise metrics and routes optimization.It's a robust solution that ensures both security and reliability for your telephony system

What’s Next?

Congratulations! Your users can now log in to your application with multi-factor authentication enabled, receiving a one-time password via Twilio Verify.Interested to know more about Twilio Verify? Check out Twilio Verify API documentation . We can't wait to see what you build and secure!

Dr Mingchao Ma has more than 15 years’ experience in Cybersecurity. In the account security team at Twilio he helps customers build solutions using Twilio Cloud security APIs. He previously worked at Microsoft UK helping financial sector customers adopt Microsoft Cloud security technologies. Before this he was a lead security architect at IBM UK. Mingchao has a PhD in information security, and cybersecurity certifications such as CISSP-ISSAP, CISM, SABSA etc. He is a Microsoft Certified Azure Solutions Architect Expert, Microsoft 365 Certified Enterprise Administrator Expert and Microsoft Certified Azure DevOps Engineer Expert.

Yukti Ahuja is a Principal Solutions Engineer at Twilio. As an SE she is a problem solver extraordinaire, blending technical expertise with communication skills to bridge the gap between complex technology and practical solutions for customers.