Implementing a global notification system in blazor server with SignalR

Viewed 1433

I'm trying to implement a notification system that notifies specific users depending on their roles or permissions in Blazor Server Side .NET 5 with SignalR. I've already implemented Identity Authentication and Authorization.

In my MainLayout.razor page i've implemented a HubConnectionBuilder as follows:

private HubConnection _hubConnection;

protected override async Task OnInitializedAsync()
{
    //SignalR
    _hubConnection = new HubConnectionBuilder()
        .WithUrl(MyNavigationManager.ToAbsoluteUri("/hub"))
        .Build();

    _hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
    {
        Toaster.NotificacaoGlobal(user, message);
        StateHasChanged();
    });

    await _hubConnection.StartAsync();
}

I've also created a TestHub.cs where i have a few methods as follows:

public class TestHub : Hub
{
    public async Task SendMessage(string user, string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }

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

    public override Task OnDisconnectedAsync(Exception exception)
    {
        return base.OnDisconnectedAsync(exception);
    }
}

In Startup.cs i've added:

app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
                endpoints.MapHub<TestHub>("/hub");
            });

and

services.AddSignalR();

My ideia was to store the connectionId and UserId on the OnConnectedAsync() and then on the method await Clients.All.SendAsync("ReceiveMessage", user, message); filter the user with something like Clients.User(userId).SendAsync("ReceiveMessage", user, message);

I've tried to use both Context.UserIdentifier and Context.User.Identity.Name inside OnConnectedAsync() method and both return null. Some said to use the [Authorize] atribute in the TestHub.cs but that gives me an exception: HttpRequestException: Response status code does not indicate success: 401 (Unauthorized). on await _hubConnection.StartAsync()

Implementing IUserIdProvider did not resolve the problem as some suggested:

public class IdBasedUserIdProvider : IUserIdProvider
 {
      public string GetUserId(HubConnectionContext connection)
      {
           //TODO: Implement USERID Mapper Here
           //throw new NotImplementedException();

           return connection.User.FindFirst("sub").Value;
      }
 }


services.AddSingleton<IUserIdProvider, IdBasedUserIdProvider>();

I'm new to blazor and SignalR and might be something that I'm not aware.

Thanks in advance!

EDIT 1:

As asked i'm going to clarify the problem i'm having as best as I can.

What do I need? A system for sending push notifications to specific users.

What have I done? Tried to implement SignalR to send those notifications through SignalR

The struggles: After implementing the TestHub.cs class and the initializer on OnInitializedAsync() on my razor page I was unable to map the identity user with the connectionId in SignalR. What I need is some clarification, if possible, whether am I making a mistake implementing SignalR, if SignalR makes sense in this cenario or if is there any other tool or system that makes more sense.

I hope I clarified enough. Thanks.

1 Answers

I've tried to use both Context.UserIdentifier and Context.User.Identity.Name inside OnConnectedAsync() method and both return null.

create a custom IUserIdProvider

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

and add this in your programm.cs

builder.Services.AddSingleton<IUserIdProvider, UserNameProvider>();

then Context.UserIdentifier should not be null anymore

[Authorize]
public class ChatHub : Hub
{
    public async Task SendMessage(string message)
    {
        await Clients.All.SendAsync("ReceiveMessage", Context.UserIdentifier, message);
    }
}
Related