SignalR Core- Negotiation Causes CORS Error with JavaScript Client - signalr.client

I'm using the latest version of SignalR from NET6. I have a JavaScript client in MVC 4 calling the hub. When I publish to Azure, I get a CORS error on the negotiation handshake. If I turn off the negotiation the CORS error goes away. Why does the negotiation call not use my CORS policy? What are the pros and cons for the negotiation?
Here's my JavaScript console:
Here's my NET6 API CORS policy:
builder.Services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader().WithOrigins
(
//omitted
).AllowCredentials().SetIsOriginAllowed(o => true).WithMethods("GET", "POST");
}));
My client builder:
var connection = new signalR.HubConnectionBuilder()
.withUrl(connectionUrl,
{
accessTokenFactory: () => {
if (typeof bearerToken !== 'undefined') {
return bearerToken.getToken;
}
},
skipNegotiation: true, //if I change this to false the error occurs
transport: signalR.HttpTransportType.WebSockets,
})
.withAutomaticReconnect()
.configureLogging(signalR.LogLevel.Information)
.build();
skipNegotiation: true removes the error

skipNegotiation will skip the HTTP negotiate that does transport fallback and will only run WebSockets. WebSockets and CORS are not compatible so you end up skipping CORS when using that.
You can follow these github issue1,github issue2 to learn more skipNegotiation in signalr.

I found my issue. The SignalR Core JavaScript client will only connect with websockets if used in a .Net Framework app. All other protocols will fail. I tested with a NET 6 and Core 3.1 app and I was able to use Websockets and LongPolling. So if you're using an old .Net Framework app that's using a core client, limit the client to websockets only and skip the negotiation handshake:
.withUrl(connectionUrl,
{
accessTokenFactory: () => {
if (typeof bearerToken !== 'undefined') {
return bearerToken.getToken;
}
},
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets,
})

Related

SignalR .Net client cannot connect with https

I am using SignalR Core 2.4.1.0.
This is an Owin project, self-hosted.
My configuration:
public void Configuration(IAppBuilder app)
{
app.Use(async (ctx, next) =>
{
Console.WriteLine($"Incoming: {ctx.Request.Path}");
await next();
});
app.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration();
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(10);
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubConfiguration);
}
I am able to connect to https://localhost:9999/signalr/hubs from a web browser fine.
I am also able to connect to SignalR when not using https. (after removing the urlacl )
I also have tried adding a middleware before the SignalR to see the incoming request.
With http the middleware shows the Request and path.
With https the middleware shows the Request from the web browser but never shows any request from the client.
The client just changes states from connecting to disconnected with not exceptions.
My client for testing is .Net console application:
var hub = new HubConnection("https://localhost:9999");
var hubProxy = hub.CreateHubProxy("MyHUB");
hub.Error += (e) =>
{
};
hub.StateChanged += (s) =>
{
Console.WriteLine(s.NewState.ToString());
};
hub.Start();
Console.ReadLine();
I've used SignalR before but this is my first time trying to implement ssl.
In summary, .Net client will connect via http but not https.
Browser can connect to the JS library over https but I haven't tried using the JS library yet.
T.I.A.

Enabling TLS1.2 or TLS1.3 in c# asp.net core 3.1

I am building a web app using asp.net core 3.1.
I want to enable TLS1.2 (or TLS1.3 if it works and is backward compatible.)
I have a web site running under IIS Express that is failing the SSL certificate.
The console shows the following error:
I followed some instructions and I thought I could solve the problem by executing the following code in CreateHostBuilder in Program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(s => {
s.ConfigureHttpsDefaults(k =>
{
k.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
});
}).UseStartup<Startup>();
});
I have run the application and it is still failing with the same error.
I am also running an implementation of IdentityServer4 on my local machine. That does not seem to have the same problem.
The identityserver4 site is secure.
How do I force my site to use TLS1.2 (or later)?
You can set the supported TSL protocols here:
webBuilder.UseKestrel((context, serverOptions) =>
{
serverOptions.AddServerHeader = false;
serverOptions.Listen(IPAddress.Any, 80);
serverOptions.Listen(IPAddress.Any, 443,
options =>
{
var cert = ...Load TLS certificate;
options.UseHttps(serverCertificate: cert, configureOptions: httpsOptions =>
{
httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
});
});
});
See also
https://github.com/dotnet/aspnetcore/issues/22563
https://karthiktechblog.com/aspnetcore/how-to-use-tls-1-2-in-asp-net-core-2-0-and-above

