Click here to Skip to main content
Click here to Skip to main content
Go to top

Securing a Web Application with a Bluetooth Headset Adapter

, 7 May 2013
In this article, I am going to show you a proof-of-concept code segment that demonstrates of how you can use the serial number of a Bluetooth headset adapter to assist in providing an authentication mechanism to a web application.

Editorial Note

This article is in the Product Showcase section for our sponsors at CodeProject. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.

Introduction

It is becoming more and more accepted that passwords alone are not enough to secure an application. There is a need for "two-factor authentication" to more adequately secure sensitive data. Two-factor authentication is the notion that in addition to a password, some physical identifier must be presented that assists in identifying a user to a system. Typical implementations of two-factor authentication feature a physical token or a phone-based app that generated a unique serial number every 60 seconds.

Fortunately, Plantronics new Legend UC headset adapter comes with a unique serial number that identifies it. With this additional piece of information, we can use this as part of this authentication mechanism. In this article, I am going to show you a proof-of-concept code segment that demonstrates of how you can use the serial number of a Bluetooth headset adapter to assist in providing an authentication mechanism to a web application. This is not a completely secure implementation, and there are ways to enhance the implementation, but I will leave those exercises to the security experts.

Project Setup

In this sample, I will start with an ASP.Net Empty Web Application. In order to use the Plantronics Spokes client-side library, we need to add jQuery to the application. I can add jQuery easily by running the following command in the package manager console:

Install-Package jQuery

With jQuery installed, I also need to add a reference to the spokes.js file. You can get a copy of this script in the Plantronics SDK ‘Zeus’ sample. If you installed the SDK, the spokes.js file is located at: C:\Program Files (x86)\Plantronics\Plantronics SDK\Samples\Zeus

I have assembled a very simple login box on a default.html file that looks like the following:

This simple box allows the user to key in their userid, password, and their current authenticator value. In my hypothetical scenario, we will automatically complete the authenticator box with the serial number value from the headset. The HTML for this box looks like the following:

  <fieldset id="loginBox">

    <legend>Login:</legend>

    <form method="post" action="/account/login.aspx">
      <b>User Id:</b><input type="text" id="userId" name="userId" />
      <br />
      <b>Password:</b><input type="password" name="pass" />
      <br />
      <b>Authenticator:</b><input type="text" id="authenticator" name="authenticator" />
      <br />
      <input type="submit" value="Login" name="text" id="btnLogin" />
    </form>
 
  </fieldset>

Activating the Form

To activate the interaction with the Plantronics adapter, we need to start adding some JavaScript references and building a script to communicate with the headset services. First, I will add script references to the spokes.js and jquery.js files in this HTML file. Next, I will create and add a reference to a new script file called auth.js that will contain all of my logic to perform authentication.

Pro-tip: Create a reference at the top of any JavaScript file to allow Visual Studio 2010 and 2012 intellisense to identify the script content you are working with and present helpful tooltips to you while you code. Simply create a reference comment at the top of your file in the format:

/// <reference path="spokes.js" />

And Visual Studio make the references available to you.

For the purposes of this form, I am going to write a full JavaScript object that will maintain all information. I’ll start that construct with a self-executing anonymous function structure like the following:

/// <reference path="spokes.js" />
var auth = (function () {

})();

This will define a new object, remember that functions are objects in JavaScript, and assign it to the globally scoped auth variable. Inside of the function-object, I will define a constructor that will begin polling the adapter for information about the device and state changes.

var auth = (function () {

  var pollingInterval = 5000;

  var Auth = (function () {

    var spokes = new Spokes("http://127.0.0.1:32001/Spokes");
    var attached = false;

    // Constructor
    function Auth() {
      ConnectSpokes();
      setInterval(function () {
 
        ConnectSpokes();

      }, pollingInterval);
 

    }

    Auth.prototype.devicePresent = false;
    Auth.prototype.serialNumber = "NOT SET";

    return Auth;
 
  })();

  return new Auth();

})();

Several things are going on here: first, a private scope variable called pollingInterval is declared and is set with a 5000ms interval. Next, an internal object is created with private scope variables for the Spokes object and to also remember if we have properly connected to a device. Next a constructor is defined that will attempt to connect to the adapter using the Spokes (function to come) and then sets up a periodic poll to maintain that connection.

