Reputation: 63
How does the client authorize to send a message to the user?
Sending from the controller
hubContext.Clients.User(User.Identity.Name).SendAsync();
At the moment the message is not sent. Do I need to add something in OnConnection ()? Or does SignalR have a ready-made mapping mechanism for ConnectionId and User.Identity.Name?
That's how I implemented it at the moment, but it seems to me not quite right. The question is how to make the same standard tools?
public static class HubConnections
{
public static Dictionary<string, List<string>> Users = new Dictionary<string, List<string>>();
public static List<string> GetUserId(string name)
{
return Users[name];
}
}
public class GameHub : Hub
{
public override Task OnConnectedAsync()
{
if (Context.User.Identity.IsAuthenticated
&& HubConnections.Users.ContainsKey(Context.User.Identity.Name)
&& !HubConnections.Users[Context.User.Identity.Name].Contains(Context.ConnectionId))
HubConnections.Users[Context.User.Identity.Name].Add(Context.ConnectionId);
else
HubConnections.Users.Add(Context.User.Identity.Name, new List<string> { Context.ConnectionId });
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
if (Context.User.Identity.IsAuthenticated) HubConnections.Users.Remove(Context.User.Identity.Name);
return base.OnDisconnectedAsync(exception);
}
}
As I said above, I tried just like this, and it does not work
hubContext.Clients.User(User.Identity.Name).SendAsync();
Upvotes: 6
Views: 6447
Reputation: 592
The claim that signalR is using to identify the user can be changed. It is important to ensure that this claim has unique values.
Documentation says to setup a custom UserIdProvider like this:
public class NameUserIdProvider : IUserIdProvider
{
public string GetUserId(HubConnectionContext connection)
{
return connection.User?.Identity?.Name;
}
}
Add then add it to services:
public void ConfigureServices(IServiceCollection services)
{
// ... other services ...
services.AddSignalR();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
}
The snippets are taken from official documentation: https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1#use-claims-to-customize-identity-handling
Upvotes: 1
Reputation: 86
Was chasing the same issue and got the solution from https://github.com/aspnet/SignalR/issues/2498
One needs to set the NameIdentifier claim. That is the one checked by SignalR instead of the Name claim which I assumed. I set the NameIdentifier claim and I got my non-hub class to send a notification to a specific user.
Upvotes: 3
Reputation: 8642
You are using the Users
as a store for the connection id. So, for each username, you can send the message to each of the client connections you have stored for that user. Something like this:
public void SendMessage(string username, object data)
{
var connections = HubConnections.Users[Context.User.Identity.Name];
foreach(var id in connections)
{
Clients.client(id).SendAsync("Foo", data);
}
}
Upvotes: 0
Reputation: 239250
There's a client-side component. You must reference the SignalR JS file, create a connection and then subscribe to a particular message from the server. Only then will sending that message actually do something.
<script src="~/lib/signalr/signalr.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/gameHub")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on("Foo", (data) => {
// do something
});
connection.start().catch(err => console.error(err.toString()));
</script>
The above will then cause the client to run the function defined for "Foo"
above whenever the server sends a "Foo"
message like:
hubContext.Clients.User(User.Identity.Name).SendAsync("Foo", data);
Upvotes: 0