简体   繁体   中英

Blazor Server Signalr hub is missing user claims

I have already spent more than a month solving this problem to no avail.

The goal is to get user claims in a Signalr hub in a Blazor server app. I have uploaded a very simple app on my Github. The client can invoke a method on the hub by clicking a button, the hub method sends back a message with User.Identity.Name. However, since the user claims are null, I cannot get the name.

Here is the repository: https://github.com/aadurham/blazorserverauth

So many people are asking the same question here and elsewhere. Information/suggestions/solutions are scattered. It has proved to be impossible for me (probably for anybody at my level of programming) to bring together those pieces and make them work.

I suspect that it would not take much time for a good programmer to put the pieces together in the simple app above. I am sure many people, including myself, would benefit from it. I hope some good Samaritan can lend a hand. In that case, please make a pull request. I will keep the code there for others' use and will try to add later a manual. I will also come back and try to do the same with a similarly simple Blazor WASM app.

Many thanks in advance

From this repo https://github.com/BrianLParker/SignalRAuth

    protected override async Task OnInitializedAsync()
    {

        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"), options =>
            {
                options.AccessTokenProvider = async () =>
                {
                    var accessTokenResult = await AccessTokenProvider.RequestAccessToken();
                    accessTokenResult.TryGetToken(out var accessToken);
                    return accessToken.Value;
                };
            })
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            StateHasChanged();
        });

        await hubConnection.StartAsync();
    }

[Authorize]
public class ChatHub : Hub
{
    private readonly UserManager<ApplicationUser> userManager;

    public ChatHub(UserManager<ApplicationUser> userManager)
    {
        this.userManager = userManager;
    }

    public async Task SendMessage(string user, string message)
    {
        var userId = Context.UserIdentifier;
        var applicationUser = await userManager.FindByIdAsync(userId);

        await Clients.All.SendAsync("ReceiveMessage", applicationUser.Email, message);
    }
}

I have figured out a simple solution, not perfect but fine. My goal was to pass some user information to the hub so that I can register connectionid with userid (or whatever user info one likes). Available solutions, which I have failed to implement so far, suggest adding a token on connection. Instead, I am adding the user info to the header and extracting it on the hub as follows:

This is the client code:

@code{
    [CascadingParameter]
    private Task<AuthenticationState> _authstate { get; set; }


    private HubConnection connection;
    private AuthenticationState authstate { get; set; }
    private List<string> messages = new List<string>();



    protected override async Task OnInitializedAsync()
    {
        authstate = await _authstate;

        if (authstate != null)
        {
            var userid = authstate.User.Identity.Name;

            connection = new HubConnectionBuilder()
                .WithUrl(NavigationManager.ToAbsoluteUri("/myhub"), options => {
                    options.Headers.Add("USERID", userid);
                })
                .WithAutomaticReconnect()
                .Build();



            connection.On<string, string>("ReceiveMessage", ReceiveMessage);

            await connection.StartAsync();
        }
    }


    public void ReceiveMessage(string user, string msg)
    {
        messages.Add(user + " " + msg);
        StateHasChanged();
    }

    public async void InvokeSomething()
    {
        await connection.InvokeAsync("InvokeSomething");
    }
}

This is how I extract user info on the hub:

public async Task InvokeSomething()
{
    var httpCtx = Context.GetHttpContext();
    var name = httpCtx.Request.Headers["USERID"].ToString();
    await Clients.All.SendAsync("ReceiveMessage", name, " invoked this");
}`

It works. One also use the same in OnConnectedAsync.

Hope this helps to somebody. The updated code in in the github repo.

The Authorize attribute on the hub is crashing the app. So I don't think connection to the hub is authenticated (the microsoft docs claim we don't need to do anything else). I hope somebody with much better skills than me offers a better solution. In the meantime, this seems to be doing the job.

UPDATE

Solution found In order to enable authorization of the Hub class one must pass the security cookie created when the user was authenticated to Blazor Server SPA app. You should pass the cookie when the hub connection is created. This will allow you to access all the claims such as UserIdentofier and UserName through in the Hub through the Context object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM