Ready to post? First, try searching for your answer.
We have a Blazor Web App created in .net 9 that is set up to run using global InteractiveAuto render mode. Using Andrea Chiarelli’s excellent article “Add Auth0 Authentication to Blazor Web Apps” we got authentication working for the application itself in both InteractiveWebServer and InteractiveServer render modes.
Next, we added SignalR leveraging an instance of the Azure SignalR Service through the Web App. That too works as expected with the browser client receiving messages sent through SignalR in both render modes.
Finally, we enabled authentication for the SignalR connection. After this SignalR connections continue to work when the InteractiveWebAssembly render mode is in play but when InteractiveServer is in use an exception is thrown when the client calls “StartAsync” on the SignalR connection object. Injecting the AuthenticationStateService into the service that handles starting the connection shows there is an authenticated user but that authentication state is not flowing to the connection attempt. We are using the AddAuth0WebAppAuthentication method in the Web App startup (as outlined in the above mentioned article) and my suspicion is this is not sufficient to handle the SignalR connection attempt and thus the startup call to app.MapHub<{hubtype}>({route}).RequireAuthorization() is not playing well with the auth configuration setup by the AddAuth0WebAppAuthentication call.
I’ve experimented with using a custom CircuitHandler in the Web App but use of this also shows that authentication is flowing it just isn’t flowing during the SignalR connection attempt.
Any thoughts or suggestions you might have will be appreciated.
Certainly. The code can be found in the public github repo DaneVinson/jokey/tree/notifications. The exception (HttpRequestException) occurs in the server-side NotificationService (DaneVinson/jokey/blob/notifications/Jokey.WebApp/Services/NotificationService.cs). I have a try/catch wrapping StartAsync call for the HubConnection. This implementation of the NotificationService is only in play when the Blazor application is running in InteractiveServer render mode. I typically set the render mode in the Jokey.WebApp/Components/App.razor file to InteractiveServer while working on this issue (it is currently set as InteractiveAuto) to ensure the issue can be replicated. As I said when the render mode is InteractiveWebAssembly the SignalR auth flow is working as expected.
Let me know if you have any questions or if there is anything unclear.
The repo is 100% up to date and public. The links provided are relative to github root because this forum will no allow posting links. Simply prepend the links with the root github URI, e.g. add https://github_com/.
The code where the exception is occurring is pretty unhelpful. It’s simply the call to StartAsync on the HubConnection. The stack trace of the exception is similarly unhelpful as it is just what you’d expect when trying to make an unauthenticated call to an endpoint that requires authentication. But here it is:
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>d__45.MoveNext()
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.<GetNegotiationResponseAsync>d__52.MoveNext()
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.<SelectAndStartTransport>d__44.MoveNext()
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.<StartAsyncCore>d__41.MoveNext()
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.<StartAsync>d__40.MoveNext()
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.<ConnectAsync>d__3.MoveNext()
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.<ConnectAsync>d__3.MoveNext()
at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult()
at Microsoft.AspNetCore.SignalR.Client.HubConnection.<StartAsyncCore>d__62.MoveNext()
at Microsoft.AspNetCore.SignalR.Client.HubConnection.<StartAsyncInner>d__53.MoveNext()
at Microsoft.AspNetCore.SignalR.Client.HubConnection.<StartAsync>d__52.MoveNext()
at Jokey.WebApp.Services.NotificationService.<StartAsync>d__6.MoveNext() in C:\Development\repos\GitHub\jokey\Jokey.WebApp\Services\NotificationService.cs:line 35
Based on the information you provided it seems like the problem might be the fact that all of the code is running on the server side when the cookies are stored on the browser. This Github issue should be of help:
If you have any other questions feel free to let us know.
Yes, I have seen that post and experimented with it in several ways.
If I inject and IHttpContextAccessor into the service I currently use to connect to the SignalR hub I find that the service gets instantiated two separate times. The first time the HttpContext property on the injected IHttpContextAccessor is valid and there are credentials. The StartAsync method then runs correctly and a connection to SignalR is established. Then the service gets instantiated a second time. The second time the HttpContext is null and, obviously, there are no credentials to the StartAsync goes back to throwing the previously discussed exception and the client is left without a SignalR connection. I don’t know why that service is being instantiated twice but my working assumption is that the first time during the initial load the system is running in “Static” render mode and when it switched to InteractiveServer render mode it instantiates again but the second time it has no HttpContext.
I’ve also experimented with the CircuitHandler approach but there I find that when the OnCircuitOpenedAsync method is called in the custom CircuitHandler the IHttpConextAccessor in my notification service always has a null HttpContext and so it is never authenticated.
I disabled pre-rendering and now my NotificationService is only instantiated once. Unfortunately, this also means that the HttpContext property on the IHttpContextAccessor is always null. This is inline with Microsoft documentation that says HttpContext for InteractiveServer render mode is only valid during the initial request to the server, i.e. before the Blazor app is rendered.