0

I am trying to use the new User Id provider specified in signalr 2 to send messages to a specific user. When I call the Clients.All method, I see this working as my javascript code gets called from the server and the ui produces some expected text for my test case. However, when I switch to Clients.User the client side code is never called from the server. I followed the code outlined in this example: SignalR - Sending a message to a specific user using (IUserIdProvider) *NEW 2.0.0*.

NotificationHub.cs:

public class NotificationHub : Hub
{
    [Authorize]
    public void NotifyUser(string userId, int message)
    {
        Clients.User(userId).DispatchMessage(message);
    }

    public override Task OnConnected()
    {
        return base.OnConnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        return base.OnDisconnected(stopCalled);
    }

    public override Task OnReconnected()
    {
        return base.OnReconnected();
    }
}

IUserIdProvider.cs:

public class UserIdProvider : IUserIdProvider
{
    MemberService _memberService;
    public UserIdProvider()
    {
    }

    public string GetUserId(IRequest request)
    {
        long UserId = 0;

        if (request.User != null && request.User.Identity != null &&
            request.User.Identity.Name != null)
        {
            var currenUser = Task.Run(() => _memberService.FindByUserName(request.User.Identity.Name)).Result;
            UserId = currenUser.UserId;
        }

        return UserId.ToString();
    }
}

Startup.cs

    HttpConfiguration config = GlobalConfiguration.Configuration;

        config.Routes.MapHttpRoute(
            "Default2",
            "api/{controller}/{action}/{id}",
            new { id = RouteParameter.Optional });

        config.Routes.MapHttpRoute(
            "DefaultApi2",
            "api/{controller}/{id}",
            new { id = RouteParameter.Optional });

        app.Map("/signalr", map =>
        {
            map.UseCors(CorsOptions.AllowAll);

            var idProvider = new UserIdProvider();

            GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider); 

            map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
            {
                Provider = new QueryStringOAuthBearerAuthenticationProvider()
            });

            var hubConfiguration = new HubConfiguration
            {
            };
            map.RunSignalR(hubConfiguration);
        });

        app.MapSignalR();

QuerstringOAuthBearerAuthenticationProvider:

public class QueryStringOAuthBearerAuthenticationProvider
: OAuthBearerAuthenticationProvider
{
    public override Task RequestToken(OAuthRequestTokenContext context)
    {
        if (context == null) throw new ArgumentNullException("context");

        // try to find bearer token in a cookie 
        // (by default OAuthBearerAuthenticationHandler 
        // only checks Authorization header)

        var tokenCookie = context.OwinContext.Request.Cookies["BearerToken"];
        if (!string.IsNullOrEmpty(tokenCookie))
            context.Token = tokenCookie;
        return Task.FromResult<object>(null);
    }

}

Do I need to map the user to the connections myself using the IUserIdProvider through the OnConnected, OnDisconnected, etc. or does this happen automatically behind the scenes? Is there someone wrong in my posted code that could be a problem as well? I am running signalr from the same environment as my web api rest services, don't know if this makes a difference and using the default bearer token setup web api is using.

Community
  • 1
  • 1
user1790300
  • 2,143
  • 10
  • 54
  • 123

1 Answers1

0

It would be far easier for you to create a group based on the connectionid of the connecting client, in the onConnected event and broadcast to the group that matches the connected id, that way if the client disconnects, when they reconnect they would simply belong to a new group the themselves. Unless of course you are required to have an authenticated user.

Kelso Sharp
  • 972
  • 8
  • 12
  • Yes, I am required to have authenticated users and am currently using authentication tokens passed in cookies from the signalr connection and a custom provider to accept the authentication token along with the IUserProvider. – user1790300 Jul 26 '16 at 22:17
  • Also, I do need to expand this to other capabilities like determine who's online, friend to friend communication, etc. I am a like confused about how to accurately determine who is online since I am using authentication tokens. Seems like there would be a disconnect there if someone just closes their browser without logging out. – user1790300 Jul 26 '16 at 22:44
  • Have you looked at the tutorial for adding authentication to signalr apps? http://www.asp.net/signalr/overview/security/hub-authorization – Kelso Sharp Jul 27 '16 at 18:30