Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authenticate SignalR Hub using ServiceStack Authentication Plugin

I have created a ServiceStack service on top of Asp.Net that implements Basic authentication. Everything is working fine on the service routes. I am able to login and I get the session cookies which are validated on subsequent calls. I'm using an HttpClient for those requests.

I also have a SignalR Hub that runs on the same Asp.Net service, but the Principal is not authenticated on my Hub methods.

Basically what I need is for ServiceStack to intercept calls into my Hub and validate the session cookie and populate the Context.User.Identity and mark it as authenticated. If I can get that set up, a simple [Authorize] attribute on my hub will do the rest.

Here is a sample of my code:

// set up a HttpClient with a cookie container to hold the session cookie
var cookieJar = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cookieJar, UseCookies = true,  UseDefaultCredentials = false };

var client = new HttpClient(handler) { BaseAddress = _baseUri };

client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));

// do client login and get response with session cookie...
var response = client.PostAsync(...);

// add the cookies to the SignalR hub connection
var responseCookies = cookieJar.GetCookies(_baseUri);
var cookieContainer = new CookieContainer();
foreach (Cookie cookie in responseCookies)
{
   cookieContainer.Add(cookie);
}
_hubConnection = new HubConnection(_baseUri.ToString()) { CookieContainer = cookieContainer };

After this setup, my session cookies are sent to the Hub on each invocation. Somehow I need for ServiceStack to intercept those requests and set the authenticated user.

like image 423
Kevin Avatar asked Jan 04 '13 20:01

Kevin


2 Answers

Let ServiceStack do the authenication and persisting the user session. Then in the SignalR hub endpoints that need authentication put this code:

var cache = AppHostBase.Resolve<ICacheClient>();
var sess = cache.SessionAs<AuthUserSession>();
if (!sess.IsAuthenticated)
    throw new AuthenticationException();
like image 177
Johan Avatar answered Oct 23 '22 14:10

Johan


Johan's answer works but it is not very convenient to put this code to every method and if you put it in the hub constructor, it will fail in the web on page refresh with "Only ASP.NET Requests accessible via Singletons are supported" exception.

Instead, I have chosen to create a custom attribute that gives you more control on the hub and method call authorization.

Simplest attribute would look like this:

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class AuthorizeServiceStack : AuthorizeAttribute
{
    public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
    {
        return CheckAuthorization();
    }

    public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
    {
        return CheckAuthorization();
    }

    private static bool CheckAuthorization()
    {
        var cache = AppHostBase.Resolve<ICacheClient>();
        var sess = cache.SessionAs<AuthUserSession>();
        return sess.IsAuthenticated;
    }

}

As you can see, the code is the same as in Johan's answer but it will work in the web as well, ensuring that HttpContext.Current is not null when you are calling cache.SessionAs

like image 2
Alexey Zimarev Avatar answered Oct 23 '22 14:10

Alexey Zimarev