Secure Your JMS JavaScript Client

Before you add security to your clients, follow the steps in Checklist: Secure Network Traffic with the Gateway and Checklist: Configure Authentication and Authorization to set up security on Kaazing Gateway for your client. The authentication and authorization methods configured on the Gateway influence your client security implementation. In this procedure, we provide an example of the most common implementation.

Before You Begin

This procedure is part of Checklist: Build JavaScript JMS Clients.

To Secure Your JMS JavaScript Client

This procedure contains the following topics:

Authenticating your client involves implementing a challenge handler to respond to authentication challenges from the Gateway. If your challenge handler is responsible for obtaining user credentials, then you will also need to implement a login handler.

Creating a Basic Challenge Handler

A challenge handler is a constructor used in an application to respond to authentication challenges from the Gateway when the application attempts to access a protected resource. Each of the resources protected by the Gateway is configured with a different authentication scheme (for example, Basic, Application Basic, Application Negotiate, or Application Token), and your application requires a challenge handler for each of the schemes that it will encounter or a single challenge handler that will respond to all challenges. Also, you can add a dispatch challenge handler to route challenges to specific challenge handlers according to the URI of the requested resource.

For information about each authentication scheme type, see Configure the HTTP Challenge Scheme.

Clients with a single challenge handling strategy for all 401 challenges can simply set a specific challenge handler as the default using webSocketFactory.setChallengeHandler().

Let's look at an example of how to implement a single challenge handler for all challenges. This example is taken from the Kaazing JMS JavaScript tutorial here: https://github.com/kaazing/javascript.client.tutorials/tree/develop/jms.

In this example, when the JMS JavaScript client connects to the Gateway, the Gateway wil issues a authentication challenge because the JMS service on the Gateway has a realm defined. The JMS JavaScript client will display a popup dialog to the user where the user can enter in username and password credentials.

First, create a popup dialog in HTML. Note the id values as these will be used by JavaScript to obtain the username and password values entered by the user.

<div id="login_div" class="panel">
 <span class="info">User name and password values are optional</span>

 <label for="url">Location</label><input id="url"/><br/>
 <label for="username">Username</label><input id="username"><br/>
 <label for="password">Password</label><input type="password" id="password"><br/>
 <label></label>
 <div id="sso_logindiv" style="margin-left: 2px; position: absolute; border: 1px solid black; border-radius:10px; display: none; height: 190px; width: 318px; background-color:#d0e7fd; z-index: 999;">
  <div style="margin-left: 20px; height: 35px; margin-top: 20px; font-weight: bold;">
   Login
  </div>
  <div style="height: 124px; width: 296px; border: 1px solid black; border-radius:10px; background-color:white; margin-left: 10px;">
   <div style="margin-left:10px; margin-top: 10px;">
    <span style="width: 60px; font-size:11pt;">Username:</span><input id="sso_username" size="12" style="width: 180px" value=""/>
   </div>
   <div style="margin-left:10px">
    <span style="width: 60px;  font-size:11pt;">Password:</span><input id="sso_password" type="password" size="12" style="width: 180px" value=""/>
   </div>
   <div style="margin-left:45px">
    <button id="sso_login" style="margin-left:25px; width: 60px;">OK</button>
    <button id="sso_cancel" style="margin-left:25px; width: 60px;">Cancel</button>
   </div>
 </div>
</div>

Next, when the client connects to the Gateway, call an authentication function (setupSSO()) and pass it the webSocketFactory.

