Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I access the user's Claims in Identity 2 when using SignalR Hub with an ASP.NET MVC 5 project?

I have an application that is written using c# on the top of Asp.Net MVC 5 Framework.

I am using SignalR 2.2.2 to create WebSocket communication between the browser and the server to push messages from the server to the browser.

However, I need to be able to access my ClaimsIdentity object for the logged in user so I can determine what messages to podcast.

Typically, I would access the identity claims like so

IPrincipal user = System.Web.HttpContext.Current.User
IIdentity identity = user.Identity;
var claims = (IEnumerable<Claim>)identity.Claims;

However, this line System.Web.HttpContext.Current returns null; preventing me from obtaining the currently logged in user.

I am guessing that SignalR create a synchronous connection which is why System.Web.HttpContext.Current is null.

I also try to use the HubCallerContex as suggested by this SO Question but the Context object is also null.

System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();

How can I correctly access the user claims with in my Hub?

I added the following key to my appSettings in my Web.config as I am using Framework 4.5.1

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
like image 451
Junior Avatar asked Oct 28 '22 23:10

Junior


1 Answers

If using SignalR hubs and want to authorize the methods under the same Hub one would have to use the Authorize attribute on the Hub. i.e.

[Authorize]
public class MessagingHub: Hub
{
    public Task Send(string data)
    {
        return Clients.Caller.SendAsync("Send", "Data To Send");
    }
}

In order to access Claims or User Identity in the Hub method e.g Send above, then one would have the following:

[Authorize]
public class MessagingHub: Hub
{
   public Task Send(string data)
  {
     var identity = (ClaimsIdentity)Context.User.Identity;
     //one can access all member functions or properties of identity e.g, Claims, Name, IsAuthenticated... 
      return Clients.Caller.SendAsync("Send", "Data To Send");
  }
}

If using Json Web Tokens (JWT) or just token authentication as was the case for me then, from the client side this is what could be used to invoke the Hub's Send method.

NB: For my case the client is an Angular 6 app.

import { HubConnection } from "@aspnet/signalr";
import * as signalR from '@aspnet/signalr';
...
private _messagingHubConnection: HubConnection | undefined;
public async: any;
...
constructor(){}
...
 SendMessg(): void {
    if (this._messagingHubConnection) {
      this._messagingHubConnection.invoke('Send');
    }
  }
...

ngOnInit(){
    this._messagingHubConnection= new signalR.HubConnectionBuilder()
      .withUrl("messaging", { accessTokenFactory: () => "jwt_token" }) //have a logic that gets the current user's authentication token from the browser 
      .build();

    this._messagingHubConnection.start().then(() => {
        this.SendMessg();
      }).catch(err => console.error(err.toString()));

    if (this._messagingHubConnection) {
      this._messagingHubConnection.on('Send', (data: any) => {
        //data represents response/message received from Hub method'd return value.
      });

    }
}

NB: I am using .Net Core 2.1 so be sure to register the Hub. also this is with the assumption that signalR is already setup

For .Net Core, ensure that in your StartUp.cs you have;

services.AddSignalR(); // under ConfigureServices

and

app.UseWebSockets();
app.UseAuthentication();
app.UseSignalR(routes => {
    routes.MapHub<LoopyHub>("/messaging");
});

NB: From the issues on GitHub for SignalR I realized the order of the above is important just in case anyone is developing any issues. i.e. From my understanding the above order is correct.

Technically I have answered the question above but from a .NET Core & Angular context; I guess most if not all implementations follow the same approach.

like image 141
rey_coder Avatar answered Nov 15 '22 05:11

rey_coder