Net Core Cors and Angular application on IIS - c#

I'm having some troubles setting up the correct Cors settings on my application.
NetCore application with Angular6 hosted on IIS, the angular app is outside the .Net project and compiled and inserted inside the wwwroot when publishing.
Now the problem is that when I'm coding the angular part, I'd like to call directly the release server to test some functionality.
I tried any kind of approach to have this work out but it seems like I'm always hitting a problem with the Cors setup but only for the POST calls, GET works fine. So here is my startup code:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ILoggerManager, LoggerManager>();
services.AddSingleton<IDbContext, MongoDbContext>();
services.AddSingleton<IIdGenerator, IdGenerator>();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
c.AddSecurityDefinition("Bearer", new ApiKeyScheme(){In = "header",Description = "Please enter JWT with Bearer into field", Name = "Authorization", Type = "apiKey"});
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{"Bearer", Enumerable.Empty<string>()}
});
});
services.AddCustomRepositories();
services.AddCustomServices();
services.AddJwtService();
//Add cors
services.AddCors();
// Add framework services.
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.Configure<IISOptions>(options =>
{
options.ForwardClientCertificate = false;
});
services.Configure<Settings>(Configuration.GetSection("SystemSettings"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//app.UseCors("AllowSpecificOrigin");
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
app.UseDefaultFiles();
// this will serve js, css, images etc.
app.UseStaticFiles();
app.Use(async (context, next) =>
{
if (context.Request.Path.HasValue &&
!context.Request.Path.Value.StartsWith("/api/") &&
!context.Request.Path.Value.StartsWith("/swagger"))
{
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync(
env.ContentRootFileProvider.GetFileInfo("wwwroot/index.html")
);
return;
}
await next();
});
//app.UseHttpsRedirection();
app.UseSwagger();
app.UseAuthentication();
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvc();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
c.DocExpansion(DocExpansion.None);
});
}
Since I want this enabled only for development purpose I'd like to enable it globally.

i think you need to set the origin manually
app.UseCors(builder =>
builder.AllowAnyOrigin().AllowAnyMethod());
as shown in here

This's the necessary part to add cors in your web API.

Related

How to include Access-Control-Allow-Credentials in ASP .Net Core app?

Are there an explicit "Access-Control-Allow-Credentials" attribute to explicitly allow credentials to be sent with a request to a specific client site?
I have tried the following
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://my-account-name.github.io",
"http://my-account-name.github.io/My-repository",
"https://my-account-name.github.io",
"https://my-account-name.github.io/My-repository");
});
});
...
app.UseCors(MyAllowSpecificOrigins);
Not working solution source
And I get the "CORS" policy restriction in Chrome console nevertheless for those request that include credential headers and / or authorize cookies. Other requests (that don't include credentials) are passing fine with cors fetch() from JS.
The complete solution to the CORS Cookie Authorization is the SameSite = None; cookie policy (which you have to tell the browser explicitly from your server)
// Controller.cs
[EnableCors("_allowSpecific")] // !!!
public class YourController : Controller
// ...
// Startup.cs (.Net 5) / Program.cs (.Net 6-7 +)
string MyAllowSpecificOrigins = "_allowSpecific";
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(options =>
{
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None; // !!!
});
services.AddCors(options => {
options.AddPolicy(name: MyAllowSpecificOrigins,
policy => { policy.WithOrigins( "http://your-domain.your-site.com", "https://your-domain.your-site.io", "http://your-domain.your-site.io"
#if DEBUG // Use this for debugging CORS in NPM on another localhost port
, "http://localhost:8081", "https://localhost:8081", "http://127.0.0.1:8081", "http://192.168.1.64:8081"
#endif
).AllowAnyHeader().AllowAnyMethod().AllowCredentials();
});
});
services.Configure<CookiePolicyOptions>(options => {
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None; // !!!
});
// ADD your services, DbContexts, Identity, Configure<IdentityOptions>, AddDefaultIdentity<IdentityUser>, AddRoles<IdentityRole>, AddEntityFrameworkStores<ApplicationDbContext>, ConfigureApplicationCookie, AddDatabaseDeveloperPageExceptionFilter, AddSingleton<IHttpContextAccessor>, AddRazorPages, AddControllersWithViews, AddLogging,
services.ConfigureApplicationCookie(options => {
// Cookie settings
options.Cookie.SameSite = SameSiteMode.None; // !!!
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(10000);
options.SlidingExpiration = true;
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IWebHostEnvironment env, Microsoft.Extensions.Hosting.IHostApplicationLifetime appLifetime)
{
// ...
app.UseCookiePolicy(new CookiePolicyOptions {
MinimumSameSitePolicy = SameSiteMode.None // !!!
});
// app.UseRouting(); ...
app.UseCors(MyAllowSpecificOrigins); // ? 1) not sure whether you need to use both, but it works
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(MyAllowSpecificOrigins); // ? 2) not sure if you need to use both, but it works
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireCors(MyAllowSpecificOrigins); // !!!
endpoints.MapRazorPages().RequireCors(MyAllowSpecificOrigins); // !!!
endpoints.MapControllers().RequireCors(MyAllowSpecificOrigins); // !!!
});
}
//...
Not only this setup will allow you to use CORS autherization (using cookie headers), this will also allow Chrome to block any request from other than "_allowSpecific" origins.
I had to spend almost a year to figure this out.

