SharePoint 2013 – Integration Challenges – #4 Same Origin Policy & Authentication – CORS – Azure DevSecOps
Azure DevSecOps

SharePoint 2013 – Integration Challenges – #4 Same Origin Policy & Authentication – CORS

Advertisements

Hi,

I’m currently involved in integrating SharePoint with IBM Connections and I’m having a lot of fun trying to figure out all the possibilities. Since these integration considerations are not specific to SharePoint/IBM Connections, I’ll blog a series of posts which will be rather short or rather long according to the topic I’m focusing on.
In this post, I’m going to focus on CORS (Cross Origin Resource Sharing) and SharePoint 2013 considering that one way to consume SharePoint data from non Microsoft products is to leverage the SharePoint 2013 REST APIs. Microsoft solutions could make use of the CSOM but it’s not available when working with Java. Moreover, CORS is bi-directional while the CSOM in combination or not with the App Model is meant to consume SharePoint data from remote but not the other way around. Moreover, the App Model is also most suited when remaining in the Microsoft stack. I know that you can build provider-hosted apps with any technology including JAVA but it’s probably not the easiest path. So, I’ll take the angle of a remote application (say java) that tries to consume SharePoint’s REST API. But the same kind of technique can be used if you wish to work the opposite way (from SharePoint to a Remote App exposing some REST APIs)
If you haven’t read my previous posts of this integration challenges serie (click the Integration tag), you’d better do it to have a full picture of the possibilities. I’m not going to explain what’s the same-origin policy anymore.

CORS recap

In a nutshell, CORS is a communication between a browser and a server based on specific HTTP headers. The below schema shows a basic CORS conversation between a browser and a server:

where:

As this schema shows, this looks quite simple. On the server-side you can control which origin is allowed and which methods. So, you could for instance decide that GET and POST are allowed but not PUT and DELETE.
You could allow some specific HTTP Headers or not etc..

Ok for the theory but what about real world?

It turns out that at the time of writing, all the browsers are not yet compatible with CORS nor all the servers such as IIS if the targeted domain requires to be authenticated which is usually the case in SharePoint.
Because of that, preflight requests get a 401 (Unauthorized) answer from the server while it expects a 200. Therefore, for browsers that respect the CORS specification, if they receive anything else but 200, they should not perform the actual cross-domain request.Moreover, the HTTP request headers that should be part of a server response will not appear by magic, so it requires some extra configuration at the web server level.

Which approach to choose?

There are several approaches you can use in order to get things working with IIS and therefore, with SharePoint. A good blog post on that topic can be found at http://evolpin.wordpress.com/2012/10/12/the-cors/. I have tested his approach and it’s indeed working fine. In two words, in case you don’t want to read his whole post, the approach consists in :

public class CORSPreflightModule : IHttpModule
{
    private const string OPTIONSHEADER = "OPTIONS";
    private const string ORIGINHEADER = "ORIGIN";
    private const string ALLOWEDORIGIN = "http://....";
    void IHttpModule.Dispose()
    {

    }

    void IHttpModule.Init(HttpApplication context)
    {
        context.PreSendRequestHeaders += (sender, e) =>
        {
            var response = context.Response;

            if (context.Request.HttpMethod.ToUpperInvariant() == OPTIONSHEADER.ToUpperInvariant() &&
                context.Request.Headers[ORIGINHEADER] == ALLOWEDORIGIN)
            {
                response.StatusCode = (int)HttpStatusCode.OK;
            }
        };

    }
}

An alternative approach using a Reverse Proxy

So, instead of repeating what the other blogger said, let me propose an alternative approach. The HTTP module one is not my prefered option since it creates an unnecessary overhead at SharePoint level for every single request even those that are not concerned by CORS. As an alternative, if you have a Reverse Proxy at your disposal, you can achieve the same goal by placing the CORS logic at another level.
In development environments, you usually don’t benefit from Enterprise reverse proxies. Therefore, let’s see with our best friend Fiddler how to get the same results and get CORS up & running with SharePoint on your dev box. With this method, IIS is left untouched and no custom HTTP Module is required and if the final solution in the other environments use reverse-proxies, you’ll be closer to the reality.

Client request and authentication

$.support.cors=true;
$.ajax({
    url:"remote domain/_api/web/lists/getbytitle('Documents')/itemCount",
    xhrFields: {withCredentials: true},
     headers:{ "Accept": "application/json; odata=verbose" },
     contentType: "application/json; odata=verbose"
  }).done(function(data, textStatus){
     if (data.d) {
       $('#doccount').text("Number of documents in documents:"+data.d.ItemCount);
     }
  }).fail(function(){
      $('#doccount').text("failed");
  });

So, the query is very similar to a usual query. I just tell jQuery that I’m using CORS and I asked it to forward the credentials via the withCredentials attribute. For that to work fine, you need the server to send back the Access-Control-Allow-Credentials response header with the value true. If you have never authenticated to the remote domain at the time the AJAX query is sent, you’re likely going to be prompted (with Chrome/Firefox). With IE, you won’t be prompted if the target URL is in the Trusted/Intranet zone. This statement is valid for Claims – NTLM. If you use FBA, the browser will forward the Cookie if it already exists, meaning you have already authenticated before the AJAX request takes place otherwise, you’ll get a 403 which you could handle in code and redirect to the login page…Of course, as I stated in another post, you can also use Fiddler (or your production proxy) to inject authentication information on the fly but I’m not going to repeat those steps in this post.

Environment I used for my tests

Pros & Cons of CORS in combination with SharePoint 2013

Let’s start with the Cons:

No, let’s see the Pros:

But why using CORS via a reverse proxy?

If you read one of my posts about the same-origin policy http://www.silver-it.com/node/150 you might wonder why not just applying the technique it described via the reverse proxy instead of creating CORS specific rules. I think that the technique described in that post ends up in lying to the browser while if you place CORS specific rules at the reverse proxy level, you don’t lie to the browser and the same-origin policy is still taking place. Morever, you realy make use of an emerging standard, the only difference is that you place the configuration one step above.
However, lying to the browser comes with one advantage : it’s not browser/server dependent and will always work while CORS still has some limitations regarding portability. So, depending on your environment, you might chose what best suits you.
Happy Coding!

Advertisements

Advertisements