Is it possible to use HttpModule for SignalR authentication

I am developing an application that uses HttpModule to perform custom authentication and authorization. My problem is that the user id set in the HttpModule is not available in SignalR context objects.

I am executing the following in my BeginRequest HttpModule after the custom validation logic:

var userClaims = new List<Claim>();
userClaims.Add(new Claim(ClaimTypes.NameIdentifier, <some id>));
userClaims.Add(new Claim(ClaimTypes.Name, <some name>));
userClaims.Add(new Claim(ClaimTypes.Email, <da email>));
userClaims.Add(new Claim(ClaimTypes.Authentication, "true"));

var id = new ClaimsIdentity(userClaims);
var principal = new ClaimsPrincipal(new[] { id });
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;

      

I thought this would absolutely cause everything in the future to behave as if the request was authenticated, however it is not.

I've created a SignalR AuthorizeAttribute class to handle authentication that looks like this:

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class CustomAuthAttribute : AuthorizeAttribute
{
    public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
    {
        if (HttpContext.Current.Request.Path.StartsWith("/signalr/connect"))
        {
            var test = (ClaimsPrincipal)HttpContext.Current.User;
            var test2 = (ClaimsPrincipal)Thread.Current.Principal;
        }

        return true;
    }

    public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubContext, bool appliesToMethod)
    {
        var test = (ClaimsPrincipal)hubContext.Hub.Context.User;
        return true;
    }
}

      

So my plan was to access the hubContext.Hub.Context.User var from the AuthorizeHubMethodInvocation method in order to do whatever custom permission I need. However, this just contains the default WindowsPrincipal.

If I look into the AuthorizeHubConnection call (which is actually a regular HTTP request, not a websocket call), I see that the HttpContext.Current object also doesn't have a custom set as it should.

I see that I can access the HttpContext.Current.Items collection. I am guessing I can use this to drop the Principal from the module into the SignalR context, but I'm not sure if this is what I should be doing.

Is it better to just rewrite the HttpModule as an OWIN middleware? It looks like I'll still have to change things when / if we upgrade to ASP.NET 5; there is nothing like MS products to keep you safe working.

+3


source to share


1 answer


I forgot that I posted this question a while ago. I ended up explaining my solution in a comment on the MS article Authentication and Authorization for SignalR Hubs . After trying to implement the OWIN middleware for auth, I found that I would need to do some kind of deadlock configuration to run all modules for all requests, which is inefficient. I couldn't figure out how to run only the Auth OWIN middleware component for all requests, so I gave up this approach and got stuck with my HttpModule. Below is a summary of my solution for SignalR auth, hosted on the page above:

1) Create the AuthorizeAttribute class as mentioned in the article:

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class CustomAuthAttribute : AuthorizeAttribute

      

2) Decorate your hub class with the auth class you created. The naming convention is the (SomeName) attribute on the auth class itself and (SomeName) to decorate the hub.

[CustomAuth]
public class ServerWebSocket : Hub

      

3) Instead of overriding the "UserAuthorized" method as shown in the docs, override the following methods (I got this from some other SO post, but can't find it right now):



public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubContext, bool appliesToMethod)

      

To actually allow users, I catch SignalR connection requests in my HttpModule and set the item in the HttpContext item collection, like so:

if (req.Path.StartsWith("/signalr/connect") || req.Path.StartsWith("/signalr/reconnect"))
{
    var user_info = doFullAuth(<some token>);
    HttpContext.Current.Items.Add("userDat", user_info);
}

      

This is actually configured so that connection requests will be completely rejected in the HttpModule if the user does not have permission. So I don't actually implement the SignalR auth method "AuthorizeHubConnection" at all. But in the "AuthorizeHubMethodInvocation" method, I access the user data by calling the HttpContext.Current.Items that was set in the original connection request and execute custom logic to determine if the user can access that method.

This is the best way to figure out how to make it work if you want to authenticate every request to protect static files, etc.

+2


source







All Articles