'SignalR Hub in a Blazor Server App with Windows Authentication on Windows 2019 IIS 10 not working

What i've done:

  • blazor server app template with VS 2022 + .Net6.0 with Windows Authentication
  • add a simple Hub
  • host the application on Windows Server 2019 + IIS 10

(btw all is working when Hosting on Windows2012R2 + IIS8.5)

What is working:

  • all on my local IIS Express ... of course :)
  • the blazor server app pages
  • the Hub works as long as the clients are eg a TestConsole App or another Web App on another Server

What is not working:

  • the HubConnectionBuilder as far as i try to connect to it from the blazor app itself

I also open an (Issue on GitHub) , but i hope i got some more hints here.

The latest thing i tried was to create a new blazor server app without Windows Authentication just to be sure that my "client code" and HubUrl for the HubConnectionBuilder is right. And yes, with no Windows Authentication all is working as expected...

Here is my code:

TestHub.cs

using Microsoft.AspNetCore.SignalR;

namespace BlazorServerTestHub.Hubs
{
    public class TestHub :Hub
    {
        public async Task SendSomeMessage(int currentCount, string someMessage)
        {
            await Clients.Others.SendAsync("OnSendMyMessage", currentCount, someMessage);
        }
    }
}

Programm.cs

using BlazorServerTestHub.Data;
using BlazorServerTestHub.Hubs;
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
   .AddNegotiate();

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(options =>
{
    options.DetailedErrors = true;
    options.DisconnectedCircuitMaxRetained = 100;
    options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(3);
    options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(1);
    options.MaxBufferedUnacknowledgedRenderBatches = 10;
}).AddHubOptions(options =>
{
    //https://docs.microsoft.com/en-us/aspnet/core/signalr/configuration?view=aspnetcore-6.0&tabs=dotnet#configure-server-options-1
    options.EnableDetailedErrors = true;
    options.ClientTimeoutInterval = TimeSpan.FromSeconds(60);
    options.HandshakeTimeout = TimeSpan.FromSeconds(15);
    options.KeepAliveInterval = TimeSpan.FromSeconds(15);
    options.MaximumParallelInvocationsPerClient = 1;
    options.MaximumReceiveMessageSize = 32 * 1024;
    options.StreamBufferCapacity = 10;
});
builder.Services.AddSingleton<WeatherForecastService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseWebSockets();
app.UseAuthentication();
app.UseAuthorization();


app.MapBlazorHub();
app.MapHub<TestHub>("/mytesthub");
app.MapFallbackToPage("/_Host");

app.Run();

Counter.razor

@page "/counter"
@using Microsoft.AspNetCore.SignalR.Client

@inject NavigationManager navigationManager;

@implements IAsyncDisposable;

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

<p>@MyMessage</p>


@code {
    private int currentCount = 0;
    private HubConnection hubConnection = null!;
    public string MyMessage { get; set; } = null!;

    private async void IncrementCount()
    {
        currentCount++;

        await hubConnection.SendAsync("SendSomeMessage", currentCount, DateTime.Now.ToLongTimeString());
    }

    protected override async Task OnInitializedAsync()
    {
        var hubUrl = navigationManager.BaseUri + "mytesthub";


        hubConnection = new HubConnectionBuilder()
          .WithUrl(hubUrl, options =>
          {
              options.UseDefaultCredentials = true;
              options.SkipNegotiation = true;
              options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
          })
          .WithAutomaticReconnect()
          .Build();

        hubConnection.On<int, string>("OnSendMyMessage", async (counter, msg) =>
        {
            MyMessage = $"Counter: {counter}! {msg}";
            await InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();


    }

    public async ValueTask DisposeAsync()
    {
        if (hubConnection is not null)
        {
            await hubConnection.DisposeAsync().ConfigureAwait(false);
        }
        hubConnection = null!;
    }
}

this is the error message i got:

The server returned status code '401' when status code '101' was expected



Solution 1:[1]

i found a solution, but not really happy with it. maybe someone has a better way.

if i set the following registry KEY on the Windows 2019 Server:

Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\DisableLoopbackCheck = 1

then my app is working like expected.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 blindmeis