skip to Main Content

I have an ASP.NET website (not a web app) that makes an Ajax POST call using JQuery 2.1.4 to an ASP.NET Core 6.0 Web API.

When I make the call to the method I am getting a CORS error. Yet, when I make a GET Ajax request to a different method in the same controller, the request is successful.

My Ajax POST:

function insertLNItemStorageRequirement() {
  var tempLNStorage = {
    TItem: storageTempReq.t_item,
    TSrq1: storageTempReq.t_srq1,
    TSrq2: storageTempReq.t_srq2,
    TSq27: storageTempReq.t_sq27,
    TStmp: storageTempReq.t_stmp,
    TRcdVers: 0,
    TRefcntd: 0,
    TRefcntu: 0,
  };

  $.ajax({
    type: "POST",
    url: commonAPIURL + "api/LN/InsertItemStorageRequirement",
    data: JSON.stringify(tempLNStorage),
    contentType: "application/json; charset=utf-8",
    //dataType: "json",
    xhrFields: { withCredentials: true },
    success: function (response) {},
    failure: function (response) {
      alert(response.responseText);
    },
    error: function (response) {
      alert(response.responseText);
    },
  });
}

Here is the ASP.NET Core Web API method being called:

[HttpPost("InsertItemStorageRequirement")]
[Produces(typeof(IActionResult))]
public IActionResult InsertItemStorageRequirement([FromBody] Ttccgs016424 itemStorageReq)
{
    Ttccgs016424 newItemStorageReq = _LN.Ttccgs016424s.FirstOrDefault(s => s.TItem == itemStorageReq.TItem);

    if (newItemStorageReq == null)
    {
        newItemStorageReq = new Ttccgs016424()
        {
            TItem = itemStorageReq.TItem,
            TSrq1 = itemStorageReq.TSrq1,
            TSrq2 = itemStorageReq.TSrq2,
            TSq27 = itemStorageReq.TSq27,
            TStmp = itemStorageReq.TStmp,
            TRcdVers = itemStorageReq.TRcdVers,
            TRefcntd = itemStorageReq.TRefcntd,
            TRefcntu = itemStorageReq.TRefcntu
        };

        try
        {
            _LN.Ttccgs016424s.Add(newItemStorageReq);
            _LN.SaveChanges();
        }
        catch (Exception ex)
        {
            return BadRequest(ex.Message);  
        }

        return StatusCode(StatusCodes.Status201Created);
    }
    else
    {
        return StatusCode(StatusCodes.Status412PreconditionFailed, "Item Storage Requirement already exists.");
    }
}

Here is my CORS configuration in my startup.cs for the API (note that my origin is part of the origins array):

services.AddCors(setup => {
    setup.DefaultPolicyName = "open";
    setup.AddDefaultPolicy(p => {
        p.AllowAnyHeader();
        p.WithMethods("OPTIONS", "GET", "POST", "PUT", "DELETE");
        p.WithOrigins(origins);
        p.AllowCredentials();
     });
});

Request headers sent:

OPTIONS /api/LN/InsertItemStorageRequirement HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Connection: keep-alive
Host: localhost:31227
Origin: http://localhost:14612
Referer: http://localhost:14612/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36 Edg/128.0.0.0

Response:

HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Wed, 11 Sep 2024 23:43:25 GMT
Content-Length: 5995

I’ve tried changing the AJAX call to use credentials: 'include'.

And tried changed the

data: '{itemStorageReq: "' + JSON.stringify(tempLNStorage) + '"}'

Neither have resulted in a successful POST to the API.

GET requests to a different method in the same controller of the API are successful.

I suspect that this isn’t truly a CORS issue, but rather a data mismatch between what the Ajax is sending and what the API is expecting.

Any suggestions on how to troubleshoot this issue?

Update
IIS has the CORs module installed:
Picture of IIS modules on the REST API

Update 2 9/23/24

Moved my app.UseCors() to between app.UseRouting() and app.UseEndpoints() before calling authenticate and authorize in the configure() of startup.cs.

app.UseRouting();

app.UseCors("open");

app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
        endpoints.MapControllers();
});

Also, I am using endpoint routing, which according to MS, means that the CORS Module won’t automatically respond to OPTIONS requests: https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-6.0#httpoptions-attribute-for-preflight-requests.

In the same link MS recommends writing code in your endpoint to authorize the OPTIONS. So something like this?

[HttpOptions("InsertItemStorageRequirement")]
public IActionResult PreFlightRoute() {
       return NoContent();
}

2

Answers


  1. Chosen as BEST ANSWER

    I ended up modifying permissions on IIS according to this tutorial: https://techcommunity.microsoft.com/t5/iis-support-blog/putting-it-all-together-cors-tutorial/ba-p/775331.

    Enabled anonymous for the API. Then added authorization rules to limit Anonymous requests to OPTIONS verbs only. Finally, I added another authorization rule to allow all users from the 'USERS' group.

    The article above is dated, describing these changes as being superseded by the IIS CORS module, which has been implemented on my API since the beginning, but which failed to act on OPTIONS sent to the API. The CORS module, with a properly configured pipeline, should intercept OPTIONS requests without having to enable Anonymous Authentication for the entire API.

    I did follow the MS advice for properly arranging middleware in a .NET Core app: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-6.0#middleware-order-1, but it did not result in a successful POST request for me.

    I also had attempted to implement these suggestions from MS for when you are using endpoint routing (which I am): https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-6.0#httpoptions-attribute-for-preflight-requests. They also did not allow OPTIONS requests to pass.


  2. The error message you shared contains WWW-Authenticate: NTLM so that the preflight request which doesn’t contains windows auth credential shall be blocked and get 401 error.

    A CORS preflight request is used to determine whether the resource
    being requested is set to be shared across origins by the
    server…..The OPTIONS requests are always anonymous, so CORS module
    provides IIS servers a way to correctly respond to the preflight
    request even if anonymous authentification needs to be disabled
    server-wise

    Since the preflight request can’t get the desired CORS policy back, the POST request gets CORS error is the expected behavior. Similar to this ticket.

    If you are working locally, the workaround is allowing anonymous. In your API project launchsettings.json, you might need to set "anonymousAuthentication": true like below. And if you are hosting your app in IIS now, you can use CORS module just like the document I shared above said.

    {
      "iisSettings": {
        "windowsAuthentication": true,
        "anonymousAuthentication": true,
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search