.netaxioscorspreflight

Call to web api blocked by CORS policy even though .net webapi allows all methods


UPDATE 2: SOLVED: See my answer below.

UPDATE 1: Even if I hard code the program.cs in my webapi to ALWAYS set the Access-Control-Allow-Origin header the header is NOT set when calling the api with an OPTIONS/POST request.

So to rule out that there is some firewall "outside" of the server hosting the webapi when calling it I opened a remote desktop to the server and browse my web application locally. This should make the scenario "the same" as when I'm on my development machine. Unfortunately exactly the same thing happens. All types of calls except GET are "blocked by cors policy". This is driving me crazy, could it be any configuration on the web site that blocks some calls?

See hardcoded headers below that still don't help even though when running both my webapp and webapi on my development machine it works just fine:

 app.Use(async (ctx, next) =>
 {    
   var org = ctx.Request.Headers["Origin"];
   if (!string.IsNullOrWhiteSpace(org) && allowedOrigins.Contains(org))
   {
     ctx.Response.Headers.Append("Access-Control-Allow-Origin", org);
   }

   ctx.Response.Headers.Append("Access-Control-Allow-Credentials", "true");

   if (HttpMethods.IsOptions(ctx.Request.Method))
   {
     ctx.Response.Headers.Append("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS, PATCH");
     ctx.Response.Headers.Append("Access-Control-Allow-Headers", "content-type");
     await ctx.Response.CompleteAsync();
     return;
   }

   await next();
  });

I thought I understood how to configure cors for a .net framework webapi but I can't figure out this one.

I have a vue/vuetify website and a .net framework 8 webapi website running on the same server. Of course on my development computer there are errors as everything always works fine on the machines of developers.

However, when installing the websites on our test-server we get an error that seems to be cors-related. But all the "GET" calls work fine but no POST/PATCH requests work. The error logged in the console of Chrome is the following:

Access to XMLHttpRequest at 'https://crmappdev1:8050/api/generic/getAnyServiceCarisma' from origin 'https://crmappdev1:8051' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

When setting up CORS in my webapi I've been as least restricive as possible, allowing all methods and headers but still no success. The only difference I've noticed is that if I DON'T specify "AllowAnyMethod" then all calls fail but if I either say ".AllowAnyMethod" or specify which methods to allow then the "GET" start working but all other types fail. I've found a number of articles describing the order of how to chain the setup in the Program.cs for the webapi and I've tried everything but there is apparently something I'm missing. I would be most grateful for any suggestions to solve this.

The result of the POST in chrome network tab, as you can see it says "Unauthorized" and "OPTIONS" when executing the POST request which I think is a hint but I can't figure out what to make of it:

Result in chrome network tab

The current version of my Program.cs is below. Works fine on my local development machine but not when installed on the server:

public static void Main(string[] args)
{
 var builder = WebApplication.CreateBuilder(args);

 List<string> allowedOrigins = new List<string>
 {
    "http://localhost:8080",
    "http://localhost:8051",
    // DEV ENV
    "http://crmappdev1:8051",
    "https://crmappdev1:8051"        
};

builder.Services.AddCors(
    options =>
    {
        options.AddDefaultPolicy(
        policy =>
        {
            policy.WithOrigins(allowedOrigins.ToArray())
            //.AllowAnyMethod()
            //.WithMethods("GET", "PUT", "POST", "DELETE", "OPTIONS")
            //.AllowAnyHeader()
            //.AllowCredentials()
            ;
        }
);
    });

builder.Configuration
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
    .AddEnvironmentVariables();

// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();

builder.Services.AddAuthorization(options =>
{
    // By default, all incoming requests will be authorized according to the default policy.
    options.FallbackPolicy = options.DefaultPolicy;
});

builder.Services.AddMvc(option => option.EnableEndpointRouting = false).AddNewtonsoftJson();
var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();
app.UseCors(
builder =>
{
    builder
    .WithOrigins(allowedOrigins.ToArray())
    .AllowAnyHeader()
    .AllowCredentials()
    .WithMethods("GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH")
    //.AllowAnyMethod()
    ;
});

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
}

Solution

  • After reading "everything" about CORS and how to configure a WebApi to allow CORS under certain conditions I started thinking that maybe it was a problem on IIS level.

    So after finding this thread: Ajax CORS Request with http 401 in preflight I configured the web site for my WebAPI to allow "Anonymous Authentication" which finally made the OPTIONS request pass without the "401 Unauthorized". To further enhance security I added the "Authorization Rule" to apply to "All anonymous users" and apply specifically for the "OPTIONS" verb.