Okta - (BYOT) Bring Your Own Telephony Guide
Overview
This guide will take you through how to integrate 8x8's SMS API into Okta as an authenticator method using an Okta Inline Hook. Specifically we will be using Okta's Telephony Inline Hook to add 8x8's SMS API as an option.
For Okta's own guide on bringing your own telephony provider, refer to Okta's reference guide here and use Option 2.
The diagram below explains how the flow will look like from using Okta together with the Node.js server we will be building in this tutorial to send OTPs via 8x8 SMS API.
Video Demo
This Video Demo shows the integration in action and explains a high level of the setup steps in this guide. We recommend referring to this text guide for the full setup.
Requirements
- Okta Account (an Okta Dev Account is fine for testing).
- 8x8 Account and Subaccount with a SMS Sender.
- JavaScript Knowledge for running the sample code.
- API Endpoint for Okta to send a HTTP request. Required as part of their Inline Hook integration.
Setup
Setup Backend Code
We will need an example endpoint for Okta to send it's HTTP request to. We have provided some example Node.js server code below for you to use:
const express = require('express');
const bodyParser = require('body-parser');
const axios = require('axios');
const { apiKey, subaccount, sender, authKey } = require('./config');
const app = express();
const PORT = 3000;
// Middleware to parse JSON bodies
app.use(bodyParser.json());
// Function to send a successful response to Okta
const sendSuccessResponse = (res, umid, transactionMetadata) => {
res.status(200).json({
commands: [
{
type: "com.okta.telephony.action",
value: [
{
status: "SUCCESSFUL",
provider: "8x8",
transactionId: umid,
transactionMetadata: JSON.stringify(transactionMetadata)
}
]
}
]
});
};
// Function to send an error response to Okta
const sendErrorResponse = (res, errorData) => {
const errorSummary = `8x8 Error Code: ${errorData.code}. 8x8 Error Message: ${errorData.message}`;
res.status(500).json({
error: {
errorSummary: errorSummary
}
});
};
app.post('/telephony-hook', async (req, res) => {
// Print the request body
console.log('Received request body:', req.body);
// Check if the Authorization header is correct
const authHeader = req.headers['authorization'];
if (authHeader !== authKey) {
console.log('Unauthorized request');
return res.status(401).json({ error: 'Unauthorized' });
}
// Check if the required fields are present
const { msgTemplate, phoneNumber } = req.body.data.messageProfile;
if (!msgTemplate || !phoneNumber) {
console.log('Invalid request data:', req.body.data);
return res.status(400).json({ error: 'Invalid request data' });
}
// Send OTP using 8x8's SMS API
try {
const response = await axios.post(
`https://sms.8x8.com/api/v1/subaccounts/${subaccount}/messages`,
{
source: sender,
destination: phoneNumber,
text: msgTemplate,
encoding: "AUTO"
},
{
headers: {
'Authorization': `Bearer ${apiKey}`
}
}
);
// Print successful response
console.log('OTP sent successfully:', response.data);
// Respond with a successful delivery response
sendSuccessResponse(res, response.data.umid, response.data);
} catch (error) {
// Print error response
console.log('Failed to send OTP:', error.message);
// Handle errors from the SMS API and respond with an error delivery response
if (error.response && error.response.data) {
sendErrorResponse(res, error.response.data);
} else {
sendErrorResponse(res, { code: 'UNKNOWN', message: 'An unknown error occurred' });
}
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
The code takes as input a inline hook from Okta which is simply an HTTPS request. Then it will extract the phone number and message from the request body and use that information to send an API call to 8x8's SMS API to send an OTP to the destination number. It will return either success or error information to Okta as a final step.
Please note it requires parameters set in a config.js file as below. All values should be replaced and set with the corresponding values unique to your 8x8 Account and Okta Inline Hook configuration. The apiKey, subaccount and sender can be found in 8x8 Connect under API Keys and Numbers.
Key | Value |
---|---|
apiKey | Your 8x8 API Key. |
subaccount | Your 8x8 Subaccount. |
sender | Your 8x8 SMS Sender ID or Virtual Number |
authKey | Should be set to the same value as the Authentication Secret from the Inline Hook guide setup below. Used by your server to authenticate the request from Okta. |
Here is the example config.js file for reference where the values should be replaced.
// config.js
module.exports = {
apiKey: 'your_8x8_api_key',
subaccount: 'your_8x8_subaccount',
sender: 'YourSender',
authKey: '1234'
};
Here is the example package.json for your Node.js application. It includes the packages axios, body-parser and expressed used in the backend server code.
{
"name": "okta_inline_hook_integration",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"axios": "^1.7.3",
"body-parser": "^1.20.2",
"express": "^4.19.2"
}
}
After placing this code in the same directory, you can run the following commands to start the server.
npm install
npm start
The output should appear as follows.
> [email protected] start
> node server.js
Server is running on port 3000
The server should be exposed to the public internet so that Okta can send a webhook. The exact method is left to you but some options may include ngrok and localtunnel if you are running on your local laptop/desktop dev environment.
For production, however, this should be running behind a proper web server setup.
Create Inline Hook
Go to Workflow > Inline Hooks on the Okta Dashboard. Select Add Inline Hook and Telephony.
Adding Inline Hook as Authenticators
In the Create Inline Hook page, fill in the following values.
Field | Description | Example Value |
---|---|---|
Name | Can be any value. We use "8x8 - SMS Authentication" | 8x8 - SMS Authentication |
URL | Should be set to the URL of your backend server that you send the code. For this tutorial we host it locally and use ngrok to expose it for demo purposes. In production you will need a hosting solution. | https://example.com/endpoint |
Authentication Field | Will be sent as part of the request header. Authentication Field is for your backend to authenticate the webhook from Okta. It can be any value Refer to Okta's page on authentication and Inline Hooks for reference. | Authentication |
Authentication Secret (Used with Authentication Field) | Will be sent as part of the request header. This should be the value your backend uses to authenticate | secretvalue |
After entering the values, click Save.
Preview the Telephony Inline Hook
In the next page you should see the following confirming a few of the values from setting up the Inline Hook. Select an Okta user to preview from your organization in data.userProfile and select anything for the requestType, we use MFA enrollment.
Afterwards click Generate Request in Step 2 on this page, it will generate an example JSON that will be sent to your endpoint so that you know what format to expect from Okta. If needed, change any of the JSON Values such as the messageProfile.phoneNumber field which we use in our tutorial code to send an OTP.
Click View Response to send the example Inline hook to your server. You should see the JSON response below from the server upon a successful request.
From our example Node.js server you should see the following output, showing the API request sent from Okta and also the output of the API call to 8x8's SMS API.
Received request body: {
eventId: '3IPD5oQfQdOttCCjUWMk3Q',
eventTime: '2024-08-07T22:15:30.000Z',
eventType: 'com.okta.telephony.provider',
eventTypeVersion: '1.0',
contentType: 'application/json',
cloudEventVersion: '0.1',
source: '<redacted>',
requestType: 'com.okta.user.telephony.pre-enrollment',
data: {
context: { request: [Object] },
userProfile: {
firstName: 'Rommel',
lastName: 'Sunga',
login: '<redacted>',
userId: '00uit3mfz9gLSSzgQ5d7'
},
messageProfile: {
msgTemplate: 'Your code is 11111',
phoneNumber: '<Destination Phone Number>',
otpExpires: '2024-08-07T22:20:25.057Z',
deliveryChannel: 'SMS',
otpCode: '11111',
locale: 'EN-US'
}
}
}
OTP sent successfully: {
umid: 'f68ac2f8-9da3-48d8-b8ec-f9702dad3b5b',
clientMessageId: null,
destination: '<Destination Phone Number>',
encoding: 'GSM7',
status: {
code: 'QUEUED',
description: 'SMS is accepted and queued for processing'
}
}
The SMS should also be delivered to your phone.
This demonstrates the inline hook is now successfully working. Now you can attach the Okta inline hook to any action that would trigger the inline hook in Okta.
Using the Inline Hook
Now that the Inline Hook has been added, in order to require it for signing into your Okta organization.
Add Authenticator
Ensure that in the Security - Authenticators page that Phone is added as an Authenticator option.
If it is not already on the list then click Add Authenticator to add it.
Create new Authentication Policy Rule
Click Add Rule on the Security - Authentication Policies page.
In the Edit Rule page, the only change we will make is for AND Authentication methods where we should include the Phone - SMS method along with any other methods we wish to offer the user authenticating into Okta.
Add to Application
After creating the Policy, add it to one of your Applications.
Signing In
When attempting to login to the application that you have configured above, you should receive the following screen prompting you to register for Phone Verification.
Again the code should be sent to your phone via SMS, follow the prompts to finish logging into application.
For subsequent sign-ins to the application it should utilize SMS as a verification method.
Conclusion
In this tutorial we have shown how to create a Telephony Inline Hook that makes use of 8x8's SMS API to send OTPs via SMS. With this integration, you can leverage the ability of 8x8's SMS API to send an OTP while integrating with Okta.
Updated 2 months ago