Ok, so I have been playing around with SignalR for a little while now. I have a VS2013 solution containing the following projects:
At the moment these run locally, domains are forwarded using hosts file. They run on separate domains because I want SignalR to run completely separately from any other project (since it will also be accessed by some desktop software, and possibly an iOS app).
In the Mvc website a little messageboard is running. This entire website including the messageboard is behind forms auth. I retrieve all messages for the board via SignalR, using Javascript to call a GetMessages
function on a custom MessageHub
:
$(function () {
// Declare a proxy to reference the hub.
var signalrUrl = SignalrBaseUrl + '/signalr';
var messageList = $('#messageList');
var messenger = $.connection.messengerHub;
messenger.client.addMessage = function (message) {
addMessageToList(message);
};
function init() {
messenger.server.getAllMessages().done(function (messages) {
messageList.empty();
addMessagesToList(messages);
})
}
[...]
// Start the connection.
$.connection.hub.url = signalrUrl;
$.connection.hub.logging = SignalrLogging;
$.connection.hub.start().done(init);
})
I want only authenticated users to be able to access the Hub, and all Hubs for that matter. Setting the Authorize attribute (or forcing authentication for all hubs via the config) is known to me, my question is about the authentication part.
So how is authentication properly done? I believe sharing the cookie won't work as long as we're on different domains (can someone confirm?). For this I need to somehow use the created Cookie in the Mvc website (I guess?) to use, but I have no idea how to send it to SignalR and what to compare it to? Or send the username and password to SignalR and do authentication there against the Membership provider in the businesslayer? What is the best pattern for achieving this? Any thoughts would be greatly appreciated.
Eventually I found the solution, by looking at some examples for single sign-on techniques for forms authentication. Basically what I did was:
add the following to BOTH the MVC and the SignalR web.config:
<authentication mode="Forms">
<forms name="MyCookieName" domain=".domain.nl" cookieless="AutoDetect" loginUrl="My/Login/Url" timeout="525600" />
</authentication>
<machineKey validationKey="12345etc" decryptionKey="cte54321" validation="SHA1" decryption="AES" />
It is very important that both projects run under the main domain name domain.nl (e.g. subdomain1.domain.com and subdomain2.domain.com). The machine key is used for cookie and ticket encryption and should be set in both web.configs. (for more info: http://www.codeproject.com/Articles/27576/Single-Sign-on-in-ASP-NET-and-Other-Platforms)
In the mvc project, the cookie gets set after login as usual:
FormsAuthentication.SetAuthCookie(loginName, true);
I used the chrome developer tools to check that the cookie got set properly. For testing purposes I put the following in my signalr hub, putting a breakpoint at the if statement:
if (Context.User.Identity.IsAuthenticated)
{
}
Then the User was not null, and IsAuthenticated was true.
Eventually I added
GlobalHost.HubPipeline.RequireAuthentication();
in my SignalR Startup class, to ensure only authenticated users can access any hub. Before login I now receive a 401 Not authorized when trying to reach the hub. After login, the cookie is created by the mvc website which is shared with SignalR, and the hub can be reached. Works like a charm and was much simpler than I expected!
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