SignalR makes real-time communication in .NET applications simple.
You can send live data, notifications, and streaming updates to clients with minimal code.
By default, SignalR hubs are accessible to all clients.
Clients can connect, call hub methods, and receive messages without authentication.
In production, you may need to know who is connecting to your hub, what they are allowed to do, and how to reject unauthorized access.
JWT authentication is the standard for securing SignalR hubs in any environment, including browsers.
It works with WebSockets, Server-Sent Events, and Long Polling transports.
In this post, we will explore:
- Why SignalR Needs Authentication
- Setting Up JWT Authentication for SignalR
- How to Stream Events with SignalR for a Given User
- Role-Based Authorization on Hub Methods
- Connecting from a JavaScript Client
- Connecting from a .NET Client
- Security Best Practices for SignalR
Let's dive in.
👉 Read original article on my newsletter: https://antondevtips.com/blog/how-to-add-jwt-authentication-to-signalr-hubs-in-aspnetcore
Why SignalR Needs Authentication
SignalR uses multiple transport protocols to maintain real-time connections: WebSockets, Server-Sent Events, and Long Polling.
By default, any client can connect to a SignalR hub and call its methods.
This creates several problems in production:
- No user identity - You can't tell who is connected or send messages to specific users.
- No access control - Anyone can call any hub method, including admin operations.
- No audit trail - You can't log which users performed which actions.
- Security exposure - Sensitive data could be streamed to unauthorized clients.
Why JWT and not Cookies?
Cookies work well for browser-based apps where users log in through a web form.
But JWT tokens are the better choice when:
- Your clients are mobile apps, desktop apps, or SPAs.
- You need cross-platform authentication.
- Your API and frontend run on different domains.
- You're building microservices that need stateless authentication.
There is one important detail about how tokens are handled in SignalR connections.
In standard REST APIs, the client sends the JWT token in the Authorization HTTP header.
But SignalR can't always do this.
When using WebSockets or Server-Sent Events in a browser, the browser API does not allow setting custom headers.
Instead, the token is sent as a query string parameter: ?access_token=<token>.
This means you need extra configuration on the server to read the token from the query string.
We will cover this setup in the next section.
For a deeper dive into authentication and authorization in ASP.NET Core, read my article on Authentication and Authorization Best Practices.
Setting Up JWT Authentication for SignalR
Let's build the backend for the stock price streaming application.
In Modern .NET versions, you no longer need to install the SignalR NuGet package.
SignalR is now included in the ASP.NET Core Web SDK.
Here is how you can configure SignalR in your project:
builder.Services.AddSignalR();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// Map the SignalR hub
app.MapHub<StockPriceHub>("/hubs/stocks");
We need to configure JWT bearer authentication and add the OnMessageReceived event handler that extracts the token from the query string for SignalR connections:
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authConfig.Key));
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = authConfig.Issuer,
ValidAudience = authConfig.Audience,
IssuerSigningKey = key
};
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("/hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
When a browser connects to a SignalR hub via WebSockets or SSE, it can't send the Authorization header.
The JavaScript client sends the token as ?access_token=<token>.
The OnMessageReceived event fires before the JWT middleware validates the token.
Token extraction is limited to paths starting with /hubs.
This avoids reading tokens from query strings on other endpoints.
👉 Read original article on my newsletter: https://antondevtips.com/blog/how-to-add-jwt-authentication-to-signalr-hubs-in-aspnetcore
Top comments (0)