I have a WebApi secured with Basic Auth which is applied to the entire Api using a AuthorizationFilterAttribute. I also have SignalR Hubs sitting on several of my Api Controllers.
Alongside this I have a web page which makes use of my WebApi. The web page is mostly written in Backbone, so in order to make calls to my secured WebApi, I have added the following jquery
$.ajaxSetup({
beforeSend: function (jqXHR, settings) {
jqXHR.setRequestHeader('Authorization', 'Basic ' + Token);
return true;
}
});
This works for communicating with my Api Controllers, but adding the above code has broken the connection to my SignalR Hubs, specifically:
XMLHttpRequest cannot load http://localhost:50000/signalr/negotiate?_=1366795855194.
Request header field Authorization is not allowed by Access-Control-Allow-Headers.
Removing the jqXHR.setRequestHeader()
line restores my SignalR Hub connection but breaks the Api calls.
Given the above, I could do something hacky and only set the request header if the request being made isn't to /signalr but that just feels dirty...
Is there a cleaner way around this?
Am I just doing something silly? Has anyone else ran in to this?
I think the real solution for the issue will be to make sure that "Authorization" is part of the allowed Headers(Access-Control-Allow-Headers) returned from the signalR response for "negotiate" request.
You could register the header in your web.config just like this possibility.
What I didn't mention before is that I have a DelegatingHandler which sends back the correct Headers to any request coming in to my WebApi. This works perfectly for any requests to my WebApi but I wrongly assumed that this would also apply to SignalR requests.
As SignalR relies on several different transport methods, it doesn't seem reasonable to assume I have access to Authorization headers in the first place - they're not a requirement of all WebSockets implementations for example (see here)
My current solution has been to make use SignalR's HubPipeline (detailed here). Using this, I believe I can pass the Basic Auth credentials in a query string and write a separate module for handling Authorization for the SignalR requests:
Passing the Query string
The Filter
Registering the Filter
Additionally...
Note that because it's not a reliable assumption to send an Authorization header with SignalR requests, for the aforementioned reasons, I am still filtering my $.ajaxSetup to only affect non-SignalR requests:
In doing this, I'm leaving SignalrBasicAuthFilterAttribute class to take on full responsibility for Authorizing SignalR requests.
Further Reading: