Updating CORS Policy Origins During RunTime on All Servers - asp.net

I am trying to update the CORS Policy Origins during runtime. However, It is only updating on one server and not on all the servers. My web application is hosted on 4 servers and I want to update it on all 4 servers.
Here is the Startup.cs Code:
// Configure CORS
services.AddCors(c =>
{
c.AddPolicy("CorsPolicy", options => options
.SetIsOriginAllowedToAllowWildcardSubdomains()
.WithOrigins(origins)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
});
Code To Update the Origins During runtime :
public class CorsPolicyAccessor : ICorsPolicyAccessor
{
private readonly ICorsPolicyProvider _cors;
private readonly string _corsPolicy;
public CorsPolicyAccessor(ICorsPolicyProvider cors)
{
_corsPolicy = "CorsPolicy";
_cors = cors;
}
public async Task<CorsPolicy> GetPolicy(HttpContext context)
{
return await _cors.GetPolicyAsync(context,_corsPolicy);
}
public async Task AddDomainToPolicy(string origin, HttpContext context)
{
//_cors.GetPolicyAsync(context, _corsPolicy);
(await _cors.GetPolicyAsync(context, _corsPolicy)).Origins.Add(origin);
}
I have tried searching a lot and have tried corsoptions as wel. But No Luck yet.

Related

CORS policy issue when consuming ASP NET Core 3.1 Web Api by Angular 8

I have encountered issue with CORS policy when developing Angular 8, ASP NET Core Web Api web application. My angular app is running on http://localhost:4200
There is one service created for communication with Web Api. It looks as follows
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
apiUrl: string = "";
constructor(private http: HttpClient) {
this.apiUrl = 'https://localhost:44316';
}
login(Username: any, Password: any){
return this.http.post<Observable<ResultItem<AuthenticationResponse>>>(this.apiUrl + "/api/User/Authenticate", {Username: Username, Password: Password});
}
}
Services is later called within component, but it is simply injected, and used with subscribe method.
onLogin(){
this.authenticationService.login(this.loginFormValues.username.value, this.loginFormValues.password.value).subscribe(
result => {});
}
Web Api is running seperatly, on https://localhost:44316/
End point for the method called from Angular looks as follows:
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
this.userService = userService;
}
[HttpPost("Authenticate")]
public async Task<IActionResult> Authenticate(AuthenticationModel model)
{
return Ok(await userService.Login(model));
}
}
What I am most concerned about is my Startup file. So far, I have tried to change the CORS setting there, but with no successful results. Code of the Startup.cs file looks as follows.
Quick note:
Two lines of code within ConfigureServices method use some of my external functions, and their purpose is to:
AddSubstracture: registers all repositories as transients and registers DbContext.
AddApplication: registers services which are one layer above repositories as transients
Startup.cs code looks as follows
public class Startup
{
private IServiceCollection _services;
public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
Configuration = configuration;
Environment = environment;
SportFacilityUnitSettings = configuration.Get<SportFacilityUnitSettings>();
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
public SportFacilityUnitSettings SportFacilityUnitSettings { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc(option => option.EnableEndpointRouting = false);
services.AddSubstructure(Configuration, Environment, SportFacilityUnitSettings);
services.AddApplication();
services.AddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddHttpContextAccessor();
_services = services;
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(
options => options.SetIsOriginAllowed(x => _ = true).AllowAnyMethod().AllowAnyHeader().AllowCredentials()
);
app.UseMvc();
app.UseHsts();
app.UseMiddleware<JwtMiddleware>();
app.UseAuthentication();
app.UseRouting();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
When I hit the login button, which purpose is to send the request, I receive following error in web browser console.
Access to XMLHttpRequest at 'https://localhost:44316/api/User/Authenticate' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The weirdest thing about it, is when I debug it, and set up a breakpoint in Api layer, debugger hits it, then it enters the service layer and fails somewhere inside Authentication method .
Go to IIS where your application is hosted and check if you have set the below information right.
#Step 1 : IIS --> HTTP Response header]
#Step 2 : : Setting 4 fields in your API app hosted under IIS
#Step 3: If the above 2 steps does not work, make sure you follow the msdn information to enable cors for your application
https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1
Step 4 : : Investigate the header information you are using in your web API and if that's allowed under your IIS setting (as mentioned in Step 1)
Step 5 : : Place a breakpoint in your authenticate method to see where and why its failing. You may get more clue from this error information as well.
Step 6 : Try enabling CrossDomain to true from your front end.
Step 7 : Try enabling https for both the application (calling application and called application)

SignalR gives 404 when using WebSockets or ServerSentEvents for some users but not for others

SignalR gives me 404 when trying to connect for some users. URLs are the same except for access_token.
It is stable reproducible per user (I mean that some users are stable OK, some users are stable 404).
access_token parsed jwt diff (left is OK user, right gets 404):
I did a trace level of logs and have next:
For the OK user:
For the user that gets 404:
Note: URLs under black squares are the same.
Front End is Angular 9 with package "#microsoft/signalr": "^3.1.8", and here's the code that builds the connection:
private buildHub(): HubConnection {
console.log(this.authService.accessToken);
let builder = new HubConnectionBuilder()
.withAutomaticReconnect()
.configureLogging(LogLevel.Information)
.withUrl('ws/notificationHub', {
accessTokenFactory: () => this.authService.accessToken
});
if (this.debugMode) {
builder = builder.configureLogging(LogLevel.Trace);
}
return builder.build();
}
Backend is using next code in Startup for configuring signalR hub:
In public void ConfigureServices(IServiceCollection services):
services.AddSignalR()
.AddJsonProtocol(options =>
{
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
});
In public void Configure(IApplicationBuilder app, IHostingEnvironment env):
app.UseSignalR(route =>
{
route.MapHub<NotificationHub>("/ws/notificationHub");
});
Also we use custom authentication, so we have Authorize attribute for the Hub class:
[Authorize]
public class NotificationHub: Hub<INotificationHubClient>
and this code in public void ConfigureServices(IServiceCollection services):
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = identityServerSettings.Url;
options.Audience = identityServerSettings.ApiScopeName;
options.RequireHttpsMetadata = identityServerSettings.RequireHttpsMetadata;
options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/ws"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
Unfortunately, I don't have the full access to the environment where it is reproducible, but I can request to see any settings or try to make some changes.
What else can I try to troubleshoot the issue?
UPDATE: negotiate is fine for both users.
I had this issue recently, after the size of my JWT increased. I found that in my case the 404 error was being thrown by IIS because the query string exceeded the limit of 2048. After increasing the query string max length, my issue was resolved.

Cors issue with Blazor webassembly standalone client

I am struggling with some cors issue for DELETE and POST.
GET works fine.
Setup is: .Net core API in Azure API Management and Blazor webassembly standalone client (Azure app service) that calls the API.
Error I get when try to DELETE is.
"has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present"
So I find it difficult to understand what code that is needed here. It is so many example everywhere on CORS for .net core and also the different Blazor setups (server, hosted wasm etc).
I guess I need to handle the preflight request in some way for this to work?
This is what I use right now:
My ServiceTimes.razor that calls the API
#code {
private const string ServiceEndpoint = "https://MyProdAPI.azure-api.net/api/ServiceTimes";
private LWS_ServiceTimes[] servicetimes;
LWS_ServiceTimes servicetimeObj = new LWS_ServiceTimes();
string ids = "0";
bool showAddrow = false;
bool loadFailed;
protected override async Task OnInitializedAsync()
{
ids = "0";
try
{
servicetimes = await Http.GetFromJsonAsync<LWS_ServiceTimes[]>(ServiceEndpoint);
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
// Delete Method
protected async Task DeleteServiceTimes(long ServiceTimesID)
{
showAddrow = false;
ids = ServiceTimesID.ToString();
await Http.DeleteAsync("https://MyprodAPI.azure-api.net/api/ServiceTimes/1"); //Deletes the ID=1
}
Blazor webassembly standalone client Program.cs
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://prodsite.azurewebsites.net") });
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://xxxxxxxx-xxxx-xxxx-xxxx-xxxx/API.Access"); //API.Acess my scope in API
});
builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri("https://MyprodAPI.azure-api.net"))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("ServerAPI"));
await builder.Build().RunAsync();
}
API Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<LwsSpiderContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyDatabase")));
services.AddSwaggerGen();
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("https://prodsite.azurewebsites.net");
builder.AllowAnyMethod();
builder.WithHeaders(HeaderNames.ContentType, HeaderNames.Authorization, "x-custom-header");
builder.AllowCredentials();
});
});
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
// app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("MyAllowSpecificOrigins");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