How can I exclude some routes from UseStatusCodePagesWithReExecute?

I have a full-stack app in ASP.NET Core 5. The front-end is React and the back-end is OData.
I need to use app.UseStatusCodePagesWithReExecute("/"); in my Configure() method to redirect any unknown requests to index.html, as routing is handled by the client-side code.
The problem is that in OData standard, when a key in a GET request is invalid, it returns a 404 error. This error will cause a redirect to index.html as well.
My Question: How can I exclude any request that starts with /odata.svc from UseStatusCodePagesWithReExecute()?
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// generated swagger json and swagger ui middleware
// You can access the swagger ui at /swagger/index.html
app.UseSwagger();
app.UseSwaggerUI(x => x.SwaggerEndpoint("/swagger/v1/swagger.json", "ASP.NET Core Sign-up and Verification API"));
//app.UseCors("CorsPolicy");
// global cors policy
app.UseCors(x => x
.SetIsOriginAllowed(origin => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.All
});
app.UseRouting();
// global error handler
app.UseMiddleware<ErrorHandlerMiddleware>();
// custom jwt auth middleware
app.UseMiddleware<JwtMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(30);
// add an endpoint for an actual domain model
// registered this endpoint with name odata (first parameter)
// and also with the same prefix (second parameter)
// in this route, we are returning an EDM Data Model
endpoints.MapODataRoute("odata.svc", "odata.svc", GetEdmModel(app.ApplicationServices));
endpoints.EnableDependencyInjection();
endpoints.MapControllers();
// enable serving static files
endpoints.MapDefaultControllerRoute();
});
// Redirects any unknown requests to index.html
app.UseStatusCodePagesWithReExecute("/");
app.UseHttpsRedirection();
// Serve default documents (i.e. index.html)
app.UseDefaultFiles();
//Set HTTP response headers
const string cacheMaxAge = "1";
// Serve static files
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// using Microsoft.AspNetCore.Http;
ctx.Context.Response.Headers.Append(
"Cache-Control", $"public, max-age={cacheMaxAge}");
}
});
}
This seems like an ideal use case for the UseWhen() extension method. This functionality is (under)documented by Microsoft, though #Paul Hiles has a more comprehensive write-up about it on his DevTrends blog. Basically, this allows you to conditionally inject middleware into your execution pipeline.
So, to conditionally exclude UseStatusCodePagesWithReExecute() if your request path starts with /odata.svc, you would simply wrap your UseStatusCodePagesWithReExecute() call with a UseWhen() condition, as follows:
app.UseWhen(
context => context.Request.Path.StartsWithSegments("/odata.svc"),
appBuilder =>
{
appBuilder.UseStatusCodePagesWithReExecute("/");
}
);

No Webpage was found for the address Azurewebsite

I'm getting this
No web page was found for the web address: https://limbu.azurewebsites.net/
Everything worked fine in development but after publishing I get this error.
I can navigate to Views by typing in url but not the index page. I'm also not able to perform Register and login functions from controller in published website.
My Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
{
services.Configure<IServiceProvider>(options => {
options.GetService<AppDbContext>().Database.Migrate();
}).AddDbContextPool<AppDbContext>(options => {
options.UseSqlServer(_config.GetConnectionString("AzureSqlConnection"));
//options.EnableSensitiveDataLogging(true);
});
}
else
{
services.AddDbContextPool<AppDbContext>(options => options.UseSqlServer(_config.GetConnectionString("Connection")));
}
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
}).AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
services.AddTransient<IMailService, SendGridMailService>();
services.AddHostedService<TimedHostedServices>();
services.AddScoped<IGetGlobalCovidData, GetGlobalCovidData>();
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
}).AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
//ServicePointManager.ServerCertificateValidationCallback += //This code is security risk as it validates all certificates
// (sender, certificate, chain, errors) => //Not to be used for production and used this instance as I trust the
// { //The site I'm pulling data from
// return errors == SslPolicyErrors.None;
// };
//services.AddLogging(loggingBuilder => { //This code is security risk as it displays all sensitive data
// loggingBuilder.AddConsole() //Not recommended for production
// .AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Information);
// loggingBuilder.AddDebug();
//});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapDefaultControllerRoute();
});
}
Please advise me on what I should provide for debugging.
my .cshtml build action is -content and donotcopy
.cs build action is -c# compiler and donotcopy
For the users who are getting the same issue, There could be different root causes for 404 error messages. Sub-status codes will help you understand the issue better.
You may check whether the site files are deployed correctly or not through Kudu Console. Also, suggest you Enable diagnostics logging for web apps in Azure App Service incase if you haven’t enabled earlier to check the complete error details and root cause.

