CORS with Basic or Windows Authentication

Update

Along with one of our customers I rethought the approach below to integrate everything into the module, see the sample for more details.

Intro

In the ServiceAPI help there is a sample on how to use the CORS feature.  There are some limitations in this feature that make it unlikely to work for most ServiceAPI users.

Preflight

When using a browser, maybe via jQuery, the odds are that a preflight request will be sent.  This is an HTTP OPTIONS request to confirm that the correct CORS headers are returned.  This request is sent without any authentication.  The problem when using IIS basic or integrated authentication is that this request will be rejected.

The solution

The solution is not to use the CORS feature at all but to fall back on traditional ASP.Net techniques, as detailed in this very helpful post .  

Module

Firstly write a, ASP.Net Module to allow us to get into the pipeline before IIS rejects our request, compile this in Visual Studio and place the DLL in the ServiceAPI bin folder.  All OPTIONS requests will now be returned with the status 200.

public class CORSModule : IHttpModule
{
    public void Dispose()  { }

    public void Init(HttpApplication context)
    {
        context.PreSendRequestHeaders += delegate
        {
            if (context.Request.HttpMethod == "OPTIONS")
            {
                var response = context.Response;
                response.StatusCode = (int)HttpStatusCode.OK;
            }
        };
    }
}

Configure module

Now we wire the module into our service in the system.webServer\modules element...

<modules runAllManagedModulesForAllRequests="true">
  <add name="CORSModule" type="CORSModule" />
</modules>

Set CORS headers

Lastly we add the CORS headers to system.webServer\httpProtocol

<customHeaders>
  <add name="Access-Control-Allow-Methods" value="POST,GET,OPTIONS" />
  <add name="Access-Control-Allow-Origin" value="http://localhost" />
  <add name="Access-Control-Allow-Headers" value="Content-Type,Authorization" />
  <add name="Access-Control-Allow-Credentials" value="true"/>
</customHeaders>

Test it out

Once you have implemented the above the jQuery code like this should be successful in accessing a ServiceAPI instance using basic authentication.

$.ajax({
    beforeSend: function (xhr) {
            xhr.setRequestHeader("Authorization", "Basic " + btoa("USERNAME" + ":" + "PASSWORD"));
    },

    url: "http://davidc2012r2/ServiceAPI82/Record/1?&properties=RecordTitle",
    type: 'GET',
    dataType: 'json',
})
.done(function (response, statusText) {
    
    if (statusText === "success") {
        alert("Record named: " + response.Results[0].RecordTitle.Value + " was found.");
    }
})
.fail(function (xhr) {
    var err = eval("(" + xhr.responseText + ")");
    alert(err.ResponseStatus.Message);

});
Written on February 20, 2017