function handleConnect() {
	log("CONNECT: " + url.value + " " + username.value);

	var jmsConnectionFactory = new JmsConnectionFactory(url.value);
	connect.disabled=true;
	//setup challenge handler
	setupSSO(jmsConnectionFactory.getWebSocketFactory());
...

In setupSSO(), create a new BasicChallengeHandler and apply a loginHandler to it. The loginHandler uses a callback function to call the popup and obtain the user credentials entered in the popup dialog. The credentials are returned via the callback. Lastly, the new BasicChallengeHandler is applied to the webSocketFactory.

function setupSSO(webSocketFactory) {
	/* Respond to authentication challenges with popup login dialog */
	var basicHandler = new BasicChallengeHandler();
	basicHandler.loginHandler = function (callback) {
		popupLoginDialog(callback);
	}
	webSocketFactory.setChallengeHandler(basicHandler);
}

Lastly, this is the popupLoginDialog() function used to collect the user credentials from the HTML popup dialog. Note the PasswordAuthentication() function to collect the username and password and the callback(credentials) that returns the credentials to the challenge handler.

function popupLoginDialog(callback) {
	//popup dialog to get credentials
	var popup = document.getElementById("sso_logindiv");
	popup.style.display = "block";
	var login = document.getElementById("sso_login");
	var cancel = document.getElementById("sso_cancel");

	//"OK" button was clicked, invoke callback function with credential to login
	login.onclick = function () {
		var username = document.getElementById("sso_username");
		var password = document.getElementById("sso_password");
		var credentials = new PasswordAuthentication(username.value, password.value);
		//clear user input
		username.value = "";
		password.value = "";
		//hide popup
		popup.style.display = "none";
		callback(credentials);
	}
	//"Cancel" button has been clicked, invoke callback function with null argument to cancel login
	cancel.onclick = function () {
		var username = document.getElementById("sso_username");
		var password = document.getElementById("sso_password");
		//clear user input
		username.value = "";
		password.value = "";
		//hide popup
		popup.style.display = "none";
		callback(null);
	}
}

This example is taken from the Kaazing Enterprise JavaScript Tutorials at https://github.com/kaazing/javascript.client.tutorials. For more information on challenge handlers and how to configure location-specific challenge handling strategies, see the JavaScript Client API.

To have the JavaScript tutorial prompt you for authentication, use the URL wss://demos.kaazing.com/jms-auth to connect to the Gateway. The username is tutorial, and the password is tutorial.

Managing Log In Attempts

When it is not possible for the Kaazing Gateway client to create a challenge response, the client must return null to the Gateway to stop the Gateway from continuing to issue authentication challenges.

The following example is modified code taken from the JavaScript tutorial (https://github.com/kaazing/javascript.client.tutorials) and demonstrates how to stop the Gateway from issuing further challenges.

var maxRetries = 2;
var retry = 0;
function setupSSO(factory) {
  /* Respond to authentication challenges with popup login dialog */
  var basicHandler = new BasicChallengeHandler();
  basicHandler.loginHandler = function(callback) {
    if (retry++ >= maxRetries) {
      callback(null);       // abort authentication process if reaches max retries
    }
    else {
      popupLoginDialog(callback);
    }
  }
  factory.setChallengeHandler(basicHandler);
}
...

Registering Challenge Handlers at Locations

Client applications with location-specific challenge handling strategies can register a DispatchChallengeHandler object, on which location-specific ChallengeHandler objects are then registered. The result is that whenever a request matching one of the specific locations encounters a 401 challenge from the server, the corresponding ChallengeHandler object is invoked to handle the challenge. For example:

var basicHandler1 = new BasicChallengeHandler();
basicHandler1.loginHandler = function(callback) {
        popupLoginDialog(callback);
};

var basicHandler2 = new BasicChallengeHandler();
basicHandler2.loginHandler = function(callback) {
        callback(new PasswordAuthentication("username", "password"))
};

var dispatchHandler = new DispathChallengeHandler();
dispatchHandler.register("ws://myserver.example.com", basicHandler1);
dispatchHandler.register("ws://anotherserver.example.com", basicHandler2);

webSocketFactory.setChallengeHandler(dispatchHandler);

// Enable the ChallengeHandler by using this webSocketFactory to create a new WebSocket object
var websocket = webSocketFactory.createWebSocket("ws://myserver.example.com");

Using Wildcards to Match Sub-domains and Paths

You can use wildcards (“*”) when registering locations using DispatchChallengeHandler. Some examples of locationDescription values with wildcards are:

Negotiating a Challenge

A Negotiate challenge handler handles initial empty "Negotiate" challenges from the Gateway. It uses other candidate challenge handlers to assemble an initial context token to send to the Gateway, and is responsible for creating a challenge response that can delegate to the appropriate candidate.

In addition, you can register more specific NegotiableChallengeHandler objects with this initial NegotiateChallengeHandler to handle initial Negotiate challenges and subsequent challenges associated with specific Negotiation mechanism types and object identifiers.

Use DispatchChallengeHandler to register a NegotiateChallengeHandler at a specific location. The NegotiateChallengeHandler has a NegotiableChallengeHandler instance registered as one of the potential negotiable alternative challenge handlers.

var negotiableHandler = new NegotiableChallengeHandler();
var negotiableHandler.loginHandler  = function(callback) {...};
var negotiateHandler = new NegotiateChallengeHandler();
negotiateHandler.register(negotiableHandler);

webSocketFactory.setChallengeHandler(negotiateHandler);

Next Step

You have completed the JavaScript client examples.

See Also

JavaScript Client API