Search code examples
asp.net-coresignalrauthorize-attributeasp.net-core-signalrasp.net-core-2.1

How to make SignalR hubs that require authorization honor loggin in / logging out?


I have a simple chat implementation in SignalR. Everyone may read what others are typing, but only registered users may speak:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace WebApplication1.Hubs
{
    public class ChatHub : Hub
    {
        [Authorize]
        public async Task SendMessage(string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", Context.User.Identity.Name, message);
        }
    }
}

And it works. Except... When the user has the chat opened in one tab, but logs in / logs out in the other tab.

  • When the user logs out in the other tab, they may still freely speak in the chat. The chat sends their messages with their nick they had before they logged out. This persists until the chat tab is reloaded.
  • When the user is not logged in, but still opens chat in one tab, and then logs in in the other tab, they may still not speak in chat until they reload the chat tab.

Why, in spite of the presence of the [Authorize] attribute, does the SendMessage method still work even if the user is no longer authenticated?

How to fix this? How to make my chat hub follow authentications that happen after the client has connected?

ASP .NET Core 2.1 RC1, because 2.0 doesn't support SignalR.

EDIT: As per request from comments, Startup.Configure:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseAuthentication();

    app.UseSignalR(routes =>
    {
        routes.MapHub<ChatHub>("/chathub");
    });

    app.UseMvc();
}

Solution

  • In websocket credentials are only sent to establish the connexion and are not included in every frame. So once the connexion established there's no way for signalr to know that the user has logged off.

    Like you said when the user logs off you could open another socket to send a message to all tabs that the user has logged off.

    Or you could periodicaly check the cookies by javascript that to check the user is still log in and close the signalr connexion if he is not.

    Another solution would be to use Shared Worker or Broadcast channel to send message between tabs.