I have a CORS problem when self-hosting SignalR with OWIN, which only happens when I try to enable authentication.
The error I get in my web browser is:
XMLHttpRequest cannot load http://.../signalr/negotiate?[snip] Origin ... is not allowed by Access-Control-Allow-Origin
This only happens if I enable authentication in my self-hosted server using the approach in this answer:
public void Configuration(IAppBuilder app)
{
var listener = (HttpListener)app.Properties[typeof(HttpListener).FullName];
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
app.MapHubs(new HubConfiguration { EnableCrossDomain = true });
}
If I comment out the AuthenticationSchemes
line then CORS works (and I've checked everything in these instructions). I get the same problem if I use other authentication schemes than NTLM.
Using Fiddler to examine what's going on, without authentication enabled I see the necessary CORS headers coming back from the server:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: [my server]
However once I enable authentication I get a 401 response which is missing these headers. All the requests have the necessary Origin
header.
Having examined the SignalR source code it looks like the headers are being set, but presumably with authentication enabled the HttpListener
is sending the initial 401 response without hitting this code.
So I think my question is: How do I get the HttpListener
to include an Access-Control-Allow-Origin
header in its negotiation of authentication protocols?
I have gotten NTLM authentication to work with cross domain signalR self-hosted in OWIN by allowing the preflight requests anonymous access.
What one needs to do is create a delegate for choosing the authentication scheme which looks for the preflight request headers, and allows these through anonymously. All other requests will use NTLM.
public void Configuration(IAppBuilder appBuilder)
{
var listener = (HttpListener)appBuilder.Properties[typeof(HttpListener).FullName];
listener.AuthenticationSchemeSelectorDelegate += AuthenticationSchemeSelectorDelegate;
}
private AuthenticationSchemes AuthenticationSchemeSelectorDelegate(HttpListenerRequest httpRequest)
{
if (httpRequest.Headers.Get("Access-Control-Request-Method")!=null)
return AuthenticationSchemes.Anonymous;
else
return AuthenticationSchemes.Ntlm;
}
I presume you're using Chrome, which very unhelpfully tells you that these headers are missing and that this is the problem, when actually you have probably just forgot to set your XMLHttpRequest
's withCredentials
property to true
.
If you're using jQuery you can do this for all requests with:
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
options.xhrFields = { withCredentials: true };
});
You also need to do the right thing with OPTIONS
requests as in the other answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With