I’m working on a WinForms application that needs to connect to an Azure SignalR Service hosted on a web app. I’m encountering a 401 Unauthorized
error when trying to establish the SignalR connection.
Here is the code I’m using to initialize the SignalR connection in my WinForms app:
private async void InitializeSignalRConnection()
{
await Task.Delay(5000);
try
{
string token = await GetSignalRAccessToken();
connection = new HubConnectionBuilder()
.WithUrl("https://your-signalr-endpoint/client/?hub=notifications", options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.WithAutomaticReconnect()
.Build();
await connection.StartAsync();
connection.On<TransactionData>("SendMessageToClients", async (transactionData) =>
{
MessageBox.Show($"Transaction Received in WinForms: {transactionData.TransactionId}");
await connection.InvokeAsync("AcknowledgeTransaction", transactionData.TransactionId, "Received and Processed by WinForms");
});
Console.WriteLine("SignalR connection started.");
}
catch (Exception ex)
{
MessageBox.Show($"Error initializing SignalR connection: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Here is the relevant configuration from my web app:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddCors(options => options.AddPolicy("testing", policy =>
{
policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
}));
string azureSignalRConnectionString = builder.Configuration.GetValue<string>("AzureSignalRConnectionString");
builder.Services.AddSignalR().AddAzureSignalR(azureSignalRConnectionString);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("testing");
app.UseAuthorization();
app.MapHub<NotificationHub>("/notifications");
app.MapControllers();
app.MapRazorPages();
app.Run();
What I’ve Tried:
Verified the SignalR connection URL.
Checked CORS policy settings.
Confirmed that the token is correctly generated and passed.
Expected Result:
Successful connection to the SignalR hub.
Actual Result:
401 Unauthorized error.
Any help on what might be causing this issue would be greatly appreciated.
2
You are passing an access token, but the server-side configuration doesn’t show any JWT or token validation setup in the above.
Add JWT Authentication to the Server
Program.cs:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add Razor Pages and other services
builder.Services.AddRazorPages();
builder.Services.AddCors(options => options.AddPolicy("testing", policy =>
{
policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
}));
// Add JWT Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "your-issuer-url", // Replace with your issuer URL
ValidAudience = "your-audience", // Replace with your audience
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-signing-key")) // Your signing key
};
// Allow SignalR access token to be passed as a query string
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
// If the request is for the SignalR hub
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/notifications"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
// Add SignalR and use Azure SignalR Service
string azureSignalRConnectionString = builder.Configuration.GetValue<string>("AzureSignalRConnectionString");
builder.Services.AddSignalR().AddAzureSignalR(azureSignalRConnectionString);
builder.Services.AddControllers();
// Build the app
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("testing");
app.UseAuthentication(); // Make sure to include this line for authentication
app.UseAuthorization();
app.MapHub<NotificationHub>("/notifications");
app.MapControllers();
app.MapRazorPages();
app.Run();
NOTE: Check that the token audience (aud
) claim matches the SignalR endpoint URL.
On Client-Side add logging like below:
connection = new HubConnectionBuilder()
.WithUrl("https://your-signalr-endpoint/client/?hub=notifications", options =>
{
options.AccessTokenProvider = () => Task.FromResult(token);
})
.WithAutomaticReconnect()
.ConfigureLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Debug); // Set to debug for detailed logs
logging.AddConsole();
})
.Build();
Token Generated:
Successful Connection:
[DEBUG] - Generated Token: eyJodHRwOiJ... (a long JWT token)
[DEBUG] - Token Claims:
iss: https://issuer-url/
aud: https://signalr-endpoint/client/?hub=notifications
exp: 1694997200
sub: [email protected]
[DEBUG] - Attempting to establish SignalR connection to: https://your-signalr-endpoint/client/?hub=notifications
[DEBUG] - Connection state: Connecting
[INFO] - SignalR connection started successfully.
[DEBUG] - Transaction Received in WinForms: TX12345
[DEBUG] - Acknowledging transaction TX12345: "Received and Processed by WinForms"