Integrating JavaScript web applications with a web service using Crowd

In a previous blog post Bram described how to set up a Stateless Web Service and secure this using a combination of Spring Security 3 and Crowd. In this entry of the blog post I will describe how you can communicate with this web service from a pure HTML & JavaScript web application.


A key concept of this strategy is retrieving a Crowd Single-Sign-On token and storing this locally at the client side, passing it to the web service with each subsequent call that requires authentication. There are multiple ways of achieving this goal, but in this example I shall demonstrate how to do this using HTML5’s web storage. If you haven’t read Bram’s blogpost yet, please do this first in order to gain some understanding on the material discussed here.

HTML5 web storage


As previously described in Oscar’s blogpost, a new function of HTML5 is the possibility to store data offline at the client side using the localStorage or sessionStorage. Contrary to cookies, these are not automatically sent to the server with each subsequent call. They are stored using a key/value structure and are available over multiple HTML files on the same domain. This creates the possibility of storing necessary data when logging in and retrieving this data later on. As this technique is quite modern, it’s advisable to do some research on browser support prior to implementing this solution. Should you wish to support older browsers as well, unfortunately you are bound to use cookies to hold this data in the web application.

The Crowd SSO token can be stored for later usage with the following JavaScript statement:
sessionStorage.setItem("crowdSSOToken", 
"IVB90N6WRu5BA3BlAZB3YA00");

Cross domain calls


Before it’s possible to perform calls to the web service, we need to make sure the browser doesn’t block these outgoing calls. One method of achieving this is by using Cross Origin Resource Sharing, with which you specify on the server-side which client addresses to accept calls from. Contrary to older methods like JSONP this method supports all types of HTTP requests, rather than just GET. CORS is also registered in a W3C standard. Like the HTML5 web storage this technique is relatively new, so research the browser support prior to implementing it in your web application.

The browser performs a “preflight call” first which asks the web service if the client web application is allowed to perform regular calls to the web service, and it returns the following response header if it is:
Access-Control-Allow-Origin: example .com

Login procedure


In the first call to the web service, we need to authenticate ourselves without a Crowd session token, as we do not yet possess one. This can be done by implementing a login controller in the web service or by passing the token from another application. In the case of logging in the first application, a username and password is required. Passing this to the CrowdAuthenticationProvider will return a Crowd SSO token, which can now be stored in the webStorage on the client side. The user can now be redirected to the index page or wherever he/she came from.

Subsequent calls to web service


In each subsequent call that is supposed to access protected resources, the Crowd token is necessarry as a method of authentication and authorization. This can be done by adding a header called “Authorization” to the request with the value: “Crowd crowdSSOToken”. The web service can retrieve the token from this header and pass this along to Crowd, which in its turn checks whether the passed token is valid. If so, Spring Security allows the call and returns the requested data. If not, a HTTP status 401 “Unauthorized” is returned to the web application. Upon retrieving a HTTP Status 401, it’s possible to redirect the browser to a login page. In this login page, a user can (re-)enter its credentials and try to retrieve a new Crowd token.

An example of an Ajax call using this header and redirecting upon retrieving a 401 status is shown below:
jQuery.ajax("https://rest.examplewebservice.com",
{
beforeSend : {
xhr.setRequestHeader('Authorization',
"Crowd " + sessionStorage.getItem("crowdSessionToken"));
},
statusCode : {
401: function() {
window.location.replace("./login.html");
}
}
});

Why don’t you just build a back-end?


Building a pure HTML/JavaScript web application has several advantages over building a web application with a back-end that generates a front-end to view in the browser. This is enough material for a seperate blog post, but to provide some subtext I will shortly elaborate on the matter here.

Firstly, there’s the issue of maintainability. If you build a web application with its own back-end which in its turn communicates with another web service, you’ll most likely have to build (some) duplication in order for it to work. The more code you write, the more code you end up maintaining. Why not cut out the middle man?

Secondly, there’s the issue of performance. Delegating as much work as possible to the client’s computer rather than generating the HTML on the server can save precious processing power and allows you to use it where you really need it. Modern web browsers have blazing fast JavaScript engines and are perfectly equipped to deal with the generation of web pages themselves. If this is the case, why not let the client side deal with it?

Furthermore, if the web application is built using a different programming language or framework this could lead to compatibility issues. An example we have experienced at 42 is the communication from a Grails web application to a Spring MVC web service. This required some conversion on both ends, which slowed the progress of the project drastically. By building a clean HTML/JavaScript web application, these issues can all be taken away.

Conclusion


After finishing these steps you can now authenticate and authorize a user with a Crowd SSO token. If you have several applications that communicate with the same Crowd server, you could pass this token along between these applications, in order to ensure users only have to login on one of these rather than all of them. As the username and password are sent over a request at least once, it’s advised to set up a secured (HTTPS) connection. The Crowd SSO token is automatically invalidated after a certain time of inactivity, after which a HTTP status 401: “Unauthorized” can be returned and the user is required to login again. This is a safety measure and the interval can be configured, should a Crowd SSO token ever fall in the wrong hands it’s not possible to gather a username and password from it, making it a safer alternative than repeatedly sending the username and password over the net.