5

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"/>
Junior
  • 11,602
  • 27
  • 106
  • 212
  • Are you using SignalR hubs? There is [Context.User](https://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.hubs.hubcallercontext.user(v=vs.118).aspx) property, have you checked it? – Zabavsky Nov 17 '17 at 11:19
  • @Zabavsky the `Context` object itself id null. – Junior Nov 17 '17 at 15:36
  • Where are you trying to access the context from? – Pawel Nov 17 '17 at 16:58
  • From the Constructor. My constructor called the base() class – Junior Nov 17 '17 at 17:00
  • The constructor of what? The hub? – Pawel Nov 17 '17 at 23:04
  • I am asking because SignalR [first creates the hub and then populates the context](https://github.com/SignalR/SignalR/blob/7dc8921376743b73856cb1f7bb9ca1db8de1abe2/src/Microsoft.AspNet.SignalR.Core/Hubs/HubDispatcher.cs#L455-L464) so the context is initialized when the instance is created and therefore you cannot access it when in the ctor. – Pawel Nov 17 '17 at 23:12
  • Yes the constructor of the Hub class – Junior Nov 17 '17 at 23:12
  • 1
    There is documentation for this, here: https://learn.microsoft.com/en-us/aspnet/signalr/overview/security/hub-authorization. Have you correctly annotated your hub methods? – Brendan Green Nov 28 '17 at 23:27
  • @BrendanGreen yes I added it but still same issue – Junior Nov 29 '17 at 16:35
  • Show more of your code. Your hub definition, and the code that is making the calls. – Brendan Green Nov 29 '17 at 23:04

2 Answers2

3

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.

rey_coder
  • 422
  • 8
  • 12
0

All of you'll missing out one important thing.

Unlike controllers you wont be able to access the claims within constructor within Hub. Access the claims once its connected. like below.

 [Authorize]
    public class YourHub: Microsoft.AspNetCore.SignalR.Hub
    {

          public override async Task OnConnectedAsync()
          {
             ...
             var identity = (ClaimsIdentity)Context.User.Identity;
          }
  }
Navin
  • 684
  • 1
  • 11
  • 24