1

I'm developing an ASP.NET Core MVC web application. The application uses Windows Authentication. I want to send notifications to the client side from an API controller once it receives a post request. I found this resource https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-5.0) that says I need to use a CustomUserIdProvider to get the Id of tat specific user. However, I'm not sure how should I call the following method:

_myHub.Clients.User(specificUserId).SendAsync("ReceiveMessage", message);

How can I get that specificUserId in my API controller. I tried injecting the CustomUserIdProvider and then calling this method:

_myHub.Clients.User(_customUserIdProvider.GetUserId(conn)).SendAsync("ReceiveMessage", message);

However I don't know how do I get the HubConnectionContext conn object that is required by the GetUserId method from this API controller.

This is the CustomUserIdProvider class:

public class CustomUserIdProvider : IUserIdProvider
{
    public string GetUserId(HubConnectionContext connection)
    {
        return connection.User?.Identity?.Name;
    }
}

In case this doesn't work, is there any other way in which one can send SignalR notifications to specific users authenticated with Windows Authentication to an ASP.NET MVC web application? Thanks!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
diego esteban
  • 55
  • 1
  • 8
  • Don't you store this ids (or get them from incoming post request)? I mean.. how could you now to whom exactly you will have to send this message after all? – Timur Umerov Jun 18 '21 at 17:33
  • I was thinking of sending the [Domain]\[Username] in the post request. Then if I can get all the connections Ids generated by the customUserIdProvider, I could map those usernames with their corresponding connectionId. But, how could I get those connection Ids in my API controller by using that customeUserIdProvider method? – diego esteban Jun 18 '21 at 17:45

2 Answers2

1

You are not supposed to pass the instance of the CustomUserIdProvider to the hub. The IUserIdProvider defines what happens behind the scenes on how the SignalR returns the userId and identifies the users.

You register your CustomUserIdProvider

services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();

The point of this is to match the user id you will provide to the user id it looks for in its list of users.

Then if you are trying to do this in a controller and not a hub I assume you can do:

public Controller(IHttpContextAccessor httpContextAccessor)
{
   _userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
}

As seen in this answer: https://stackoverflow.com/a/36641907/2490286

and then

_myHub.Clients.User(_userId).SendAsync("ReceiveMessage", message);
misha130
  • 5,457
  • 2
  • 29
  • 51
  • Thank you so much @misha130! It worked just perfectly fine!. I just needed to change the ClaimTypes.NameIdentifier to ClaimTypes.Name since the first one returned null. Then I changed _userId to message.username within the _myHub.Clients.User(message.username) method as suggested by @Timur Umerov. – diego esteban Jun 18 '21 at 19:52
1

You don't need the actual connection. If you are sending username with post request, just take that username and pass it here

_myHub.Clients.User(Username).SendAsync("ReceiveMessage", message);

or if it is contained inside a message object

_myHub.Clients.User(message.Username).SendAsync("ReceiveMessage", message);

UPDATE

If the format of the string you are getting with connection.User?.Identity?.Name in your CustomUserIdProvider is [Domain][Username], then you should be fine passing that through Post request and then using it in User() method.

Timur Umerov
  • 473
  • 3
  • 8
  • Thank you! Your solution works but just after using the mapping generated by _userId = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value; as was suggested by @misha130. – diego esteban Jun 18 '21 at 19:54