REST hooks for WebRTC Click to Call. Implementation experience

The “Click to Call” button on the site is an “innovation” that has been around for about 10 years. The technologies under the hood have changed, but the principle has remained the same – we click on the button on the website page, JavaScript is launched, which requests access to the microphone and establishes a connection with the server – the WebRTC SIP gateway. Further, one client-server leg is a browser-gateway, the second leg can be arbitrarily long and through the SIP proxy chain can ultimately connect to a mobile or landline phone. Thus, the browser turns, in a sense, into a softphone and becomes a full participant in VoIP telephony.

One-click call is very convenient for users who visit the site through mobile browsers, and there are most of them today. In addition to convenience for customers, there is still an opportunity to save money. It can be configured so that a call from the “Click to Call” button will be charged as a call within your PBX, i.e. in most cases free of charge. The savings, in comparison with the connection and monthly maintenance of the 8-800 number, are quite large.

We have on our site minimal example of implementation such a button. Everything is fine in the example – the code is simple, take it, copy it and be happy. But there is one BUT! It is unsafe to transmit connection parameters to the SIP server in JS code, an attacker can easily spoof the called subscriber’s number or use the credentials of your SIP server to call penguins in Antarctica and retell “War and Peace” to them. And then the profit from the placement of the “Click to call” button on the site can turn into huge losses.

Therefore, for Production implementation, we recommend storing connection parameters on the server side and substituting them when initiating a call. To do this, you can use REST Hook scripts.

Hooks? What are hooks?

REST Hooks are simple scripts that work with JSON in the body of an HTTP request and return JSON in the body of HTTP responses. REST Hook scripts replace standard WCS API applications and allow you to process data about connections, calls and video streams on the backend server.

REST Hook can be used for the following purposes:

  • Authentication of connections to the server by token or password

  • Receiving real-time information about connections, disconnects, start and end of streams, calls, etc.

  • Overriding data sent from the client. For example, you can override and hide the real name of the flow or the direction of the call.

  • Implementation of custom signaling with data transmission via WebSockets, for example, sending a text message in a chat to all connected clients.

In this article, we will look at how you can securely pass the SIP server credentials and callee number for the “Click to Call” button using REST Hook technology.

What do you need to work?

  1. Front-end Web server – organizes the interface with the user. Displays a web page with a “Click to Call” button.

  2. WCS is an intermediary between the user and the SIP server. Converts WebRTC stream from browser to SIP format.

  3. Backend Server – A Web server that powers the REST Hook.

  4. SIP server and SIP phone.

Logically, we separate the front-end, back-end and WCS servers, but physically they can be placed on the same machine. In this article, to simplify the parsing of the example, we use three separate virtual machines.

What to do and how to set it up?

Let’s start the setup from the backend server.

Install and configure Nginx and PHP, for example Nginx on CentOS 7

After that, in the file /etc/nginx/nginx.conf in the “server” section, add the following lines. This setting will make our REST Hook script available for the “/ connect” and “/ call” events:

location / {
  try_files $uri $uri/ /index.php?$request_uri;
  }

In the directory for the web server files (we have / var / www) create the “index.php” file, in which we place the main code of the REST Hook being created. This script will implement a domain access check and transfer the parameters for connecting to the SIP server and the number of the called subscriber:

<?php
 
$api_method = array_pop(explode("https://habr.com/", $_SERVER['REQUEST_URI']));
$incoming_data = json_decode(file_get_contents('php://input'), true);
$domain = "yourdomain.com";
 
switch($api_method) {
    case"connect": 
	$origin = $incoming_data['origin'];
     
    //logs
    error_log("sessionId: " . $incoming_data['sessionId']);
    error_log("origin: " . $origin); 
    
    $found = strpos($origin, $domain);
 
    if ($found !== false){
        error_log("User authorized by domain " . $domain);
    }else{
        error_log("User not authorized by domain: " . $domain . " Connection failed with 403 status.");
        ubnormalResponse(403);
    }
	
	$rest_client_config = json_decode(file_get_contents('rest_client_config.json'), true); 
    $incoming_data['restClientConfig'] = $rest_client_config;
  
	$incoming_data['sipLogin'] = "10001";
    $incoming_data['sipAuthenticationName'] = "10001";
    $incoming_data['sipPassword'] = "Password_123";
    $incoming_data['sipDomain'] = "172.16.30.156";
    $incoming_data['sipOutboundProxy'] = "172.16.30.156";
    $incoming_data['sipPort'] = "5060";
    break;
	case "call":
    // Callee Number
    $incoming_data['callee'] = "10002";
    break;
} 
header('Content-Type: application/json');
echo json_encode($incoming_data);

