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>();
});
Related
I am using ASP.NET Core Identity. After signing in with with password like this:
Microsoft.AspNetCore.Identity.SignInResult result = await signInManager.PasswordSignInAsync(appUser, login.Password, isPersistent:false, lockoutOnFailure:false);
I remain signed in. If the browser window is closed, I'm trying to get the cookie to be removed.
This is my startup code:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppIdentityDbContext>(options =>
options.UseMySql(
Configuration["ConnectionStrings:DefaultConnection"], new MySqlServerVersion(new Version(10, 6, 5))));
services.AddIdentity<AppUser, IdentityRole>().AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddScoped<IPatientsRepository, PatientRepository>();
services.AddScoped<IWebhooksNotificationsRepository, WebhooksNotificationsRepository>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
I set the IsPersistent property for the cookie to false and I expected that it should not persist if the browser window is closed.
Be aware that this is up to the browser. And many modern browsers like Chrome and Edge make it more convenient for the user by picking up right where the program left off.
It does this by acting as though the browser was never closed.
Architecture
Our web-applications are being deployed to our kubernetes cluster which are being integrated in our application gateway via the ingress extension (Azure Gateway ingress). If you navigate to the web-application, you need to sign-in and authenticate via configured app registration in our AAD B2C.
The web-application itself is being hosted on port 80 in our kubernetes cluster, but will be accessible via https within our application gateway. The application gateway will have the necessary certificates and so on.
The docker-compose (deployment of the pods) have enabled the environment variable "FORWARDING_HEADERS".
The AAD B2C does have the correct redirect URIs configured.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI()
.AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null)
.AddDapr();
services.AddCookiePolicy(options =>
{
options.Secure = CookieSecurePolicy.Always;
options.MinimumSameSitePolicy = SameSiteMode.None;
options.HandleSameSiteCookieCompatibility();
});
services.UseCoCoCore()
.UseCoCoCoreBootstrapper<CoreComponent>()
.UseCoCoCoreBootstrapper<UiComponent>()
//the following line is registering the AuthComponent, see below for more details
.UseCoCoCoreBootstrapper<UiAuthComponent>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, ILog logger)
{
loggerFactory.AddSerilog(logger.GetLogger(), dispose: true);
if (!env.IsDevelopment())
{
app.UseExceptionHandler("/Home/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.UseCookiePolicy();
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
AuthComponent.cs
It is basically this
public void RegisterAuthorization(IConfiguration configuration, string configSectionName, string[] intialScopes)
{
_serviceCollection.AddMicrosoftIdentityWebAppAuthentication(configuration, configSectionName)
.EnableTokenAcquisitionToCallDownstreamApi(intialScopes)
.AddInMemoryTokenCaches();
_serviceCollection.AddAuthorization(options =>
{
options.AddPolicy("IsGroupMember",
policy => { policy.Requirements.Add(new IsGroupMemberRequirement()); });
}););
}
I am using a configuration with this properties
{
"AzureAdB2CConfig": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "myAaadB2c.onmicrosoft.com",
"ClientId": "<client-id>",
"TenantId": "<tenant-id>",
"ClientSecret": "<client-secret>",
"CallbackPath": "/myapp/signin-oidc"
}
}
What I expect
Navigating to our https://custom.domain.com/myapp/ should allow me to authenticate myself and forward to the desired entry point of my web-application, e.g. https://custom.domain.com/myapp/Overview
What is actually happening?
The following scenarios are working without any problems:
Running the application on my machine via localhost.
Running the application in my kubernetes cluster, exposed via LoadBalancer, and accessing it via the public IP.
If I am navigating to the following url https://custom.domain.com/myapp/ , I am getting the HttpStatusCode 404. The 404 is about "/signin-oidc" which he cannot find. I have checked via my browser the header entries and it looks OK for me. The hostname of my header is also correct (custom.domain.com).
Additional info
Ingress configuration
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: myapp
namespace: myapp-namespace
annotations:
appgw.ingress.kubernetes.io/appgw-ssl-certificate: myCert
appgw.ingress.kubernetes.io/backend-hostname: custom.domain.com
appgw.ingress.kubernetes.io/backend-path-prefix: /
appgw.ingress.kubernetes.io/cookie-based-affinity: 'true'
kubernetes.io/ingress.class: azure/application-gateway
spec:
rules:
- http:
paths:
- path: /myapp/*
pathType: Exact
backend:
service:
name: myapp-service
port:
number: 80
I have found the solution via https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-6.0
I added to my application the following code
app.Use((context, next) =>
{
context.Request.PathBase = new PathString("/myapp");
return next(context);
});
I also changed the callbackpath back to /signin-oidc
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.
I'm migrating an ASP.NET Core MVC application from version 1.0.2 to 2.0.
I have in this application an external authentication via Facebook.
When I'm launching the web application I'm facing to this error:
InvalidOperationException: The SignInScheme for a remote authentication handler cannot be set to itself. If it was not explicitly set, the AuthenticationOptions.DefaultSignInScheme or DefaultScheme is used.
I searched here but it did not help me for now.
In my Startup.cs, the method "ConfigureServices" looks like this:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
// If you want to tweak Identity cookies, they're no longer part of IdentityOptions.
services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
services.AddAuthentication()
.AddFacebook(options =>
{
options.AppId = Configuration["AuthenticationFacebookSettings:AppId"];
options.AppSecret = Configuration["AuthenticationFacebookSettings:AppSecret"];
options.SignInScheme = "Facebook";
options.SaveTokens = true;
});
services.Configure<AuthenticationFacebookSettings>(Configuration.GetSection("AuthenticationFacebookSettings"));
services.AddMvc();
// Add application services.
.
.
// my services
.
.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
The method "Configure" looks like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
I am missing something here because everything looks alright...
I spend a lot of time on this issue and I am clueless...
Thanks for your help!
The link of #ps2goat put me on right way.
I changed my Facebook options like this (by taking the default value):
services.AddAuthentication()
.AddFacebook(options =>
{
options.AppId = Configuration["AuthenticationFacebookSettings:AppId"];
options.AppSecret = Configuration["AuthenticationFacebookSettings:AppSecret"];
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.SaveTokens = true;
});
In fact with .NET Core 2.0 migration, I had to change the SignInScheme. In my case I did set it to the default value with:
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
More precisely, for launching the application it was not enough.
I had to update my view for using "GetExternalAuthenticationSchemesAsync()" method instead of "GetExternalAuthenticationSchemes()" method of the signin manager.
After adapting a little bit my code, it finally works. Thanks #ps2goat.
I'm totally stuck on implementing Windows authentication for one of my .NET Core apps that uses Aurelia for client side.
The Aurelia application is hosted on port:9000 and the .NET WebAPI is hosted on port:9001.
The idea is to serve static pages from my .NET app once the app is published but now in development I use port:9000 because of the BrowserSync provided by Aurelia.
When I use port:9000 it's all fine and dandy and I have no issues posting or getting.
If I switch to port:9001 I can still get but not post. Posting results in 401 Unauthorized.
If we look at the headers for port:9000 requests..
Get(success):
Post(failed):
You can see that there are multiple headers missing in the post for some reasons, most importantly the authentication cookie..
Base-Repo.js
import {inject} from 'aurelia-framework';
import {HttpClient, json} from 'aurelia-fetch-client';
import {AppSettings} from '../infrastructure/app-settings';
#inject(HttpClient, AppSettings)
export class BaseRepo {
constructor(http, appSettings) {
http.configure(config => {
config
.withDefaults({
credentials: 'include',
headers: {
'Accept': 'application/json'
}
})
.withInterceptor({
request(request) {
console.log(`Requesting ${request.method} ${request.url}`);
return request;
},
response(response) {
console.log(`Received ${response.status} ${response.url}`);
return response;
}
})
});
this.http = http;
this.baseUrl = appSettings.api;
}
get(url) {
console.log('BaseRepo(get): ' + url);
return this.http.fetch(this.baseUrl + url)
.then(response => { return response.json(); })
.then(data => { return data; });
}
post(url, data) {
console.log('BaseRepo(post): ' + url, data);
return this.http.fetch(this.baseUrl + url, {
method: 'post',
body: json(data)
})
.then(response => response.json())
.then(data => { return data; });
}
}
Why is GET working but not POST when using BrowserSync port?
Edit 1
Post(success) for port:9001:
Edit 2
Console message post error:
OPTIONS http://localhost:9001/api/MYURLS 401 (Unauthorized)
Fetch API cannot load
http://localhost:9001/api/MYURLS.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. The response had HTTP status code 401. If an opaque response
serves your needs, set the request's mode to 'no-cors' to fetch the
resource with CORS disabled.
Edit 3
Startup.cs
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
env.ConfigureNLog("nlog.config");
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddMemoryCache();
services.AddMvc();
services.InjectWebServices();
services.AddOptions();
//call this in case you need aspnet-user-authtype/aspnet-user-identity
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IConfiguration>(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors("CorsPolicy");
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
app.UseDefaultFiles();
app.UseStaticFiles();
//add NLog to ASP.NET Core
loggerFactory.AddNLog();
//add NLog.Web
app.AddNLogWeb();
}
}
I enabled "Enable Anonymous Authentication" in project properties and voila...
Before I only had "Enable Windows Authenticaiton" enabled, now both ports work!
When application is deployed this wont be enabled anyway because by then I will use the real IIS.
Update 1
After upgrading to .net core 2.0 I was no longer able to enable both Windows Authentication and Anonymous Authentication.
After some research I found out you have to add:
services.AddAuthentication(IISDefaults.AuthenticationScheme);
in your startup.cs in order for it to work.
More info can be found in comment section and docs.
Update 2
You need Microsoft.AspNetCore.Authentication package for authentication builder.
You will need to enable CORS in your ASP.NET Core project. There's information on how to do this here: https://learn.microsoft.com/en-us/aspnet/core/security/cors.
You need to call AddCors in ConfigureServices:
services.AddCors();
And then UseCors in Configure:
// Shows UseCors with CorsPolicyBuilder.
app.UseCors(builder => builder.WithOrigins("http://example.com"));
When you're using port 9000, you're on a different origin to the API, but with 9001, you're on the same origin and therefore CORS will not apply.
The OPTIONS requests are known as "preflighting". There's more information on those here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests.