Getting 401 Error with SignalR Owin Host 2.0.3 - signalr

I am trying to setup a load test exercising as much of the code base as possible. I am running the server and client in the same process:
class Program
{
class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration
{
EnableDetailedErrors = true
});
}
}
public static void Main(string[] args)
{
var signalr = "http://localhost:21337/";
using (WebApp.Start<Startup>(signalr))
{
var connection = new HubConnection(signalr) {Credentials = CredentialCache.DefaultNetworkCredentials};
var catalog = connection.CreateHubProxy("CatalogHub");
connection.Start().Wait();
catalog.Invoke("SendChat", String.Empty, String.Empty, String.Empty);
Console.ReadLine();
}
}
}
However, on connection.Start.Wait() I get a 401 Unauthorized error. I am not sure why because IIS is nowhere in the pipeline.

PEBKAC. Turns out the hub has [Authorize] set on it but I was using anonymous authentication. Ensuring Ntlm authentication fixed the problem.
public class Startup
{
public void Configuration(IAppBuilder app)
{
var listener = (HttpListener)app.Properties[typeof(HttpListener).FullName];
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
app.MapSignalR(new HubConfiguration
{
EnableDetailedErrors = true,
EnableJSONP = true
});
}
}

I was getting the same error after I set the Authorize attribute on my hub class and found out that Windows Authentication was disabled in the properties for my Web API project. This was pointed out in getting 401 error with signalr owin host 2.0.3. This worked perfectly when I enabled it and I was also able to get the User Identity Name in my hub
One thing to note is that I did not have to set the Authentication scheme as mentioned in the above post

Related

Blazor Server-Side with SignalR and Asp.Net Core Identity Feature : Response status code does not indicate success: 401 (Unauthorized)

Hope all is well.
I' m trying to implement SignalR in my Blazor server-side app, which also implements Asp.net core identity.
I get the following message when I navigate to the razor page that implements the signalr hub:
“HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).”
And the following is highlighted in red for exception:
await hubConnection.StartAsync();
Here is my razor page code behind:
private HubConnection hubConnection;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/EmployeePresenceStatusHub"))
.WithAutomaticReconnect()
.Build();
hubConnection.On("ReceiveMessage", () =>
{
LoadEmployeesPresenceStatus();
StateHasChanged();//Refresh the component using updated data
});
await hubConnection.StartAsync();
}
Here is my hub class:
public class EmpoyeesPresenceStatusHub : Hub
{
public async Task SendMessage()
{
await Clients.All.SendAsync("ReceiveMessage");
}
}
I've followed the example from here:
Use ASP.NET Core SignalR with Blazor
I'm presuming asp.net core identity feature in my Blazor app is causing this?
Any help most appreciated.
Try Using Cookies to connect to the hub. I am sure if that is ideal but it solved my issue.
Create a cookie provider class
public class CookieProvider
{
public string Cookie { get; set; }
}
Register this class as a Scoped Service
buider.Services.AddScoped<CookieProvider>(); //net6.0
services.AddScoped<CookieProvider>(); //net5.0
Inject the service globaly in _imports.razor
#inject CookieProvider CookieProviderService
Inside App.razor add a code section with the following
[Parameter]
public string Cookie { get; set; }
protected override Task OnInitializedAsync()
{
CookieProviderService.Cookie = Cookie;
return base.OnInitializedAsync();
}
Inside _Host.cshtml
#{
var cookie =
HttpContext.Request.Cookies[".AspNetCore.Identity.Application"];
}
<component type="typeof(App)" param-Cookie="cookie" render-mode="ServerPrerendered" />
Lastly Create Your Hubconnection
var container = new CookieContainer();
var cookie = new Cookie()
{
Name = ".AspNetCore.Identity.Application",
Domain = "localhost",
Value = CookieProviderService.Cookie
};
container.Add(cookie);
hubConnection = new HubConnectionBuilder()
.WithUrl(_navigationManager.ToAbsoluteUri(ChatHub.HubUrl),
options =>
{
options.Cookies = container;
})
.WithAutomaticReconnect()
.Build();

Client certificate not included by HttpClientHandler in .net core

