Developer's Perception

Odd thoughts from a developer.

CORS vs ASP.NET Session

| Comments

The last couple of days I have been trying to implement a pure javascript client that could connect to our SOAP API.

To do that we need to enable Cross-origin resource sharing (called CORS for short). Basically, as a security measure browsers implement Same origin policy ensuring that only programs loaded from the same domain can query resources on that domain.

Thus, JavaScript loaded from http://www.example.com/myprogram.html could query http://www.example.com/api, but no http://www.test.com/api.

As Rich Internet Applications written as javascript clients is seeing an increase in popularity (and ease of implementing due to lots of nice frameworks such as Backbone), more and more providers of services will want to expose those services directly to the clients.

To enable CORS we need to set a series of headers and respond to the HTTP OPTIONS verb request (used by the browser to figure out if the server actually supports CORS). To be honest I have yet to figure out all the headers and exactly why each one is required, but with some trial and error I have gotten it to work.

Allowing CORS from ASP.NET SOAP WebService

The first challenge for our API was to get our ASP.NET SOAP service to set the correct headers (which I just admitted to not knowing…) for only that service.

It turned out this is actually not that hard and throwing in a HttpModule setting the headers for requests to a specific url (the service) with a lot of random (more or less) headers actually enabled me to invoke the service from javascript.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void AddCors(HttpApplication httpApplication)
{
  var sb = new StringBuilder();
  var hdrs = httpApplication.Response.Headers;
  var origin = hdrs.Get("Origin");
  if (string.IsNullOrEmpty(origin))
  {
      origin = "*";
  }
  hdrs.Add("Access-Control-Allow-Origin", origin);
  hdrs.Add("Access-Control-Allow-Credentials", "true");
  sb.Append("Content-Type, Cake");

  if (httpApplication.Request.HttpMethod.ToUpper() != "OPTIONS")
  {
      hdrs.Add("Access-Control-Allow-Headers", sb.ToString());
      return;
  }

  hdrs.Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
  sb.Append(",authorization,Content-Type,accept");
  sb.Append(",accept-charset,accept-encoding,accept-language");
  sb.Append(",connection,content-length,content-type");
  sb.Append(",host,origin,referer,user-agent, SOAPAction");
  hdrs.Add("Access-Control-Allow-Headers", sb.ToString());
  hdrs.Add("Access-Control-Max-Age", "3000"); //seconds
  hdrs.Add("content-length", "0");
  httpApplication.Response.StatusCode = 204;
}

Consuming SOAP envelopes from JavaScript

I tried out a number of promising leads for consuming SOAP services. The common ground for all of them was that they used the XmlHttpRequest object internally and tried to generate a proxy based on the wsdl of the service. After a couple of tries I gave up on this approach and went closer to the metal.

Using fiddler and an integration that worked (written as a server) I spied on the SOAP envelopes needed to send the requests, and saved them as templates (in the format for Mustache.js). This way I could easily put in the values needed without having to worry about serializing object and manipulating XML. The response was simply parsed using regex’ and thus I was able to handle the envelopes.

Actually, this way of handling although primitive is not half bad. If the service changes the service contract the template/regex has to be updated, but code generated on top of the wsdl would suffer from the same, and the implementation is extremely simple and basic this way.

The last bit was sending the requests to the server which was done through jQuery and the XmlHttpRequest. This took me some fiddling (not using fiddler) and googling to find the exact parameters and such. Again I used fiddler to find the correct headers for the requests and added them using jQuery.

Using ASP.NET Cookie SessionState from JavaScript

Thus, I was able to log in to our web service and retrieve the key used for subsequent request as an access token (actually the ASP.NET session id).

Unfortunately, the web service uses ASP.NET session to maintain whether you are logged in or not. Thus, a Set-Cookie header is returned from the Connect method and that Cookie must be returned in the header of the following requests to ensure access.

Due to security measures it is not possible to add the Cookie header manually to the XmlHttpRequest object. As the session id is also returned in the SOAP response envelope it is not a problem to retrieve it and I managed it to send it in another header called CAKE (part of the explanation for the many weird headers in the CORS implementation above).

Thus, I was stuck with changing the service implementation (not relying on ASP.NET session or somehow hijacking it using my CAKE header) or forcing the potential consumers of our API to use one of the potential hacks using iframes or JSONP (which I have not tested), but at least the last option defeats the purpose of making it easy to access (one could ago that SOAP does the same, but that is another battle).

What is the point you might ask? I actually managed to implement CORS, but due to the implementation using ASP.NET Session it is completely useless.

Anyone has any hacks worth trying?

Next step…. Something more usable than SOAP……. Not using ASP.NET Session

Comments