function ubnormalResponse($code) {
    if ($code == 403) {
    header('HTTP/1.1 403 Forbidden', true, $code);
    } else {
    header(':', true, $code);
    }
    die();
}
?>

In the same directory / var / www create a file “rest_client_config.json”. The source code can be found at the end of this page

In the file “rest_client_config.json” edit the section “call”. Here we specify the policy of the REST method – overwrite data and what value will be overwritten using the script:

"call" : {
  "clientExclude" : "",
  "restExclude" : "",
  "restOnError" : "LOG",
  "restPolicy" : "OVERWRITE",
  "restOverwrite" : "callee"
 },

Then, go to the WCS server. We will assume that you already have a WCS instance installed and configured. If not, then we install according to this instructions… WCS can be run as a virtual instance on Amazon, Google cloud and DigitalOcean, or as a container in Docker

In the console of your server with WCS, we check the availability of the REST Hook script for the / connect and / call events using the “Curl” utility:

curl http://172.16.30.123/connect
curl http://172.16.30.123/call

replace 172.16.30.123 with the IP address or domain name of your backend web server.

If the output of the Curl utility contains the necessary information – parameters for the SIP connection and the number of the called subscriber – then we have configured the REST Hook correctly.

Go to CLI WCS server:

ssh -p 2001 admin@localhost 

you don’t need to change anything in this command, the default password is admin

and change the default application for handling “/ connect” and “/ call” events to our new REST Hook script using the command:

update app -l http://172.16.30.123/ defaultApp

replace 172.16.30.123 with the IP address or domain name of your backend web server.

After all these settings, go to the front-end web server.

More code to god of code!

Create two empty files Click-to-Call-min.html and Click-to-Call-min.js on the frontend. These files will contain the minimal code to implement the “Click to call” button.

HTML code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://flashphoner.com/downloads/builds/flashphoner_client/wcs_api-2.0/current/flashphoner.js"></script>
    <script type="text/javascript" src="Click-to-Call-min.js"></script>
</head>
<body onload="init_page()">
	<input type="button" id="callBtn" type="button" Value="Call"/>
    <div id="remoteAudio"></div>
    <div id="localAudio"></div>
</body>
</html>

Of JS of minimal example code we remove the data for connecting to the SIP server and the called subscriber number.

JS code:

var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; 
var CALL_STATUS = Flashphoner.constants.CALL_STATUS;
var localAudio;
var remoteAudio;

function init_page(){
	Flashphoner.init({});
	localAudio = document.getElementById("localAudio");
    remoteAudio = document.getElementById("remoteAudio");
    connect();
}

function connect() {
	var url = "wss://172.16.30.124:8443"
    var sipOptions = {
        registerRequired: true
    };
    var connectionOptions = {
        urlServer: url,
        sipOptions: sipOptions
    };
    console.log("Create new session with url " + url);
    Flashphoner.createSession(connectionOptions).on(SESSION_STATUS.ESTABLISHED, function (session) {
        console.log(SESSION_STATUS.ESTABLISHED);
    }).on(SESSION_STATUS.REGISTERED, function (session) {
        console.log(SESSION_STATUS.REGISTERED);
    });
	callBtn.onclick = call
} 
	
function call(session) {
	var constraints = {
        audio: true,
        video: false
    };
	var session = Flashphoner.getSessions()[0]; 
    var outCall = session.createCall({
        remoteVideoDisplay: remoteAudio,
        localVideoDisplay: localAudio,
        constraints: constraints,
        stripCodecs: "SILK"
	});
    outCall.call();
	callBtn.value = "Hangup";
	callBtn.onclick = function () {
		callBtn.value = "Call";
		outCall.hangup();
		connect();
	}
}

Hello, girl, connect

For testing we need:

  1. The test bench that we have collected above (frontend, WCS, backend);

  2. SIP server

  3. Two SIP accounts

  4. Browser

  5. SIP soft phone

We transfer the data for connecting the SIP account 10001 in the JS code of the page with the “Click to Call” button using the REST Hook. The “Click to Call” button is programmed to make a call to the number 10002. We will enter the credentials for account 10002 into the soft SIP phone.

Open the HTML page created on the front-end web server and click the “Call” button:

We accept an incoming call on the SIP softphone and check that there is an exchange of audio streams between subscribers:

It took a little work, but the result is worth it. Now the credentials of the SIP server and the number of the called subscriber are protected from intruders, and you don’t have to worry that someone is using your telephony to their advantage.

Links

Our demo server

Online call from the site by button

Web-SIP phone in the browser

Flashphoner.properties settings file

REST hooks

Web Call Server Rapid Deployment Documentation

Call to mobile phone via SIP server

Web SDK Click to Call

Android Click to Call

iOS Click to Call

Similar Posts

Leave a Reply Cancel reply