preflight request in enabled CORS ASP.NET Web API 2 application with windows authentication

I have a Web API application which is used in our intranet. We also used windows authentication for define a limitation on who can access to these Web APIs in the Intranet.

In addition, We have an angularJS application. It uses the WEB API application. the domain of these applications are different. Hence we used CORS.

for enabling CORS, we used Microsoft CORS Nuget packages.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {

        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        var cors = new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true };
        config.EnableCors(cors);
    }
}

I have two WEB APIs ( GET and POST). When my UI is loaded the GET API is called. When user click on save button the POST API is called.

[RoutePrefix("api/call")]
[Authorize]
public class CallController : TecApiController
{
    [HttpGet]
    [Route("GetInfo")]
    public IHttpActionResult GetCallInfo(int Id)
    {
       //return Model
    }

    [HttpPost]
    [Route("saveInfo")]
    public IHttpActionResult SetUncompleteCallToDismissed([FromBody] Model model)
    {
        // Save Model
    }
}

my AngularJS service for calling API is like following

$http.get(apiEndPoint + 'GetInfo/' + Id, { withCredentials: true })
            .then(function (response) { return response.data; },    handleError);

$http.post(apiEndPoint + 'saveInfo/', model, { withCredentials: true })
            .then(function (response) { return response.data; }, handleError);

My problem is GET method is work well. But when I click on the post I got following error in my browser console:

XMLHttpRequest cannot load http://localhost:35059/api/call/saveInfo. 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:3000‘ is therefore not allowed access. The response had HTTP status code 401.

The most wired things which happened here is when I run Fiddler for debugging the request, everything becomes fine.

please advice me how I can fixed it.

Answer

Ok, for someone with same problem.

When you enable windows authentication you cannot use * for origin. you have to specify the origin.

in my case, define following part in WebApiConfig.cs

 var cors = new EnableCorsAttribute("http://IP:PORT", "*", "*") { SupportsCredentials = true };
            config.EnableCors(cors);

The most important part is you have to handle preflight request. I choose Global.asax.cs(not the best choice, but the most starlight forward one) for handling these kind of requests

protected void Application_BeginRequest()
        {
            if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")
            {
                Response.Headers.Add("Access-Control-Allow-Origin", "http://IP:PORT");
                Response.Headers.Add("Access-Control-Allow-Headers", "Origin, Content-Type, X-Auth-Token");
                Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
                Response.Headers.Add("Access-Control-Allow-Credentials", "true");
                Response.Headers.Add("Access-Control-Max-Age", "1728000");
                Response.End();
            }
        }

Response.Headers.Add("Access-Control-Allow-Credentials", "true"); this part is mandatory if you have windows authentication in your web api.

Last thing ( and most annoying things ) is you cannot use Route and RoutePrefix attributes in your ApiController. you have to define all the route in WebApiConfig.cs, because route attributes changes the request’s header and remove all headers which is required for windows authentications and CORS.

config.Routes.MapHttpRoute(
                    name: "GetInfo",
                    routeTemplate: "api/call/getinfo",
                    defaults: new { action = "GetCallInfo", controller = "Call" }
                );