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"/>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With