It is important that we periodically poll for the state of the Spokes connection because the Spokes JavaScript library will not inform us if we are disconnected from the device. Instead, the library publishes events to an internal queue that we must poll. There is no way to see the current state of the adapter, you must remember what the last event raised was and behave accordingly. By taking these steps, we can automatically format and reformat the login box appropriately as the state of the device changes.

The ConnectSpokes function that performs this polling makes use of a call to the Spokes Device object. Here is the source for that function:

function ConnectSpokes () {
      spokes.Device.deviceList(function (result) {
        if (result.isError) {
          console.log("Unable to connect to headset");
          SetLoginBox(false);
        }
        else if (result.Result[0] == null) {
          console.log("Error - Is there a headset connected?");
          SetLoginBox(false);
        }
        else {
          console.log("Connected to the headset adapter");
          Auth.prototype.devicePresent = true;
          Auth.prototype.serialNumber = result.Result[0].SerialNumber;
 
          SetLoginBox(true)
 
        }
 
      });
    };

The Spokes library exposes a deviceList method that takes a callback function to handle the results of the request to the hardware. This callback is executed asynchronously, so the ConnectSpokes function actually and returns control very quickly, but is not completed executing until some time later. The result object that is passed in to this method is inspected to determine if there are any errors in connecting to the adapter, and to log appropriate messages to the console. You could easily change this to perform some other action if necessary, in this sample I have it call setLoginBox with a false argument. If we do have a successful connection to the device, I log it appropriately and set two static methods on the Auth object using the Auth.prototype syntax. These two variables indicate that there is a devicePresent and the serial number of the device. Finally, in the case that we did connect properly to the headset adapter, I call SetLoginBox with a true argument.

The SetLoginBox function is an internal function that uses some jQuery DOM manipulation to configure the authenticator box and to intercept the login button’s click action to set the authenticator value from the serial number on the headset adapter:

function SetLoginBox(headsetPresent) {
      if ($("#loginBox")[0]) {
 
        if (headsetPresent) {
 
          if (!attached) {
            $("#authenticator").attr("disabled", true).css({ backgroundColor: "silver", color: "red", fontWeight: "bold", textAlign: "center" }).val("HEADSET");
            $("#btnLogin").click(function () {
              $("FORM").append("<input type='hidden' value='" + auth.serialNumber + "' name='authenticator'/>");
            })
            attached = true
          }
 
        } else {
          if (attached) {
            $("#authenticator").removeAttr("disabled").css({ backgroundColor: "transparent", color: "inherit", fontWeight: "inherit", textAlign: "inherit" }).val("");
            $("#btnLogin").unbind("click");
            attached = false;
          }
        }
 
      }
    };

I wrap the execution of this method in a test for the loginBox object. By taking this defensive coding step, I can add this code to any page on the application that the loginBox may appear on. Finally, I needed to wrap the checks and formatting for the HTML objects in a check on the class scoped attached variable. Without this check, this method would attach multiple click event handlers to the btnLogin object. This would create a nasty memory leak if this method had executed hundreds of times, and had hundreds of handlers hooked up to the click of the login button.

The resulting format of this method when the headset adapter is present looks like the following figure:

This is a simple implementation, but the concepts are sound: you can use a bluetooth device to provide authenticator capabilities.

Download the source code for this article and the Plantronics SDK to give it a try. I’m sure you’ll find some great capabilities that you can use in your applications

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jeffrey T. Fritz
Telerik
United States United States
A Microsoft MVP, ASPInsider and ASP.NET Developer Evangelist for Telerik. Jeffrey is a software developer coach, architect, and speaker in the Microsoft.Net community. A Pluralsight author and international speaker, Jeffrey makes regular appearances at conferences such as TechEd, DevIntersection, CodeStock, FalafelCon, DevReach and New York Code Camp as well as user group meetings in an effort to grow the next generation of software developers
Follow on   Twitter   Google+

Comments and Discussions

 
QuestionDownload the source code for this article PinmemberDenno.Secqtinstien18-Sep-13 20:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 7 May 2013
Article Copyright 2013 by Jeffrey T. Fritz
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid