Skip to content

NET 10.0.100-rc.1.25451.107: 400 Bad Requests on SignalR Hub /negotiate HTTP call when using RequireAuthorization() on MapHub #63928

@cyclorama

Description

@cyclorama

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

dot net version: NET 10.0.100-rc.1.25451.107
application: blazor (unified)

I'm currently getting 400 Bad Request errors when trying to connect to my SignalR hubs that have been setup with default authorization in my Program.cs. After I have applied the RequireAuthorization() method via the HubEndpointConventionBuilder when mapping my endpoints, I start to receive 400 Bad Request errors when calling StartAsync on my HubConnectionBuilder within my razor view.

I looked at Fiddler to see why the request had failed, and it said the following:
A valid antiforgery token was not provided with the request. Add an antiforgery token, or disable antiforgery validation for this endpoint.

Image

Why would you need an anti-forgery token for establishing a connection to a SignalR hub?

I attempted to disable the antiforgery token validation for my SignalR hubs, but no matter what I try I am greeted with the same error.

When I remove the RequireAuthorization() call in my Program.cs, I am able to connect with no issues. I'm confused as to how anti forgery tokens are tied to authentication flow for SignalR.

My Program.cs:

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using ogd.Components;
using ogd.Components.Account;
using ogd.Data;
using ogd.Data.Clients;
using ogd.Data.Hubs;
using ogd.Data.Repositories;
using ogd.Data.Repositories.Interfaces;
using ogd.Hubs;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents()
.AddAuthenticationStateSerialization();

builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped();
builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

builder.Services.AddDbContext(options =>
options.UseSqlServer(connectionString));

builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.Stores.SchemaVersion = IdentitySchemaVersions.Version3;
})
.AddEntityFrameworkStores()
.AddDefaultTokenProviders();

builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.SameSite = SameSiteMode.Lax; // important
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // HTTPS only
});

// Email sender (placeholder)
builder.Services.AddSingleton<IEmailSender, IdentityNoOpEmailSender>();

// Logging
builder.Logging.AddConsole();
builder.Logging.AddDebug();

builder.Services.AddHttpClient();

builder.Services.Configure(options =>
{
options.ValidationInterval = TimeSpan.FromSeconds(10); // consider implication of lots of users
});

builder.Services.AddScoped<IAlertsRepository, AlertsRepository>();
builder.Services.AddScoped<IForumThreadsRepository, ForumThreadsRepository>();
builder.Services.AddScoped<IUsersRepository, UsersRepository>();
builder.Services.AddScoped<IPlatformsRepository, PlatformsRepository>();

builder.Services.AddSingleton();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}

app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.UseAntiforgery();

// Map SignalR hubs first, and disable antiforgery just for those
var hubs = app.MapGroup("/hubs").DisableAntiforgery();

hubs.MapHub("/alertshub").RequireAuthorization();
hubs.MapHub("/forumthreadshub").RequireAuthorization();
hubs.MapHub("/usershub").RequireAuthorization();
hubs.MapHub("/irchub"); // no auth

app.MapStaticAssets();
app.MapRazorComponents()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(ogd.Client._Imports).Assembly);

app.MapAdditionalIdentityEndpoints();

// seed roles + admin user
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
await DatabaseSeeder.SeedIdentityAsync(services);
}

app.Run();

Expected Behavior

I should expect to get a 200 OK response back from the /negotiate HTTP call to my SignalR hub indicating a successfully established connection

Steps To Reproduce

Adding default authorization to SignalR hubs and then attempting to connect via StartAsync() within a razor view.

Exceptions (if any)

A valid antiforgery token was not provided with the request. Add an antiforgery token, or disable antiforgery validation for this endpoint.

.NET Version

NET 10.0.100-rc.1.25451.107

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-signalrIncludes: SignalR clients and servers

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions