ASP.NET Core SignalR hub HttpContext.Connection.ClientCertificate returns null - signalr

I have an ASP.NET Core Web API with a SignalR hub.
My Program.cs has
builder.Services.AddAuthentication("MyScheme")
.AddCertificate(opt => opt.AllowedCertificateTypes = CertificateTypes.All)
.AddCookie("MyScheme", options =>
{
options.AccessDeniedPath = "/Home/Error";
options.LoginPath = "/Home/Login/";
});
builder.Services.AddSignalR();
builder.WebHost.UseKestrel(options =>
{
options.AddServerHeader = false;
options.Listen(IPAddress.Loopback, 8080, listenOptions =>
{
listenOptions.UseHttps(certificate);
});
})
.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o =>
o.ClientCertificateMode =
ClientCertificateMode.RequireCertificate);
})
.UseIISIntegration()
.UseUrls("https://*:8080");
app.MapHub<EPSHub>("/TESTHub", options => {
options.Transports = HttpTransportType.WebSockets;});
My web.config looks like this:
<system.webServer>
<handlers>
<remove name="aspNetCore"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
<security>
<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />
<authentication>
<windowsAuthentication enabled="false" />
<anonymousAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<basicAuthentication enabled="false" />
<iisClientCertificateMappingAuthentication enabled="true" />
</authentication>
<requestFiltering>
<!-- This will handle requests up to 1GB -->
<requestLimits maxAllowedContentLength="52428800" />
</requestFiltering>
</security>
</system.webServer>
My TESTHub.cs has a method OnConnectedAsync where I try to get the client certificate:
public override async Task OnConnectedAsync()
{
var httpCtx = Context.GetHttpContext();
IHttpContextFeature feature = (IHttpContextFeature)this.Context.Features[typeof(IHttpContextFeature)];
HttpContext cntx = feature.HttpContext;
if (httpCtx.Connection.ClientCertificate is null)
{
var result = await httpCtx.Connection.GetClientCertificateAsync();
}
}
But result always is null.
My client app is a WPF application using AspNetCore.Client.
HubConnection.cs has method:
private async Task StartConnection(string url)
{
string certificate = #"C:\test\Certificate.pfx";
var cert = new X509Certificate2(certificate, "1234", X509KeyStorageFlags.MachineKeySet);
Console.WriteLine("cert private key: " + cert.PrivateKey);
var hubConnection = new HubConnectionBuilder()
.WithUrl($"{url}TESTHub", options =>
{
options.ClientCertificates.Add(cert);
})
.Build();
try
{
await hubConnection.StartAsync();
if ((HubProvider.Connection is null ||
HubProvider.Connection.State == HubConnectionState.Disconnected) &&
hubConnection.State == HubConnectionState.Connected)
{
HubClientModel.ConnectAsync(hubConnection);
_isConnectionExist = true;
HubProvider.ServerURI = url;
return;
}
}
}
Has anyone dealt with this problem using SignalR in .NET 6 projects before?
I suggest my API need some additional configure for SignalR client certificates. But all my attempts for resolve this problem have not been successful

Related

ASP.Net Core application with Angular 14 showing "This localhost page can’t be found" after publishing on IIS

I am developing an ASP.Net Core 3.1 app with Angular 14. It is working fine when I launch it in debug mode from Visual Studio. But when I publish it on my IIS local folder, it doesn't work.
Below is the error page when I launch it through IIS:
Below is the page when I launch it using debug mode from VS:
Below is my startup.cs file:
public class Startup
{
public IConfiguration configRoot
{
get;
}
public Startup(IConfiguration configuration)
{
configRoot = configuration;
//configuration.GetConnectionString("database_chatapplication");
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ChatApplicationDBContext>(options =>
options.UseSqlServer(configRoot.GetConnectionString("database_chatapplication")));
//services.AddControllers();
services.AddControllers(options => options.EnableEndpointRouting = false);
services.AddRazorPages();
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IRoomRepository, RoomRepository>();
services.AddScoped<IChatRepository, ChatRepository>();
services.AddMvcCore().AddApiExplorer();
services.AddSwaggerGen();
}
public void Configure(WebApplication app, IWebHostEnvironment env)
{
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseSwagger();
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V2");
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
app.MapRazorPages();
app.Run();
}
}
Below is program.cs:
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services); // calling ConfigureServices method
builder.Services.AddCors();
var app = builder.Build();
startup.Configure(app, builder.Environment); // calling Configure method
// Add services to the container.
builder.Services.AddControllersWithViews();
//builder.Services.AddControllers();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html"); ;
app.Run();
This is web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\ChatApplicationWithSQLServer.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
<!--ProjectGuid: 429fe3e4-1017-422e-bbe8-4ebd34060c5b-->
This is how my published folder looks like:
Please help my resolve this issue.
After a lot of debugging, I found that it was some code in startup.cs file that was causing the issue.
Below code was the culprit:
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
app.MapRazorPages();
app.Run();
After removing above lines, I'm able to run my project using IIS published folder.

Custom 404 page on Azure App Service Linux asp.net core webapp

Like the long title say:
how can I redirect all the non-existing requests to a /404 page?
I try the web.config solution, the routes.json and/or the staticwebapp.config.json solution, both in /site/wwwroot folder (where dll and exe are) and in /site/wwwroot/wwwroot, but without success.
It is an Asp,net core razor Pages webapp, .net core 6, deployed on App Service Linux
Thanks
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
<remove statusCode="404" subStatusCode="-1" />
<error statusCode="404" path="/404" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
</configuration>
{
"responseOverrides": {
"404": {
"rewrite": "/404"
}
},
"platformErrorOverrides": [
{
"errorType": "NotFound",
"serve": "/404"
}
]
}
To add custom Error page in ASP.Net Core Web App, please check the below workaround.
In Program.cs file , add UseStatusCodePagesWithReExecute middleware to generate the unique error page.
My Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
else
{
app.UseStatusCodePagesWithReExecute("/Error/{0}");
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Add a new Controller, name it as HandleErrorController and add a view with name Error.cshtml.
My HandleErrorController.cs
using Microsoft.AspNetCore.Mvc;
namespace CustomErrorPage.Controllers
{
public class HandleErrorController : Controller
{
[Route("Error/{statusCode}")]
public IActionResult StatusCodeHandler(int StatusCode)
{
switch (StatusCode)
{
case 404:
ViewBag.StatusErrorMessage = "Sorry, the requested resource is not available";
break;
}
return View("Error");
}
}
}
My Error.cshtml
#{
ViewBag.Title = "Not Found";
}
<h1>#ViewBag.StatusErrorMessage</h1>
<a asp-action="index" asp-controller="home">
Navigate to the home page
</a>
Re-Deploy the WebApp.
Navigate to Azure portal => Your Web App => Configuration => Application Settings, add the below Environment variable
ASPNETCORE_ENVIRONMENT
Output:
Thanks to KudVenkat for the clear Explaination

Index is not created in asp.net core using ELK stack and nLog

Following is my nLog configuration file
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
<add assembly="NLog.Targets.ElasticSearch"/>
</extensions>
<targets async="true">
<target xsi:type="BufferingWrapper" name="ElasticSearch"
flushTimeout="5000"/>
<target name="elastic" xsi:type="ElasticSearch" index="myservicename"
uri="http://localhost:9200"
requireAuth="true"
username="elastic"
password="dd+A0Y=mmE1RgmIDoeHh"
layout ="API:MyServiceName|${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" >
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="elastic" />
</rules>
</nlog>
Following is my Program.cs file
using ikvm.runtime;
using NLog.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLogging();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception)
{
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
NLog.LogManager.Shutdown();
}
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Trace);
}).UseNLog();
After this when I ran the code and search in the local host:9200, I can not find the index.
So I used this curl command
curl -X PUT "localhost:9200/myservicename"
and the index is created but I can not find any logs there

Asp net core API - put method

