Reputation: 51
I use JWT Bearer tokens for my application - simple chat on my site. Chat users can be logged in and not logged in. To use chat, you can be logged or not. (Logged in users can receive private messages and so on).
This means that there may be users in my hub with or without an account (AspNetUser) in the system.
I correctly identify users using Context.Users
. SignalR connects connectionId to userId (and other logged in user information). To make it happen, I decided to insert an Authorization
filter. When I don't include this filter, the hub sees all users as anonymous.
I would like my hub was able to service all connections (logged in or anonymous users). Currently, due to the [Authorize]
attribute, only logged in users can use my hub. Anonymous users receive a 401 error.
The below method should explain what I want to achieve:
[Authorize(AuthenticationSchemes = OAuthIntrospectionDefaults.AuthenticationScheme)]
public class NotificationHub : Hub
{
public override Task OnConnectedAsync()
{
string name = Context.User.Identity.Name;
if (!string.IsNullOrEmpty(Context.User.Identity.Name))
{
Console.WriteLine($"New Connection! It's a registered user: {name}!");
}
else
{
Console.WriteLine($"New Connection! It's an anonymous user!");
}
return base.OnConnectedAsync();
}
}
Why do I need it? All users receive the same public messages. However, a private message can be sent to logged in users. I do it as follows (and only appropriate user receive this message):
Clients.User("userId").SendAsync("Message", "Something");
Is it possible for my hub to treat users like:
Upvotes: 5
Views: 2409
Reputation: 488
In relation to your code, I would suggest you change:
if (!string.IsNullOrEmpty(Context.User.Identity.Name))
To:
if (Context.User.Identity.IsAuthenticated)
The MSDN documentation does not make it obvious (at a glance) but 'SignalR' has it's own (same-name) 'Authorize' attribute; so if you derive from it, you can override the following method (within your custom/derived version - e.g. "SignalRAuthorizeAttribute"):
public override bool AuthorizeHubMethodInvocation(
IHubIncomingInvokerContext hubIncomingInvokerContext,
bool appliesToMethod)
{
if (hubIncomingInvokerContext.MethodDescriptor.Attributes.OfType<SignalRAllowAnonymousAttribute>().Any() ||
hubIncomingInvokerContext.MethodDescriptor.Hub.HubType.GetCustomAttributes<SignalRAllowAnonymousAttribute>().Any())
{
return true;
}
return
base.AuthorizeHubMethodInvocation(
hubIncomingInvokerContext,
appliesToMethod);
}
And therefore you can have your hubs closed by default (so that the exceptional anonymous one/s have to be actively whitelisted with a custom 'SignalRAllowAnonymousAttribute' attribute):
// GlobalHost.HubPipeline.RequireAuthentication();
var authorizeAttribute =
new SignalRAuthorizeAttribute();
GlobalHost.HubPipeline.AddModule(
new AuthorizeModule(
globalConnectionAuthorizer: authorizeAttribute,
globalInvocationAuthorizer: authorizeAttribute));
// Marker class
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class SignalRAllowAnonymousAttribute : Attribute
{
}
Upvotes: 0
Reputation: 1235
You just have to add [AllowAnonymous] to your hub.
This code is working for me with ASP.NET Core SignalR:
[AllowAnonymous]
[Authorize(AuthenticationSchemes = "Bearer")]
public class ChatHub : Hub
{
Upvotes: 3