Blazor WebAssembly HttpResponseMessage empty Headers [duplicate]

I must be doing something wrong here but I can't figure it out; it seems to be a CORS issue from what I can tell. I need to expose Access-Control-Expose-Headers: * to any origin but dotnet core 2.1 isn't doing what I expect.
Relevant Startup.cs code:
public void ConfigureServices(IServiceCollection services)
{
//Mapping settings to POCO and registering with container
var settings = new AppSettings.ReportStorageAccountSettings();
Configuration.Bind(nameof(AppSettings.ReportStorageAccountSettings), settings);
services.AddCors(options =>
{
options.AddPolicy("AllowAll",
builder =>
{
builder
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials();
});
});
services.AddSingleton(settings);
services.AddApiVersioning();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("AllowAll");
app.UseHttpsRedirection();
app.UseMvc();
}
This application is hosted in Azure and I have added a * entry to the CORS settings in Azure just for good measure. Now, whenever the client application (which is also hosted in Azure) makes a post request, the headers are not accessible via JS and Access-Control-Expose-Headers: * is not present in the response. However, I can see the headers when I inspect the network response and when using Fiddler. I have tried Axios and Jquery for accessing the headers to rule out any issues with the JS. What am I doing wrong here?
In the controller I respond with:
Response.Headers.Add("Location", $"api/someLocation");
return StatusCode(StatusCodes.Status202Accepted);
The CorsPolicyBuilder's AllowAnyHeader method configures the Access-Control-Allow-Headers response header, which is used only for preflighted requests. The Access-Control-Expose-Headers response header is what's needed, which is configured using WithExposedHeaders.
Here's a complete example:
services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials()
.WithExposedHeaders("Location"); // params string[]
});
});
As Kirk mentioned .WithExposedHeaders() method is what is needed.
Another variation to Kirk's answer is:
// in Startup.cs
// at the end of ConfigureServices() add:
services.AddCors();
// at the top of Configure() add:
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().WithExposedHeaders("*"));

Asp.Net Core - Enable HttpCompression in Shared Hosting Environment?

I'm using a shared host running a ASP.Net Core 2.0 web app. When I run the app on localhost, I can see HttpCompression is working just fine (content-encoding: gzip) response header is returned for JS files).
After I deploy to the shared host, the content-encoding: gzip response is no longer there. I tried adding various httpCompression/urlCompression settings in the web.config, but the hosting company says these settings are disabled for my plan.
Is there any other way to make Gzip compression work, or do I have to use a hosting plan where it is enabled in IIS?
Edit: I'm also using the ResponseCompression middleware as part of ASP.Net Core and the content-encoding response headers still do not appear. Configure code is as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging();
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
});
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
loggerFactory.AddDebug(LogLevel.Debug);
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/error");
}
app.UseStatusCodePagesWithRedirects("/error/{0}");
app.UseResponseCompression();
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.Append("Cache-Control", "public, max-age=604800");
}
});
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "areas",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
var options = new RewriteOptions()
.AddRedirectToHttps();
app.UseRewriter(options);
}
Use the Response Compression Middleware; add a reference to the Microsoft.AspNetCore.ResponseCompression or Microsoft.AspNetCore.All NuGet package.
See docs.
Set it up via:
WebHost.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddResponseCompression();
})
.Configure(app =>
{
app.UseResponseCompression();
// ...
})
.Build();
Edit: In a comment below you mention that your website is on https which is the reason why response compression is off by default to prevent CRIME and BREACH attacks.
You can re-enable this via the EnableForHttps option.
services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<GzipCompressionProvider>();
});

Categories

Resources