javascript xmlhttp error on signalr in asp.net core

In my application 2 projects and mvc client run at port(5002) and web api project run at port (5001). I have implemented signalr in mvc client. Now showing error log in console as below:
and i have also added configuration to my api project for core policy like:
services.AddCors(options =>
{
options.AddPolicy("signalr",
builder =>
{
builder.WithOrigins("https://localhost:5002")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
And now also showing same error. Please suggest.
You need to configure your CORS like this:
services.AddCors(options =>
{
options.AddPolicy("signalr", builder => builder.WithOrigins("https://localhost:5002")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed((host) => true));
});
The lambda function that you pass to the .SetIsOriginAllowed() method returns true if an origin is allowed, so always returning true allows any origin to send requests to the api. The allow origin access control http header returned when using this method contains the origin that sent the request, not a wildcard, e.g. Access-Control-Allow-Origin: http://localhost:4200.

Unable to call .NET Core 3.x REST API that has anonymousAuthentication=false

I have two .NET core 3.1 apps: A Blazor web page server application and a REST API. Both on HTTP.
In my REST API controller I need to get the HttpContext.User.Identity.Name.
To do this I need to set "windowsAuthentication": true and "anonymousAuthentication": false in the REST API. If I use Postman the REST API is working as intended.
My problem is that once I set anonymousAuthentication:false my web page can no longer access the REST API.
If I set anonymousAuthentication:true HttpContext.User.Identity.Name returns null but the the web page can call the REST API.
I suspect this i a Cors problem. I have this Cors code in my web project:
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder =>
builder
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin());
});
and
app.UseCors("CorsPolicy");
Any help appreciated!
I managed to resolve this myself. Thanks to this post https://stackoverflow.com/a/59329927/4919060
Before calling the REST API I added this.
var handler = new HttpClientHandler()
{
UseDefaultCredentials = false,
Credentials = System.Net.CredentialCache.DefaultCredentials,
AllowAutoRedirect = true
};
Http = new HttpClient(handler);
Http.BaseAddress = new Uri("<API base URL>");
await Http.GetJsonAsync<....

API using SignalR with IdentityServer4 hangs on 3+ connections

My scenario is I have an API that implements SignalR and IdentityServer4. My client is a Vuejs SPA served from a .net-core app. After the vue client has the access_token I initiate the signalr connection and save it inside vuex store.
This is how I setup my connection:
var connection = new signalR.HubConnectionBuilder()
.withUrl(http://example.com + '/notifyHub',
{
accessTokenFactory: () => { return token }
}
).build();
connection.start()
.then(() => {
window.console.log("signalR connection successful")
})
.catch(function (err) {
return window.console.error(err.toString());
});
This is my API configuration of SignalR with IS4 Authentication
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = $"{_config["Server"]}";
options.RequireHttpsMetadata = _env.IsProduction() ? true : false;
options.ApiName = "api";
options.ApiSecret = "secret";
options.TokenRetriever = new Func<HttpRequest, string>(req =>
{
var fromHeader = TokenRetrieval.FromAuthorizationHeader();
var fromQuery = TokenRetrieval.FromQueryString();
return fromHeader(req) ?? fromQuery(req);
});
});
services.AddSignalR();
//sub claim is used from the token for individual users
services.AddSingleton<IUserIdProvider, UserProvider>();
And application
app.UseWebSockets();
app.UseAuthentication();
app.UseSignalR(routes =>
{
routes.MapHub<NotificationHub>("/notifyHub");
});
The client always establishes a successful connection, and all the live notifications work as expected.
Now to test the functionality I launch 2 clients, 1 from Chrome and 1 from Firefox, while the two clients are connected everything works.
As soon as try to connect a 3rd client (using Chrome Incognito or IE Edge), the signalr connection is successful but the API hangs for all 3 clients. If I refresh any of the clients the other 2 will resume working, if close one of the clients the other two resume working fine.
There is no valuable info in the logs on the client or the api side to indicate to what is happening.
My question is what can cause this issue and how to do I investigate?
Is this an issue of launching multiple clients from the same host, and will this occurs in production will it just block the single host launching the clients or all of the hosts?
EDIT
I changed from localhost to local IP 192...* to try connect from my mobile browser see if that changes anything, same thing happened, after 3rd client connects the API hangs, If I don't connect to the signalR hub by omitting connection.start() I can launch as many clients as I can without breaking.
I am hosting on local IIS at the moment and not IIS express.