I needed to support, in addition to standard out-of-the-box cookies/forms authentication, also Basic Authentication over HTTPS for SignalR. SignalR runs in the context of a mixed MVC/WebApi site.
After putting the pieces together how to implement this, I used ThinkTecture.IdentityModel.Owin.BasicAuthentication
library for this on the server, like this:
app.Map("/basicauth", map =>
{
map.UseBasicAuthentication("realm", ValidateUser);
map.MapSignalR<AuthenticatedEchoConnection>("/echo");
map.MapSignalR();
});
But instead of returning a challenge, I always get a HTTP 302 response that redirects to the Login page of the MVC site. To better debug this, I quickly rolled my own simple OWIN middleware for basic authentication and got the same result. Further testing with a simple mapping like this:
app.Map("/test", map =>
{
map.Use((context, next) =>
{
// context.Response.StatusCode = 401;
context.Response.Write("Hello World!");
return Task.FromResult(0);
});
revealed that a simple "Hello World" response is returned normally. But when I comment out the the line that sets the response code to 401, I get the redirect to the Login page again. I do not understand this behavior ... why does my MVC site gets involved here and not the 401 response is returned? How can I prevent this?
For OWIN, besides the special /basicauth
map above, I had only the top level SignalR mapping in the startup method defined that should continue to work for all cookie authenticated calls:
app.MapSignalR();
Nothing else had been configured by me for OWIN.
Can anybody help me with this?
Ok, I found a way to work around this issue. The first part is to throw out map.MapSignalR<AuthenticatedEchoConnection>("/echo");
from the first sample - it is not necessary and for some reason I not found out it prevented SignalR from working properly.
The second part, and the workaround for the actual problem, is that in the client I also send the credentials with the first request and do not wait for a challenge. Thus, a 401 never happens and so no redirect to login page. And that's good enough for me.
So the workaround is, in the client, do not use NetworkCredential like this:
connection.Credentials = new NetworkCredential(username, password);
Instead, add the Authorization header yourself so it is included already in the first request:
string creds = string.format("{0}:{1}", username, password);
string encodedCreds = Convert.ToBase64String(Encoding.Utf8.GetBytes(creds));
connection.Headers.Add("Authorization", "Basic " + encodedCreds);
In addition, what I found out is that with OWIN, this issue seems to be a more general one and not only related when used with SignalR or even Basic Auth:
Thus, with some modifications to the basic authentication module, it should also be possible to prevent this redirection. I might look into this and will update this post when done.