Open bmoteria opened 4 years ago
@bmoteria thanks for contacting us.
Are you trying to start a blazor server application cross-origin?
@javiercn No. The blazor server app and signalr hub are hosted on the same site. Here is the sample project.
Thanks for contacting us.
We're moving this issue to the Next sprint planning
milestone for future evaluation / consideration. We will evaluate the request when we are planning the work for the next milestone. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
@captainsafia Is there any workaround for this issue? We're writing an enterprise application and we need to determine if could proceed further or find an alternative path. Thanks! (cc @BrennanConroy)
Hi @bmoteria -- not sure yet! We'll be investigating this issue soon and will post back about whether this a bug/known issue/fixable in your app/etc.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
This is a blocking issue for me too. The only way I got rid of the 401 was to allow Anonymous Authentication on IIS on a QA environment but that is not an option for Production.
I also get a 401, but this is on Azure App Services using Azure AD. It works using the subdomain.azurewebsites.net domain but when I access the site from our custom configured domain I get a 401. I have added CORS to allow all origins but still no luck. Is there any workaround for this issue?
I am facing this exact same issue, in an angularjs client. I can get passed it by using websockets as transport only, and then setting skipNegotiation to false. Then it works for obvious reasons.
I am facing this exact same issue, in an angularjs client. I can get passed it by using websockets as transport only, and then setting skipNegotiation to false. Then it works for obvious reasons.
@raltrifork, thanks for the tip. Does this makes sense?
_hubConnection = new HubConnectionBuilder()
.WithUrl(_hubUrl, options =>
{
options.UseDefaultCredentials = true;
options.SkipNegotiation = false;
options.Transports = Microsoft.AspNetCore.Http.Connections.HttpTransportType.WebSockets;
})
.WithAutomaticReconnect()
.Build();
Still no luck. Sticky 401! :-( I also tried some other options ... same result!
We found a temporary workaround. Instead of using signalr, we now use Scoped service (for example TaskCountService.cs
). The code inside task count service looks like so:
public event Action OnReloadTaskCount;
public void ReloadTaskCount()
{
OnReloadTaskCount?.Invoke();
}
Subscribe to OnReloadTaskCount
event inside navbar component (TopNavbar.razor
):
public partial class TopNavbar
{
[Inject]
protected TaskService TaskService { get; set; }
[Inject]
protected TaskCountService TaskCountService { get; set; }
protected overriede void OnInitialized()
{
TaskCountService.OnReloadTaskCount += ReloadTaskCount;
}
private void ReloadTaskCount()
{
InvokeAsync(() => {
_assignedToMeCount = TaskService.GetAssignedToMeCount(_currentUserIdentityName);
StateHasChanged();
});
}
}
Now that it's subscribed to an event, you can invoke ReloadTaskCount
method of TaskCountService.cs
from any razor pages/components:
*****Create.razor.cs*****
public partial class Create
{
[Inject]
protected TaskCountService TaskCountService { get; set; }
// other code goes here...
public async Task SubmitValidFormAsync()
{
// calling database repository.
// and other stuff..
TaskCountService.ReloadTaskCount();
}
}
Jesus! My mistake I meant setting SkipNegotiation to true, if this is set it should at least not be on the negotiation request the exception is thrown, since it is not performed.
Just another follow up to say I found another way to make it work (temporarily, until it's solved by the SignalR/Core/Blazor team) and it's by moving the Hub to a separate project, publish it on a separate IIS application and with anonymous authentication enabled. Had to move also the WEB API Controller that interacts with the hub and, for the moment, it also has to remain with anonymous authentication. This way I've been able to remove the anonymous authentication from the main project. So far it's working fine, not graceful, just fine.
I have the same problem. Are you going to fix this?
I was able to resolve this issue by simply setting the UseDefaultCredentials configuration option to true:
this.hubConnection = new HubConnectionBuilder()
.WithUrl(
hubUrl,
config => config.UseDefaultCredentials = true)
.WithAutomaticReconnect()
.Build();
My workaround for this issue is to catch the SignalR update with JavaScript and invoke a Blazor method.
JavaScript code:
const connection = new signalR.HubConnectionBuilder().withUrl("/myhub").build();
window.blazorSignalR = {
init: function (dotnetHelper, bindTo, nameFunc) {
connection.on(bindTo,
function (someArg) {
return dotnetHelper.invokeMethodAsync(nameFunc, someArg);
});
}
}
Component code:
private DotNetObjectReference<myComponent> dotNetRef;
private string EventName = "SomethingHappened";
protected override async Task OnInitializedAsync()
{
dotNetRef = DotNetObjectReference.Create(this);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("blazorSignalR.init", dotNetRef, EventName, "OnSomethingHappened");
}
}
[JSInvokable("OnSomethingHappened")]
public async Task OnSomethingHappened(string data)
{
// React to SomethingHappened
}
SignalR:
await myHub.Clients.All.SendAsync("SomethingHappened", "data");
I was able to resolve this issue by simply setting the UseDefaultCredentials configuration option to true:
this.hubConnection = new HubConnectionBuilder() .WithUrl( hubUrl, config => config.UseDefaultCredentials = true) .WithAutomaticReconnect() .Build();
Thank you so much, that did the trick!
Addendum: No, only when running locally, but when published to IIS I get the error 401 (unauthorized) again.
Any updates on that?
Any updates? just work local but no on real IIS
We're also waiting for an update on this.
Almost 2 years and this is still awaiting a response? @captainsafia Any updates?
Any update on this ? We need this feature as most of our apps uses Windows Authentication and not able to use Blazor Server App with SignalR.
@darrylluther Thanks for the trick, works locally, but not working with IIS after deployment.
Any update on this thread. This still does not work for me as i still get 401 Unauthorized when using Windows Authentication
Same error here. Waiting for updates
Ran into the same problem. Works fine locally, 401 when deployed to iis.
To learn more about what this message means, what to expect next, and how this issue will be handled you can read our Triage Process document. We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. Because it's not immediately obvious what is causing this behavior, we would like to keep this around to collect more feedback, which can later help us determine how to handle this. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact work.
After further discussion we believe this is not specific to Blazor by any means and is rather something that should be addressed on the SignalR side. @BrennanConroy moving to your area path.
After further discussion
Can you share any details about this discussion...
Update on our side, we managed to get it to work, but not through using SignalR, we had to revert to using long polling instead. When connecting using HubConnectionBuilder, configure the transport to use websockets, then longpolling. Do the same for MapHub in Program.cs. This is not good for production scenarios, but fine for us with <10 users.
Hope this helps anyone else.
Are people in this thread trying to do impersonation? Or are you just trying to authenticate with the application's identity?
If you're trying to authenticate with the apps identity, setting UseDefaultCredentials as suggested in https://github.com/dotnet/aspnetcore/issues/25000#issuecomment-795685660 should work. I'm guessing all the thumbs up means this has worked for a bunch of people.
If you're trying to do impersonation, take a look at the impersonation docs at https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-8.0&tabs=visual-studio#impersonation
@halter73
Only works locally. Not when the app is deployed to IIS.
That didn't answer my question, but I'm going to assume that means you're not trying to do impersonation. In that case, it probably means that app pool identity does not have permission to access the SignalR server. This would make sense without some custom configuration considering that the default virtual account created by IIS is not joined to any domain.
On IIS 8.0 or later, the IIS Admin Worker Process (WAS) creates a virtual account with the name of the new app pool and runs the app pool's worker processes under this account by default.
If you're trying to authenticate with the apps identity, setting UseDefaultCredentials as suggested in #25000 (comment) should work. I'm guessing all the thumbs up means this has worked for a bunch of people.
If you're trying to do impersonation, take a look at the impersonation docs at https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-8.0&tabs=visual-studio#impersonation
To be clearer here I'll give code examples and screenshots. It seems most people are writing code like:
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<h2>@name</h2>
@code
{
private string name;
private int currentCount = 0;
private HubConnection? hubConnection;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/hub"), config =>
{
config.UseDefaultCredentials = true;
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string>("name", userName =>
{
name = userName;
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
}
private void IncrementCount()
{
currentCount++;
}
}
And this works for IISExpress because IISExpress is running as the logged in user. Which during development is likely your personal/work account. But when you publish the app to IIS, the app is now running under the Application Pool Identity
. This means the HubConnection
isn't going to connect as the user that is accessing the page, but as the "user" hosting the app.
What most people probably want is for the HubConnection
to connect as the user that is accessing the application. This is where impersonation comes in. You need to tell the application to run the HubConnection
code as the browsing user.
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<h2>@name</h2>
@code {
private int currentCount = 0;
private HubConnection? hubConnection;
private string name;
protected override async Task OnInitializedAsync()
{
var user = (WindowsIdentity)(await AuthenticationStateProvider.GetAuthenticationStateAsync()).User.Identity;
await WindowsIdentity.RunImpersonatedAsync(user.AccessToken, async () =>
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/hub"), config =>
{
config.UseDefaultCredentials = true;
})
.WithAutomaticReconnect()
.Build();
hubConnection.On<string>("name", userName =>
{
name = userName;
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
}
);
}
private void IncrementCount()
{
currentCount++;
}
}
@BrennanConroy do you think this should be documented or is there any other pending work here? We still are somewhat unsure how this related to Blazor Server.
do you think this should be documented or is there any other pending work here?
Yes, this is clearly a Blazor Server coding pattern issue that people don't understand. We should document it, or if you're feeling crazy, Blazor Server can wrap component code in WindowsIdentity.RunImpersonatedAsync(...)
when Windows Auth is being used so users don't have to think about it when writing their components.
to all those using NginX, this is the X/Y problem - the issue lies within your nginx configuration! search for the line statring with proxy_set_header Connection
and replace it with proxy_set_header Connection "upgrade";
(see this StackOverflow answer) - for me i had to replace proxy_set_header Connection keep-alive;
with the above.
`@using Microsoft.AspNetCore.Components.Authorization @inject AuthenticationStateProvider AuthenticationStateProvider @using System.Security.Principal
@using Microsoft.AspNetCore.SignalR.Client @using BlazorBootstrap
@inject NavigationManager Navigation @implements IAsyncDisposable
@code { private HubConnection? _hubConnection; private int progress; private string test;
protected override async Task OnInitializedAsync()
{
var user = (WindowsIdentity)(await AuthenticationStateProvider.GetAuthenticationStateAsync()).User.Identity;
test = user.Name;
await WindowsIdentity.RunImpersonatedAsync(user.AccessToken, async () =>
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub"), config =>
{
config.UseDefaultCredentials = true;
})
.WithAutomaticReconnect()
.Build();
_hubConnection.On<int>("ReceiveProgress", p =>
{
progress = p;
InvokeAsync(StateHasChanged);
});
await _hubConnection.StartAsync();
}
);
}
public async ValueTask DisposeAsync()
{
if (_hubConnection != null)
{
await _hubConnection.DisposeAsync();
}
}
}`
still fails on the server despite having a valid username
System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized). at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.NegotiateAsync(Uri url, HttpClient httpClient, ILogger logger, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.SelectAndStartTransport(TransferFormat transferFormat, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsyncCore(TransferFormat transferFormat, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsync(TransferFormat transferFormat, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken) at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncInner(CancellationToken cancellationToken) at Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(CancellationToken cancellationToken)
works on local but but not iis server with windows auth enabled and websockets enabled
Local host content
@BrennanConroy
@QuinnNash
If you want to fix the 401 error code you need to have trusted SSL certificate for hosted application.
When you are trying to deploy app on IIS be sure that you created valid SSL certificate for the host (IIS Development Certificate or any created by Visual Studio or by you with PowerShell) - so if you deploy app with binding for https as "localhost" create certificate for localhost, if you deploy as "app.test" create certificate for "app.test", same for IP address.
Next you have to copy or move created certificate to Trusted Root Certification Authorities (you can do it in certmgr.msc).
I had the same issue and that helped me in development environment.
My Blazor program is running on an rpi with Lets Encrypt SSL security (hosted on https:// with a lock).
When page is accessed without refresh, console reports only the following error Failed to load resource: net::ERR_BLOCKED_BY_CLIENT
- and works fine.
When i refresh the page, i get a .NET error with 123:1 GET https://{my site} 500 (Internal Server Error)
error in the console.
Describe the bug
I have a Blazor Server app that uses Windows Authentication. It requires SignalR hub connection to update partial UI when a user click a button. I have followed Use ASP.NET Core SignalR with Blazor WebAssembly documentation as well read #22767 and tried following SignalR cross-origin negotiation for authentication. The following code works when debugging locally on IIS Express using Visual Studio 2019 Enterprise but it returns 401 Unauthorized exception while starting hub connection when it's deployed to IIS, especially when it has subdomain binding (http://subdomain.mydomain.org). It only works when navigating via server name and port number "http://servername:60006". Please note that the signalr hub are hosted along with Blazor server app so there is no cross origin.
NavMenu.razor
IncludeRequestCredentialsMessageHandler.cs
To Reproduce
Exceptions (if any)
Further technical details
dotnet --info
Runtime Environment: OS Name: Windows OS Version: 10.0.18363 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\3.1.401\
Host (useful for support): Version: 3.1.7 Commit: fcfdef8d6b
.NET Core SDKs installed: 3.1.400 [C:\Program Files\dotnet\sdk] 3.1.401 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed: Microsoft.AspNetCore.All 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET Core runtimes or SDKs: https://aka.ms/dotnet-download