Sam Sam - 3 months ago 75
ASP.NET (C#) Question

How can I authorize a SignalR Hub using Azure Mobile Apps Authentication

I have my mobile app using Facebook Auth through azure. The auth works fine for the ApiControllers with the

[MobileApiController]
flag.

I can't seem to find how to make my SignalR Hub authorize - the Authorize attribute blocks access from users. All the articles I have found seem to be old and using the deprecated Azure Mobile Services which is different and not compatible.

I have configured my SignalR client to connect as long-polling with the x-zumo-auth header set.

Sam Sam
Answer

I ended up making a custom SignalR authentication attribute. Code below for anyone else interested.

  public class HubAuthorizeAttribute : AuthorizeAttribute
  {
    public HubAuthorizeAttribute()
    {
    }

    public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
    {
        var owinContext = request.GetHttpContext().GetOwinContext();

        ClaimsPrincipal claimsPrincipalFromToken;
        var options = Startup.AuthenticationOptions;

        string tokenFromHeader = request.Headers[AppServiceAuthenticationHandler.AuthenticationHeaderName];
        if (!string.IsNullOrEmpty(tokenFromHeader))
        {
            bool claimsAreValid = options.TokenHandler.TryValidateLoginToken(tokenFromHeader, options.SigningKey, options.ValidAudiences, options.ValidIssuers, out claimsPrincipalFromToken);
            if (claimsAreValid)
            {
                var identity = claimsPrincipalFromToken.Identity as ClaimsIdentity;

                request.Environment["server.User"] = new ClaimsPrincipal(identity);
                return true;
            }
        }

        return false;
    }

    public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
    {
        var connectionId = hubIncomingInvokerContext.Hub.Context.ConnectionId;
        // check the authenticated user principal from environment
        var environment = hubIncomingInvokerContext.Hub.Context.Request.Environment;
        var principal = environment["server.User"] as ClaimsPrincipal;
        if (principal != null && principal.Identity != null && principal.Identity.IsAuthenticated)
        {
            // create a new HubCallerContext instance with the principal generated from token
            // and replace the current context so that in hubs we can retrieve current user identity
            hubIncomingInvokerContext.Hub.Context = new HubCallerContext(new ServerRequest(environment), connectionId);
            return true;
        }
        else
        {
            return false;
        }
    }
}
Comments