I've been building a web service that, up to now, consisted entirely of ASP.NET Web API calls. I now need to add SignalR into the mix but am wondering how I can keep the security aspect consistent.
I'm currently using OAuth bearer tokens (with OWIN authentication), so my client will basically have an access_token that I'd normally simply add to the headers for any web API calls. However, how do I do the same (or similar) with SignalR? Would I authenticate when I create the connection or on each call? And, most importantly, how would I specify the access token in the first place?
Thanks.
When using the browser client, no additional configuration is needed. If the user is logged in to your app, the SignalR connection automatically inherits this authentication. Cookies are a browser-specific way to send access tokens, but non-browser clients can send them.
If your SignalR application transmits sensitive information between the client and server, use SSL for the transport.
(this is so late :D but maybe useful for someone) you can add accessTokenFactory to hubConnection in client side, and then check it in your backend(asp.net core 3.1).
from BackEnd you must override JwtBearerEvents and check access_token inside OnMessageReceived, also your hub endpoint path
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
// other options here ... //
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/ConnectionHub"))) // for me my hub endpoint is ConnectionHub
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
from client side, add your accessToken options (TypeScript):
public async startConnection(): Promise<void> {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl('https://localhost:5001/ConnectionHub', { // ConnectionHub is hub endpoint
accessTokenFactory: () => this.getAccessToken(),
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets
}).build();
await this.hubConnection.start();
// ....
}
getAccessToken(): string {
return 'your token' ; }
microsoft said: Individual hub methods can have the [Authorize] attribute applied as well
[Authorize]
public class ChatHub : Hub
{
public async Task Send(string message)
{
// ... send a message to all users ...
}
[Authorize("Administrators")]
public void BanUser(string userName)
{
// ... ban a user from the chat room (something only Administrators can do) ...
}
}
and finaly you can read and check claims or identity attributes inside any of hub's method:
public override async Task OnConnectedAsync()
{
// Get UserID. Assumed the user is logged before connecting to chat and userid is saved in session.
string userID = Context.User.Identity.Name;
// Get ChatHistory and call the client function. See below
await GetHistoryAsync(userID);
await base.OnConnectedAsync();
}
I have written a sample that has helped out a few people already with SignalR authentication. I hope that it may help you too.
https://github.com/louislewis2/AngularJSAuthentication
Barguast, sorry for the brevity of my response. Going through the link you might understand that there are too many moving parts for me to accurately write an explanation of the most important parts that you should pay attention too. The sample code contains three project, of which you will be concerned with only the .Api and the .Web projects.
Let me know if there is any part that you need further clarification or guidance on, and I will happily help you out as all the other people who have encountered a need for the same example, which is why I was asked to build the example out in the first place.
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