I'm struggling to have the API PUT method working correctly while consuming my api (hosted on Plesk) from a blazor webassembly (.net6).
I Already have the GET and POST method working fine and already set my cors policy (AllowAnyOrigins,AllowAnyMethod,AllowAnyHeader) but still getting the error: "No 'Access-Control-Allow-Origin' header is present on the requested resource" and 405 if i try directly in the web api (swagger).
Any ideas?
here is the reply from swagger:
allow: GET,HEAD,OPTIONS,TRACE,PUT,POST content-length:
104 content-type: text/html date: Thu,05 May 2022 12:29:43 GMT
server: Microsoft-IIS/10.0 via: Proxy x-firefox-spdy: h2 x-powered-by: ASP.NET x-powered-by-plesk: PleskWin
request url
https://************.it/api/Requisitions/62
here is the controller:
[HttpPut("{id}")]
public async Task<IActionResult> UpdateRequisitionAsync(int id, [FromBody] RequisitionDTO requisitionFromUI)
{
if (!ModelState.IsValid)
{
throw new BadHttpRequestException("Error in the requisition");
}
else
{
var requisitionInDb = await _context.Requisitions.SingleOrDefaultAsync(a => a.Id == requisitionFromUI.Id);
if (requisitionInDb != null)
{
requisitionInDb.PriceCurr = requisitionFromUI.PriceCurr;
requisitionInDb.PurchaseQty = requisitionFromUI.PurchaseQty;
requisitionInDb.WantedReceiptDate = requisitionFromUI.WantedReceiptDate;
requisitionInDb.PartDescription = requisitionFromUI.PartDescription;
requisitionInDb.RequisitionNote = requisitionFromUI.RequisitionNote;
await _context.SaveChangesAsync();
return Ok(requisitionFromUI);
}
return NotFound();
}
}
in program.cs:
builder.Services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyMethod().AllowAnyOrigin().AllowAnyHeader();
}));
var app = builder.Build();
the request pipeline:
app.UseSwagger();
if (!app.Environment.IsDevelopment())
{
app.UseSwaggerUI(c => {
c.SwaggerEndpoint("/swagger/v1/swagger.json", "myapi v1");
c.RoutePrefix = String.Empty;
});
}
else
{
app.UseSwaggerUI(c => {});
}
app.UseHttpsRedirection();
app.UseCors("MyPolicy");
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
I am not sure, but here goes what I think could help you. You need to add allowed list of headers to your reponse in your API's startup:
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Access-Control-Allow-Headers","Content-Type, Accept, Request-Language, Request-Timezone");
});
Also, you need to specify origins you allow using the Access-Control-Allow-Origin. Either only the origin is allowed, or another domain can be allowed or any origin.
context.Response.Headers.Add("Access-Control-Allow-Origin", originHeader);
Where context is your HttpContext. In the error message, or information message you get when your request is rejected because of preflight, it should tell you what header is not allowed.
Read here Cross-Origin Resource Sharing
Also read here about Middleware delegates
browsers make a "preflight" request to the server hosting the
cross-origin resource, in order to check that the server will permit
the actual request. In that preflight, the browser sends headers that
indicate the HTTP method and headers that will be used in the actual
request.
I was able to do it amending the web.config file as follow:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
</modules>
<handlers>
<add name="aspNetCore" path="*" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\RocketstarWebApiNext.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
bu I'd like to have Visual Studio generating the file correctly and not fixing this every API publish/modifications.

when i hosting my .net5 web_api on iis server then return error : 'Access-Control-Allow-Origin'

Error Massage: Access to XMLHttpRequest at 'URL' from origin 'URL' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have a .net5 web_api application and frontend develop using reactjs. This web_api work perfectly on localhost. I used ionos windows hosting. when I host my web_app on IIS server then the error comes.
I have tried on web.config file. Like that
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
I have tried on startup.cs file. Like that
services.AddCors(opt =>
{
opt.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
policy.WithOrigins("http://localhost:3000")
.WithHeaders(HeaderNames.ContentType, "x-custom-header", HeaderNames.CacheControl)
.WithMethods("POST", "PUT", "DELETE", "GET", "OPTIONS")
.AllowCredentials()
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520))
.Build();
});
});
and
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("http://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
I'm trying to solve this error various way. But i can't do it.
Please help me to solve this.
I created a new asp.net core 5 api project and enabled cors policy by adding code in startup.cs like below, and then publish the project to IIS in my virtual machine. I called the api in my asp.net core MVC project and it worked well for me. I think you may follow this official document or my code to check if you missed some configuration.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApi", Version = "v1" });
});
//add cors service and send the policy
services.AddCors(options =>
{
options.AddPolicy(name: "mypolicy", builder =>
{
builder.WithOrigins("https://localhost:44323/")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApi v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("mypolicy");//use cors policy by this line
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
In the end, I solved this error ☺️. this error comes from "WebDAVModule".
I just remove this module from web.config file.
Add modules-remover before handlers.
web.config
<modules>
<remove name="WebDAVModule" />
</modules>
<handlers>
</handlers>

Resources