DevStack

Building a Two-Factor Authentication system using Bandwidth’s BXML

Jacob Mulford
Jacob Mulford
Share

We’ve previously discussed the benefits of two-factor authentication and how to build a two-factor authentication system using Bandwidth’s Voice API in an earlier blog post. But what about building a two-factor authentication system using Bandwidth’s BXML?

What is BXML?

Bandwidth’s BXML is an alternative to Bandwidth’s Voice API for handling phone call operations for active phone calls. Take note that I said “active” phone calls; BXML is a template language for operating on active phone calls and cannot be used to create a phone call. Creating a call must be done with Bandwidth’s Voice API. Documentation for creating a phone call can be found here. Other than creating phone calls, all functionality provided with Bandwidth’s Voice API can be achieved with Bandwidth’s BXML.

BXML works by you hosting a server that contains endpoints with different BXML documents. Bandwidth accesses these endpoints with HTTP GET requests to update your active phone call, and each BXML document has a reference to the location of the next BXML document. Documentation for Bandwidth’s BXML can be found here.

Two-Factor Authentication (2FA) with BXML

Here’s a simplified overview of how 2FA works for registration and login systems with phone calls.

Register: User registers with username, password, and phone number => Bandwidth calls the user’s phone number => User must perform some task with the phone call to prove that he/she owns the phone number => Registration is approved or denied based on the result of the user’s task => If approved, the user’s username, password, and phone number are stored in the login system

Login: User logs in with username and password => If the username and password combination is correct, Bandwidth calls the phone number linked to this user => User must perform some task with the phone call to approve or deny the login => Login is approved or denied based on the result of the user’s task

Now, let’s discuss a possible implementation of each of these systems using a simple login system and Bandwidth’s BXML.

During the registration process, our goal is to confirm that users own the phone number that they claim to own. If every registration attempt is assigned a random 4-digit code that can only be seen on the user’s browser, then the only way anyone answering the phone call on the provided phone number can know the code is if they are also viewing the registration page.

For logging in, our goal is to finalize the login process with confirmation on the previously authenticated phone number. A simple phone call with a prompt such as, “Press 1 to login. Press anything else to deny the login,” will work. This will finalize the login only if the user presses 1 after answering the phone call will be sufficient for this.

Routes that create BXML for handling these flows are shown below

get "/bxml/login" do
  protected!
  # Ask to press 1 to login
  number = escape_phone_number(params['number'])
 
  requestUrl = "%s/bxml/login/getDigits?number=%s" % [$SERVER, number]
  bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
  bxml_string += '<Response>'
  bxml_string += '<Gather requestUrl="%s" maxDigits="1">' % [requestUrl]
  bxml_string += '<SpeakSentence voice="susan" gender="female">Please press 1 to authenticate the login, and anything else to deny th
  bxml_string += '</Gather>'
  bxml_string += '</Response>'
 
  return bxml_string
end
 
get "/bxml/register" do
  protected!
  # Ask user to input secret code
  number = escape_phone_number(params['number'])
 
  requestUrl = "%s/bxml/register/getDigits?number=%s" % [$SERVER, number]
  bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
  bxml_string += '<Response>'
  bxml_string += '<Gather requestUrl="%s" maxDigits="4">' % [requestUrl]
  bxml_string += '<SpeakSentence voice="susan" gender="female">Please input your secret 4 digit code that is displayed on the screen<
  bxml_string += '</Gather>'
  bxml_string += '</Response>'
 
  return bxml_string
end
 
get "/bxml/register/getDigits" do
  protected!
  # Get digits from request
  # Store in register hash for number
  number = unescape_phone_number(params['number'])
  digits = params['digits']
  $REGISTER_INPUTS[number] = digits
 
  requestUrl = "%s/bxml/hangup" % [$SERVER]
 
  bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
  bxml_string += '<Response>'
  bxml_string += '<Redirect requestUrl = "%s"></Redirect>' % [requestUrl]
  bxml_string += '</Response>'
 
  return bxml_string
end
 
get "/bxml/login/getDigits" do
  protected!
  # Get digits from request
  # Store in register hash
  number = unescape_phone_number(params['number'])
  digits = params['digits']
  $LOGIN_INPUTS[number] = digits
 
  requestUrl = "%s/bxml/hangup" % [$SERVER]
 
  bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
  bxml_string += '<Response>'
  bxml_string += '<Redirect requestUrl = "%s"></Redirect>' % [requestUrl]
  bxml_string += '</Response>'
 
  return bxml_string
end
 
get "/bxml/hangup" do
  protected!
  bxml_string = '<?xml version="1.0" encoding="UTF-8"?>'
  bxml_string += '<Response>'
  bxml_string += '<Hangup></Hangup>'
  bxml_string += '</Response>'
 
  return bxml_string
end

The full sample code for this application can be found here.

BXML security

Notice all these routes begin with a “protected!” function call. This function, taken from Sinatra’s documentation, is shown below:

# Taken from http://sinatrarb.com/faq.html
def protected!
  return if authorized?
  headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
  halt 401, "Not authorized\n"
end
 
# Taken from http://sinatrarb.com/faq.html
def authorized?
  @auth ||=  Rack::Auth::Basic::Request.new(request.env)
  @auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == [$SERVER_BXML_USERNAME, $SERVER_BXML_PASSWORD]
end

Since BXML is accessed by making a GET request on a URL, anyone can access BXML by knowing the URL that hosts the BXML. In addition, any operation on your server that is performed when accessing BXML can be performed at any time by an unknown user making a request. This can be very problematic in some cases. For example, with the above application, an unknown user can bypass the login authentication by simply making the following request in a browser while Bandwidth is performing the gather (assuming the unknown user knows a valid user’s phone number):

<server>/bxml/login/getDigits?number=<number>&digits=1

Thankfully, routes can be configured to only accept requests under certain conditions, as shown above with the Ruby/Sinatra server. These routes can be configured to only accept a request sent with certain credentials, and Bandwidth can include these credentials with callbacks. It is highly advisable to protect your BXML routes. More information on this feature can be found here.

Conclusion

For the end user experience, a voice application developed with BXML is equivalent to a voice application developed with API calls. However, for the developer, these experiences are very different. It is important to understand the differences in the interaction between your server and Bandwidth when using both BXML and API calls. Still have questions? Get in touch with one of our experts to learn more about building a two-factor authentication system using our APIs.

Talk to an expert Watch our BXML video