Trying to configure multiple .NET Core pipelines, HttpContext ends up null

I am trying to follow this guide to create a logical separation in my MVC app between the API portion and the MVC portion:
https://www.strathweb.com/2017/04/running-multiple-independent-asp-net-core-pipelines-side-by-side-in-the-same-application/
Here is my implementation of the extension method he is using:
public static IApplicationBuilder UseBranchWithServices(this IApplicationBuilder app,
PathString path, Action<IServiceCollection> servicesConfiguration,
Action<IApplicationBuilder> appBuilderConfiguration)
{
var webhost = new WebHostBuilder()
.UseKestrel()
.ConfigureServices(servicesConfiguration)
.UseStartup<StartupHarness>()
.Build()
/* ojO */ .CreateOrMigrateDatabase(); /* Ojo */
var serviceProvider = webhost.Services;
var featureCollection = webhost.ServerFeatures;
var appFactory = serviceProvider.GetRequiredService<IApplicationBuilderFactory>();
var branchBuilder = appFactory.CreateBuilder(featureCollection);
var factory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
branchBuilder.Use(async (context, next) => {
using (var scope = factory.CreateScope()) {
context.RequestServices = scope.ServiceProvider;
await next();
}
});
appBuilderConfiguration(branchBuilder);
var branchDelegate = branchBuilder.Build();
return app.Map(path, builder => { builder.Use(async (context, next) => {
await branchDelegate(context);
});
});
}
When I try to use this and the SignInManager together, HttpContext is always null.
Here's a ridiculously simplified version of my AccountController:
public AccountController(SignInManager<AppUser> mgr) {
_mgr = mgr;
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var result = await _mgr.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);
}
}
I am calling app.UseAuthentication() in the ApplicationBuilder; for purposes of brevity, I have that setup as an extension method. This works:
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureMvcServices(Configuration);
}
public void Configure(IApplicationBuilder builder, IHostingEnvironment env)
{
builder.ConfigureMvcBuilder(env);
}
This does not:
public void Configure(IApplicationBuilder builder, IHostingEnvironment env)
{
builder.UseBranchWithServices(StaticConfiguration.RootPath,
services =>
{
services.ConfigureMvcServices(Configuration);
},
app => { app.ConfigureMvcBuilder(env); }
);
builder
.Run(async c =>
await c.Response.WriteAsync("This should work"));
}
In the latter example, the services object ends up with a Count of 335 descriptors, in the former (working) example it has 356.
I tried looking over this blog to see if I could figure out what is going on, but I don't see a strong correlation between what's in the method and what Gordon is describing. So I'm totally lost. Any help would be appreciated. If there's a way to split up the extension method and pass in the services for each pipeline, that would be fine with me.
Yes I know you only see one pipeline here. The point is to add more.
Here's what I ended up with:
This was the wrong way to solve this problem. I needed one properly configured pipeline. I combined my API and Web projects into a single project (though that probably doesn't matter, as long as everything is registered).
(For other reasons, I added async. Nothing to do with it.)
Program.cs:
public static async Task Main(string[] args)
{
IWebHost webhost = CreateWebHostBuilder(args).Build();
await webhost.CreateOrMigrateDatabase();
await webhost.RunAsync();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.ConfigureAppConfiguration((host, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddJsonFile("appsettings.json");
if (host.HostingEnvironment.IsDevelopment())
config.AddUserSecrets<Startup>();
else
config.AddEnvironmentVariables(prefix: "MYAPP_");
})
.UseKestrel()
.ConfigureLogging((app, logging) =>
{
//snip
})
.UseStartup<Startup>()
.UseUrls("http://*:4213");
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
var connString = Configuration.GetConnectionString("default");
services.ConfigureJwt(_signingKey, Configuration);
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-2.2#configure-localization
services.AddLocalization(options => options.ResourcesPath = StartupConfiguration.ResourcesFolder);
services.AddDbContext<AppDbContext>(builder =>
{
builder.UseLazyLoadingProxies()
.UseSqlServer(connString, with => with.MigrationsAssembly("App.Data"));
});
services.ConfigureAuthentication(Configuration);
var mapperConfig = new MapperConfiguration(maps =>
{
maps.ConfigureMvc();
maps.ConfigureApi();
});
var mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-2.2#configure-localization
services.AddMvc() // (config => { config.Filters.Add(new AuthorizeFilter()); }) // <~ for JWT auth
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSpaStaticFiles(angularApp =>
{
angularApp.RootPath = "dist"; // TODO
});
//snip
}
I split the maps (IMapper, unrelated; they are just extension methods to configure the ViewModels for the two namespaces) but I did not split the pipelines. One properly configured pipeline, with the static website in {folder}/dist. Runs both the API and the MVC project. And it's a lot less to manage.
The full code, for the interested, is here.

How to enable CORS in ASP.NET Core Preflight

I’m working with ASP.NET Core. Created the API Controller to handle Create, Get, Delete etc. I want to be able to call he APIs from Angualr2 and finally Nativescript as an APP. Set the Cors in ConfigureServices of startup as follows:
#region Cors
//allows Cors for API
//NB: must procede any endpoints
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520))
.Build();
}));
// Apply as default to all controllers. API etc
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new CorsAuthorizationFilterFactory("AllowAll"));
});
#endregion
Then referenced posy in the controller and actions:
[Produces("application/json")]
[Route("api/recipes")]
[EnableCors("AllowAll")]
and also
// DELETE: api/5
[HttpDelete("{id}")]
[EnableCors("AllowAll")]
public async Task<IActionResult> DeleteRecipe([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Recipe.Models.Recipe recipe = await _context.Recipe.SingleOrDefaultAsync(m => m.RecipeID == id);
if (recipe == null)
{
return NotFound();
}
_context.Recipe.Remove(recipe);
await _context.SaveChangesAsync();
return Ok(recipe);
}
Everything works save for delete method. I get 405 method not allowed. This however works if hosting server and client are on the same domain (i.e. Localhost testing). The issue as I understand it is the preflight checks. What am I missing. Please note this is .Net Core and different from prev. versions of asp.net where u can simply create in web.config. Which incidentally I tried creating double entries in header

Resources