I'm having difficulties sending a certificate using HttpClientHandler because the certificate simply won't appear on Server's request. The certificate has the proper EKU for server and client authentication, and Key Usage of "Digital Signature". #davidsh regarded the issue 26531 for the lack of logging that HttpClient had but running my project in Visual Studio (with logs set to Trace and using dotnet 3.1.401) no output error came out. I'm not very familiar at all with logman but I ran it when the issue supposed to happen as I executed my code and nothing from the log stood out indicating what the problem could be. Running out of options to test the code I attempted to add a certificate without the private key on the client request to see if the httpClientHandler.ClientCertificates.Add ... would throw any error saying something like "You need a certificate with private key to sign your request", shouldn't it say anything?
On client:
services.AddHttpClient<ILetterManClient, LetterManClient.LetterManClient>()
.ConfigureHttpClient(client =>
{
client.BaseAddress = new Uri(configuration.GetValue<string>("Microservices:LetterManAPI"));
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
HttpClientHandler httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = ValidateServiceCertficate;
httpClientHandler.ClientCertificateOptions = ClientCertificateOption.Manual;
clientCertificate = new X509Certificate2("client_cert.pfx", "developer");
httpClientHandler.ClientCertificates.Add(clientCertificate);
return httpClientHandler;
});
On server:
public class ValidateClientCertificates : TypeFilterAttribute
{
public ValidateClientCertificates() : base(typeof(ValidateClientCertificatesImpl))
{
}
private class ValidateClientCertificatesImpl : IAsyncAuthorizationFilter
{
X509Certificate2 clientCertificate;
public ValidateClientCertificatesImpl(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
clientCertificate = new X509Certificate2("client_cert.crt");
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var certificate = await context.HttpContext.Connection.GetClientCertificateAsync();
if ((certificate == null) || (!certificate.Thumbprint.Equals(clientCertificate.Thumbprint)))
{
context.Result = new UnauthorizedObjectResult("");
return;
}
}
}
}
Side note:
I've been also trying to debug my project using code compiled from corefx repo to see what's going but Visual Studio insists reference the code from local installed sdk instead of the project from corefx that it's referencing it but this is another issue.
I've created this project that simulates the issue. It creates the certificates and it has two projects with one service and another client implemented.
Any help will be very welcomed.
These are the guidelines for Kestrel to require Client certificate but it assumes that the CA is installed in the machine otherwise you have to specify the client certificate directly when configuring Kestrel server as follows:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o => {
o.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
o.ClientCertificateValidation = ValidateClientCertficate;
});
});
});
public static Func<X509Certificate, X509Chain, SslPolicyErrors, bool> ValidateClientCertficate =
delegate (X509Certificate serviceCertificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
X509Certificate2 clientCertificate;
clientCertificate = new X509Certificate2("client.crt");
if (serviceCertificate.GetCertHashString().Equals(clientCertificate.Thumbprint))
{
return true;
}
return false;
};
Unfortunately, you can't require Client certificates for a specific route as I intended.

Blazor Request blocked by CORS policy

