How can I connect to my SignalR hub from a different host? - c#

I have an ASP.NET MVC 5 app that is running SignalR 2.2.2 this app is running on hub.domain.com. On another Asp.Net MVC-5-based app (i.e. localhost:15371) I want to interact with the hub.
On my localhost:15371 application, I added the following code
<script>
var myHubHost = 'http://hub.domain.com/';
</script>
<script src="http://hub.domain.com/Scripts/jquery.signalR-2.2.2.min.js"></script>
<script src="http://hub.domain.com/signalr/hubs"></script>
<script src="http://hub.domain.com/signalr/custom_code.js"></script>
However, I am getting the following error when trying to connect to the hub on app.domain.com but it is working fine when I run it directly from hub.domain.com
Error: Error during negotiation request.
To enable CORS on my hub app by adding the following to my <system.webServer></system.webServer> section in the Web.config file.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
I also, tried to enable JSONP and and the detailed errors on my hub like so
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.EnableJSONP = true;
app.MapSignalR(hubConfiguration);
}
What could be causing this error? What else needs to be done to connect to my hub from another app?
The code that is used to connect to the my hub is as follow and found in the custom_code.js file.
$(document).ready(function () {
// Reference the auto-generated proxy for the hub.
var app = $.connection.myHubName;
// The getApiUrl() method return http://hub.domain.com/signalr
// as the myHubHost variable is set to http://hub.domain.com/
$.connection.hub.url = getApiUrl('signalr');
$.connection.hub.error(function (error) {
console.log(error)
});
// Create a function that the hub can call back get the new events
app.client.updatePanel = function (message) {
// Do stuff
}
// Start the connection.
$.connection.hub.start();
// This allows me to set a variable to control the base-url host when including this file on a different app.
function getApiUrl(uri)
{
var link = "/";
if (typeof window.myHubHost !== typeof someUndefinedVariableName) {
link = window.myHubHost;
}
return link + uri;
}
});
UPDATED
I enabled logging as per like so
$.connection.hub.logging = true;
I also installed Microsoft.Owin.Cors package to enable cors as per the documentation. Here is my current configuration
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true
};
map.RunSignalR(hubConfiguration);
});
Here is the logs that I get in the console. As you can see, the negotiation fails.
SignalR: Auto detected cross domain url.
SignalR: Client subscribed to hub 'myHubName'.
SignalR: Negotiating with 'http://hub.domain.com/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%5D'.
SignalR error: Error: Error during negotiation request.
SignalR: Stopping connection.

I figured out my problem finally.
I had html base tag in my layout which was causing the problem.
I removed
and my problem was solved!

Related

Why are .NET 6 Application + Application Gateway + Open ID Connect - Path based routing on different app services not working

