Closed michael-gould closed 4 years ago
@michael-gould thanks for contacting us.
@BrennanConroy is there a way for configuring this in the underlying signalr client?
Seems like what's missing here is the ability to configure the underlying websocket to do the right thing
Hi, thanks for getting back on this.
I don't think the websocket connection is the issue. It works if I skip the negotiation step and just use websockets as the transport. The problem is not having the authentication cookie attached to the Http POST to /negotiate. Thanks
is there a way for configuring this in the underlying signalr client?
Yes, you need to use the HttpMessageHandlerFactory
option and provide your own HttpMessageHandler
that sets options on the request. I'm guessing WASM sets "omit" by default for credentials?
@BrennanConroy likely. Can you post a snippet here on how to do that?
@guardrex can we get this documented?
Thanks - I'll give it a try.
Is this Blazor PWA-only (thus only for the Blazor PWA doc) or is it for all Blazor WASM scenarios (thus for the Blazor Hosting model configuration topic in its Blazor WASM config area)?
If for Blazor WASM generally, I will cross-link to it in the Blazor PWA doc.
I think this would apply to any Blazor WASM scenario.
The only current coverage (I think) is this ...
| HttpMessageHandlerFactory
| null
| A delegate that can be used to configure or replace the HttpMessageHandler
used to send HTTP requests. Not used for WebSocket connections. This delegate must return a non-null value, and it receives the default value as a parameter. Either modify settings on that default value and return it, or return a new HttpMessageHandler
instance. When replacing the handler make sure to copy the settings you want to keep from the provided handler, otherwise, the configured options (such as Cookies and Headers) won't apply to the new handler. |
Because the advice speaks about copying certain settings that I'm unaware of, it's best if one of you give me a specific example if you want a code example in the text. Otherwise, it's only safe for me to mention it (briefly and similar to the way that you, @BrennanConroy, stated it :point_up:) and cross-link to that SignalR topic section.
This works for me:
var client = new HubConnectionBuilder()
.WithUrl(new Uri("http://signalr.example.com"), options =>
{
options.HttpMessageHandlerFactory = innerHandler => new IncludeRequestCredentialsMessagHandler { InnerHandler = innerHandler };
}).Build();
public class IncludeRequestCredentialsMessagHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
return base.SendAsync(request, cancellationToken);
}
}
I'll use that if @BrennanConroy gives it an upvote :+1:.
I'm very happy with that example :+1:
Beautiful - that worked a treat.
Thanks so much for your help.
@guardrex has this been documented already?
I'm developing a Blazor Webassembly PWA, and want to connect to a Signalr hub that's on a different subdomain, using cookie authentication.
I'm developing with Visual Studio 16.6.1 on Windows 10, Net SDK 3.1.301. Servers are hosted on Centos 8.1, using Asp.Net Core 3.1 applications on the servers and Blazor WebAssembly 3.2.0 for the web client. The SignalR client is using NuGet package Microsoft.AspNetCore.SignalR.Client 3.1.5 The SignalR Hub server uses Microsoft.Azure.SignalR 1.4.3
The application running in Blazor Webassembly loads from app.example.com, and the user logs in and is issued with a secure HttpOnly authentication cookie. The SignalR client then tries to connect to signalr.example.com, where the SignalRHub is hosted, but the negotiate step fails with a 401 Unauthorized. The server at signalr.example.com is set up to accept authentication cookies issued from app.example.com, with the required CORS policies, and using Azure Blob storage and Azure keyvault to provide data protection service that's shared by both servers. I have verified that I'm able to POST successfully to the negotiate endpoint at signalr.example.com, provided I specifically request for credentials to be included, using:
requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
Here is my example client code, in a razor page:Here's the result when I run the code as-is:
I've checked on the requests coming into the server and the authentication cookie is missing from the POST to /messagehub/negotiate coming from HubConnection.
If I uncomment the two lines to skip negotiation and force use of websockets then the SignalRConnection works, so it appears that it’s just the negotiate step that is failing. However this approach isn't compatible with using the Azure SignalR service so is not a good solution.
Also if I comment out the use of:
requestMessage.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);
then the direct POST to /messagehub/negotiate also fails with 401 Unauthorized.I can’t set the authentication cookie manually in the options on HubConnectionBuilder.WithUrl(), because the HttpOnly authentication cookie can’t be read from the client code.
There doesn’t appear to be any way to tell the HubConnection to use BrowserRequestCredentials.Include so that the authentication cookie is sent with the POST to /messagehub/negotiate. If I've just missed something it would be great to hear about it! Thanks