I am trying to send a request from a Blazor(client-side) client to a server and i keep getting this error:
Access to fetch at '[route]' (redirected from '[other route]') from
origin '[origin route]' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
On the server i have already added the CORS extension in the pipeline to no avail:
Server Startup
public void ConfigureServices(IServiceCollection services) {
services.AddCors();
services.AddResponseCompression(options => {
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
{
MediaTypeNames.Application.Octet,
WasmMediaTypeNames.Application.Wasm,
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
app.UseResponseCompression();
app.UseMvc();
app.UseBlazor<Client.Startup>();
}
Blazor Client request
public async Task<Catalog> GetCatalogAsync() {
try {
HttpRequestMessage message = new HttpRequestMessage {
RequestUri = new Uri(BASE_PATH + Routes.GET_CATALOG), //BASE_PATH= 172.XX.XX.XX:8600
Method = HttpMethod.Get
};
var resp = await this.client.SendAsync(message); // client is HttpClient
var resultString = await resp.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<Catalog>(resultString);
return data;
} catch (Exception ex) {
throw;
}
}
Controller
[HttpGet]
[Route(Routes.GET_CATALOG)]
public async Task<Catalog> GetCatalogAsync() {
try {
var registry = await this.adminService.GetCatalogAsync();
return registry;
} catch (Exception ex) {
throw;
}
}
POCO
[Serializeable]
public struct Catalog{
}
What else can i do to be able to reach my server? Is it due to Blazor ?
As you can see i have already added the UseCors(...).
P.S
I have published my Blazor Server project together with the Client.They are in the same directory.This folder i placed it on a computer,and i am trying from my computer to open blazor : 172.168.18.22:8600/
Update
I have also tried adding headers to my HttpRequestMessage to no avail:
HttpRequestMessage message = new HttpRequestMessage {
RequestUri = new Uri(BASE_PATH + Routes.GET_CATALOG),
Method = HttpMethod.Get,
};
message.Headers.Add("Access-Control-Allow-Origin","*");
message.Headers.Add("Access-Control-Allow-Credentials", "true");
message.Headers.Add("Access-Control-Allow-Headers", "Access-Control-Allow-Origin,Content-Type");
#Bercovici Adrian, why do you add CORS support to your App ? Do you make cross origin requests ? If you don't, don't try to solve the issue by adding unnecessary configuration that may lead to more subtle bugs.
As usual, without seeing a repo of this app, can't help you any further.
Update:
What is this UseBlazor ?
You should upgrade your app to the latest version...
New Update:
Sorry, but I'm using the current preview version of Blazor
Startup class
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
**// Instead of UseBlazor**
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseStaticFiles();
app.UseRouting();
**// This configure your end points**
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
}
}
Note that I've removed the configuration of CORS as your client and server share the same domain. Please use the docs how to configure CORS appropriately.
Try this and see if it is working for you (I guess your issue is related to the configuration of the endpoints. Somehow, it seems to me that because you did not configure the endpoints, your request is redirected, and thus you get the message displayed by you above.)
Next to do is to check if your http request was appropriately cooked. But first checks the end points.
Somehow the problem was due to a very old client version that was cached on the browser.Never again will i forget to clear the browser cache after this problem.
Thank you all for your help and support !
Check that you do not send HTTP requests when running from HTTPS. For example if you send requests to http://172.168.18.22:8600 when your application was opened in https://172.168.18.22:8600 you may have an issue.
you need to specify your policy name in the middleware.
builder.Services.AddCors(policy =>{
policy.AddPolicy("Policy_Name", builder =>
builder.WithOrigins("https://*:5001/")
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowAnyOrigin()
);});
// Configure the HTTP request pipeline.
app.UseCors("Policy_Name");

signalr self-host and static file server (e.g. owin.selfhost)

I have been "too" succesfully with my stand-alone Singular web service. We would like to now remove the IIS server, which does little other than serve about 10 JS/HTML/CSS files. Node.JS has a static-files plugin,
I looked and I saw that Katana, OWIN.SelfHost also has such a feature.
http://odetocode.com/blogs/scott/archive/2014/02/10/building-a-simple-file-server-with-owin-and-katana.aspx
Update
It was suggested to use the OWIN.Self-Host which has a feature, but I am unsure of where to put it in my existing code, and if it would effect the existing SIGNALR code (all Hubs share the same HTTP request calls, so I am also squeezing in Self-host on the same port, I wonder if that works). As well as it would be nice to use Scott Allen's (above referenced) example which also allows File Browsing.
Here is the code I current use to start up SIGNALR (V2.2) , which is run at the directly from the Main.cs file:
class Comm
{
public string url = "http://localhost:7700";
// string url = "http://*:7700"; // Requires admin privileges
public void start()
{
Task t = Task.Run(() =>
{
WebApp.Start<Startup>(url);
this.Clients = GlobalHost.ConnectionManager.GetHubContext<GatewayHub>().Clients;
Console.WriteLine("Server running on {0}", url);
Thread.Sleep(Timeout.Infinite);
});
}
public void send()
{
Clients.All.display("broadcast sent...");
}
private IHubConnectionContext<dynamic> Clients
{
get;
set;
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = (Tools.LogLevel >= 12);
hubConfiguration.EnableJavaScriptProxies = true;
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR("/signalr", hubConfiguration);
}
}
This answer is really from #Tracther, and I wish he would write it up so that I can credit him. But thanks to his input, here is what works for me. I did not need the commented lines. All I needed was to set the paths for /signlar and for the static FileSystem
}
class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = (Tools.DebugLevel > 12);
hubConfiguration.EnableJavaScriptProxies = true;
app.UseCors(CorsOptions.AllowAll);
//app.UseStaticFiles();
app.UseFileServer(new FileServerOptions()
{
//RequestPath = new PathString("/Scopes"),
EnableDirectoryBrowsing = true,
FileSystem = new PhysicalFileSystem(#".\Scopes"),
});
app.MapSignalR("/signalr", hubConfiguration);
}
}

Client can't connect to SignalR Hub (CORS does not work)

My StartUp.cs in server (Implementing cross-domain requests by using Owin.UseCors):
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
}
My client (console project):
static void Main(string[] args)
{
IHubProxy _hub;
string url = #"http://111.111.111.111:13098/signalr/hubs";
var connection = new HubConnection(url, querystringData);
_hub = connection.CreateHubProxy("TestHub");
connection.Start().Wait();
_hub.On("Broadcast", x => Console.WriteLine(x));
}
Only when I run the client (console.exe) at server (signalr project is hosted in IIS), can it connect to hub.
If I run the client at my computer, it it not capable of getting connected.
Why?
Input http://111.111.111.111:13098/signalr/hubs at Chrome:
Can you enable JSONP and see?
var hubConfiguration = new HubConfiguration { EnableJSONP = true };
Solved.
I've used a wrong port... firewall blocked it.

Resources