I have an application gateway set up ("gateway"):
apps.mydomain.com
I have an app service set up ("app"):
my-app-service.azurewebsites.net
The path based rule is set on the listener for on the gateway address above.
/apps/app1/*
The default backend target and settings are set to the root of the gateway address above.
I am using AADS as the authentication store.
Both work correctly independently as I have another route set up on the gateway. I can go to the app service and it will prompt me for credentials, then take me to the index page at the root.
my-app-service.azurewebsites.net/
What I am trying to do is set up a path based rule that routes through the gateway and lands on a path under apps.mydomain.com. For example,
apps.mydomain.com/apps/app1.
I have set up the gateway properly as I can get to a static page. For example,
apps.mydomain.com/apps/app1/somedirectory/mystaticpage.html.
My problem is that when I try to authenticate, I think the signin-oidc is routing the request incorrectly. I am able to authenticate, and it appears to pass back to apps.mydomain.com/apps/app1/signin-oidc and then the middleware passes back to the root. It is authenticating, because when it hits the error page, it shows me as logged in.
I have tried overriding the cookie policy options:
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.Secure = CookieSecurePolicy.SameAsRequest;
options.MinimumSameSitePolicy = SameSiteMode.None;
//options.HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.None;
});
I have tried listening to the OnRedirectToIdentityProvider:
builder.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
//options.CallbackPath = new PathString("/apps/app1/");
//options.CallbackPath = new PathString("/apps/app1/signin-oidc");
//options.CallbackPath = "/apps/app1/signin-oidc";
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = (context) =>
{
//https://stackoverflow.com/questions/50262561/correlation-failed-in-net-core-asp-net-identity-openid-connect
context.Options.NonceCookie.Path = "https://apps.mydomain.com/apps/app1/signin-oidc";
context.Options.CorrelationCookie.Path = "https://apps.mydomain.com/apps/app1/signin-oidc";
//https://learn.microsoft.com/en-us/azure/frontdoor/front-door-http-headers-protocol#front-door-to-backend
context.ProtocolMessage.RedirectUri = "https://apps.mydomain.com/apps/app1/signin-oidc";
return Task.FromResult(0);
}
};
});
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
My guess is that just setting the otions.CallbackPath should work, but I just get correlation or sorry, we cannot log you in errors when I try that. Not sure if there is an error in the library.
I have spent over a month on and off and engaged MS technical support trying to solve this, but have not been able to get this to work. I can't imagine I am the only one doing this. I know it is in the open ID connect middleware somewhere, but cannot find the correct combination.
This is just a demo project in .NET 6 to get this working correctly. Any code will do. If there is actual working code somewhere that would be great. Just need to get the path based routing with authentication to work.

IdentityServer 4, trying to capture traffic with fiddler?

Console application trying to get discovery
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
Works fine, however i'm trying to figure out how this thing works and I cant seem to capture the http traffic.
if i use http://localhost.fiddler to redirect to the local proxy Errors With:
Error connecting to localhost.fiddler:5000/.well-known/openid-configuration: HTTPS required (it's not setup with HTTPS, the error msg is misleading!)
Strangely later in the code when we try to authenticate to web-api with
var response = await client.GetAsync("http://localhost.fiddler:5001/identity");
localhost.fiddler works fine, now this is running in the same console.app, in program.cs so the same file. This is driving me potty why on earth can't I capture traffic going to 5000 it's HTTP!!! so what mysteries are causing this ? is there another way to view the magic http traffic going to and from Identity Server ?
Added Startup class
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
}
}
added Blog, will update it and credit if we can resolve this.
As you correctly figured out, you need to use, for example, http://localhost.fiddler, to route localhost traffic through fiddler. However, using DiscoveryClient.GetAsync uses DiscoveryClient with default policy. That default policy has the following settings important for this case:
RequireHttps = true
AllowHttpOnLoopback = true
So, it requires https unless you query loopback address. How it knows what is loopback address? There is DiscoveryPolicy.LoopbackAddresses property. By default it contains:
"localhost"
"127.0.0.1"
For that reason you have "HTTPS required" error - "localhost.fiddler" is not considered a loopback address, and default policy requires https for non-loopback addresses.
So to fix, you need to either set RequireHttps to false, or add "localhost.fiddler` to loopback address list:
var discoClient = new DiscoveryClient("http://localhost.fiddler:5000");
discoClient.Policy.LoopbackAddresses.Add("localhost.fiddler");
//discoClient.Policy.RequireHttps = false;
var disco = await discoClient.GetAsync();
If you do this - you will see disovery request in fiddler, however it will fail (response will contain error), because server will report authority as "http://localhost:5000" and you query "http://localhost.fiddler:5000". So you also need to override authority in your policy:
var discoClient = new DiscoveryClient("http://localhost.fiddler:5000");
discoClient.Policy.LoopbackAddresses.Add("localhost.fiddler");
discoClient.Policy.Authority = "http://localhost:5000";
var disco = await discoClient.GetAsync();
Now it will work as expected.

SignalR 2.2.1 ASP.NET MVC 5 Edge/IE issue

I have a problem with Signal r 2.2.1 ASP.NET MVC 5. On Chrome the SignalR server works great(i can communicate with the server), but on Edge or IE he doesn't work.
The Signal R server is an ASP.NET MVC 5 Project.
Microsoft Edge Console Logging:
Chrome:
Server startup code:
public class StartupConfiguration : Controller
{
public void Configuration(IAppBuilder app)
{
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(60);
// This value must be no more than 1/3 of the DisconnectTimeout value.
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(20);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
EnableJSONP = false,
};
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR("/signalr", hubConfiguration);
}
}
Note: Same code as Class Library (Windows Service) works great on Edge/IE/Chrome, but as Web Application (ASP.NET MVC5) it doesn't work.
Maybe I'm missing something?
Can someone help me with this issue?
It's fixed by enabling the WebSocket on the server side. Microsoft Edge and IE doesn't support Server Sent Events (SSE) so the client falls back to long polling.

Connect to signalR hub from c# web api

I've been tasked with trying to move our signalR hub to an azure cloud service with a service bus backplane. No problems there. The javascript client is able to get the hubs.js and connect without errors. We also have a web api project that needs to send messages to the hub but I cannot get it to connect. Everything I've tried doesn't work and the connection times out. I must be missing something but, since this is my first time working with signalR and Azure, I don't know what it is. The web api is hosted on IIS.
Here is the code I am trying to use to connect:
private async void InitializeHub()
{
string connectionString = "http://xxxx-xxxxx.cloudapp.net/signalr";
var hubConnection = new HubConnection(connectionString, useDefaultUrl: false);
//var hubConnection = new HubConnection(connectionString);
HubProxy = hubConnection.CreateHubProxy("clientPortalHub");
await hubConnection.Start();
}
Is there some configuration I am missing? Anything need to be done in IIS? I'm not getting any helpful error messages, just that the connection times out. I can hit the url (the real one, not the one I pasted) in a browser and get "Unknown transport".
If it helps here is the startup from the hub:
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
string connectionString = "<omitted>";
GlobalHost.DependencyResolver.UseServiceBus(connectionString, "clientPortalHub");
app.Map("/signalr", map =>
{
map. UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
};
hubConfiguration.EnableDetailedErrors = true;
map.RunSignalR(hubConfiguration);
});
}

SignalR Hub not executing the specified javascript

I am using SignalR in an ASP.Net Web Application project and am having issues. My goal is to make any changes in the administrative side of the site cause some GridViews to refresh. I was planning on doing this by sending the signal for some javascript to be run, thereby refreshing the update panels containing the GridViews.
The issue right now is that I cannot get any of the code my hub is trying to call to execute in the client. I am receiving the following error in FireBug from the jquery.SignalR.js file, but I'm not sure how to proceed to fix it:
Firefox can't establish a connection to the server at ws://localhost:40068/signalr?data=[]&transport=webSockets&clientId=92e4f7b9-0118-4fd9-bb55-5f22338d6162.
(function(n,t){"use strict";if(typeof ...on=n.signalR=i})(window.jQuery,window)
After it throws this error it still looks like it is setting up the connection, but none of the javascript being sent through the hub is executed
I have set up the following hub in my site:
namespace testProject
{
public class statusChanges : Hub
{
public void ServerChange()
{
Clients.serverChange();
}
}
}
I have the following code in my button click event in the admin section. Debugging shows that this code is being run by the server:
var clients = Hub.GetClients<statusChanges>();
clients.serverChange();
Finally I have this code in my page trying to just launch an alert when it receieves the signal to confirm it is working.
<script type="text/javascript">
$(function () {
var statusChange = $.connection.statusChanges;
statusChange.serverChange = function () {
alert(8);
};
$.connection.hub.start();
});
</script>
Does any one have any ideas why this would not run or what the FireBug error means?
The error in firebug is expected. It's the websocket connection failing, don't worry about it as SignalR will fallback to longpolling. You have a method on the server side with the same name as a client side event. That doesn't work.
You want something like this:
public Administration : Hub {
}
Event handler:
var clients = Hub.GetClients<Administration>();
clients.serverChange();
Javascript:
<script type="text/javascript">
$(function () {
var administration= $.connection.administration;
administration.serverChange = function () {
alert(8);
};
$.connection.hub.start();
});
</script>

Categories