401 response for CORS request in IIS with Windows Auth enabled

Iis 7.5Windows Authenticationasp.net Web-ApiNtlmCors

Iis 7.5 Problem Overview


I'm trying to enable CORS support in my WebAPI project, and if I enable Anonymous Authentication then everything works fine, but with Windows Auth + disabled anonymous authentication, the OPTIONS request sent always returns a 401 unauthorized response. The site requesting it is on the DOMAIN so should be able to make the call, is there any way to get around the issue without disabling Windows Authentication?

Iis 7.5 Solutions


Solution 1 - Iis 7.5

You can allow only OPTIONS verb for anonymous users.

<system.web>
  <authentication mode="Windows" />
    <authorization>
      <allow verbs="OPTIONS" users="*"/>
      <deny users="?" />
  </authorization>
</system.web>

According W3C specifications, browser excludes user credentials from CORS preflight: https://dvcs.w3.org/hg/cors/raw-file/tip/Overview.html#preflight-request

Solution 2 - Iis 7.5

Several years later, but through the answer from @dariusriggins and @lex-li I have managed to add the following code to my Global.asax:

    public void Application_BeginRequest(object sender, EventArgs e)
    {
        string httpOrigin = Request.Params["HTTP_ORIGIN"];
        if (httpOrigin == null) httpOrigin = "*";
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", httpOrigin);
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Token");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");

        if (Request.HttpMethod == "OPTIONS")
        {
            HttpContext.Current.Response.StatusCode = 200;
            var httpApplication = sender as HttpApplication;
            httpApplication.CompleteRequest();
        }
    }

the httpOrigin is actually looked up in a list of allowed hosts but that just complicated things. This means that all other requests are validated but options just returns.

Thanks for this question, I would have been lost without it!

Solution 3 - Iis 7.5

From MS:

If you disable anonymous authentication, it’s by design that IIS would return a 401 to any request. If they have enabled Windows auth, the 401 response in that case would have a WWW-Authenticate header to allow the client to start an authentication handshake. The question then becomes whether the client that the customer is using can do Windows authentication or not.

Finally, it seems like there might be an underlying question about whether it’s possible or not to configure a URL such that anonymous access is allowed for one verb (OPTIONS, in this case), but require Windows authentication for other verbs. IIS does not support this through simple configuration. It might be possible to get this behavior by enabling both Anonymous and Windows authentication, setting ACLs on the content that deny access to the anonymous user, and then configuring the handler mapping for the URL in question so that it does not verify the existence of the file associated with the URL. But it would take some playing with it to confirm this.

Solution 4 - Iis 7.5

The easiest way to fix this is to create a rewrite rule with the condition request_method = ^OPTIONS$. Then set the action to be a custom response, set that to 200 OK. Then all options requests will respond with 200 instead of 401. This will fix the CORS issue.

Of course you still need to make sure you have the correct cross origin request headers.

This will stop options requests (which dont have any credentials) responding with 401 when integrated auth is enabled.

Solution 5 - Iis 7.5

The accepted answer is correct however I was troubleshooting a rest api with a "node with iisnode and npm cors module" setup for a while and was not comfortable with just enabling anonymous authentication for all users. Since its a node application the system.web tag does not do much. I ended up with the following addition to the web.config:

<system.webServer>
<security>
  <requestFiltering>
    <hiddenSegments>
      <add segment="node_modules" />
    </hiddenSegments>
  </requestFiltering>
  <authorization>
    <add accessType="Allow" verbs="OPTIONS" users="?" />
    <add accessType="Deny" verbs="GET, PUT, POST, DELETE" users="?" />
  </authorization>
</security>
</system.webServer>

Solution 6 - Iis 7.5

Extending the answer provided by @dariusriggins. Check this post: Microsoft | Developer: Putting it all together – CORS tutorial

For IIS Configurations:

Authorization rule

Authorization rule

Authorization stage (or Authorization event), we need to make sure we only allow the anonymous requests from CORS preflight and require all other incoming requests have authentication credentials supplied. We can achieve this through Authorization Rules. A default authorization rule granting all users access to the site is already in place and supplied by default by IIS. We will start by modifying this rule to only allow anonymous users, if they send requests that are using the OPTIONS http verb. Below is the target configuration in IIS for this authorization rule:

Edit Authorization rule

Edit Authorization rule

Solution 7 - Iis 7.5

Related Question: IIS hijacks CORS Preflight OPTIONS request

Merging info from answers found in multiple places. If you need to enable CORS on a ASP.net page method with Windows Authentication on the intranet, this is what seems to work. Without the changes to web.config, this doesn't work.

You need to add this to Global.asax

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        string httpOrigin = HttpContext.Current.Request.Params["HTTP_ORIGIN"] ?? HttpContext.Current.Request.Params["ORIGIN"] ?? "*";
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", httpOrigin);
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Token");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Credentials", "true");

        if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
        {
            HttpContext.Current.Response.StatusCode = 200;
            var httpApplication = sender as HttpApplication;
            httpApplication.CompleteRequest();
        }
    }

And this to web.config

 <system.webServer>
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." 
           verb="*" type="System.Web.Handlers.TransferRequestHandler" 
           preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>

Solution 8 - Iis 7.5

I've run into same issue today due to bug in IE 10 and 11, I'm using ServiceStack instead of WebApi, but the approach can work for you as well.

  1. Enabled Windows Integrated and Anonymous Authentication on IIS Web Site.
  2. Have a series of filters on the ServiceStack Pipeline,
    • For handling Cors and OPTIONS request, On Options request, I add necessary headers and end the request,
    • Filter for checking includng HttpRequest is Authenticated?,
    • etc filter,

After passing through all the filters, it executes the service.

CorsFeature.cs

AuthenticateFilter

In my AppHost,

appHost.Plugins.Add(new CorsFeature());

appHost.RequestFilters.Add(AuthenticateFilter.Authenticate);

I have modified the CorsFeature to handle OptionsRequest's in addition to adding headers, Authenticate Filter to check for requests authenticated!

Solution 9 - Iis 7.5

What worked for me (when working with AngularJS or JQuery) is to add withCredentials:true to each request on client:

$http.get("http://localhost:88/api/tests", {withCredentials :true})

And enabling CORS on server, this was done with Microsoft.Owin.Cors from nuget and adding it in Startup like below:

public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

        ConfigureOAuth(app);

        WebApiConfig.Register(config);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);

    }

References:

Solution 10 - Iis 7.5

I'm using Web API and OWIN and I tried every suggested solution but the only thing that worked was the following

//use it in your startup class
app.Use((context, next) =>
{
    if (context.Request.Headers.Any(k => k.Key.Contains("Origin")) && context.Request.Method == "OPTIONS")
    {
        context.Response.StatusCode = 200;
        context.Response.Headers.Add("Access-Control-Allow-Origin", new string[1] { "ALLOWED_ORIGIN" });
        context.Response.Headers.Add("Access-Control-Allow-Headers", new string[4] { "Origin", "X-Requested-With", "Content-Type", "Accept" });
        context.Response.Headers.Add("Access-Control-Allow-Methods", new string[5] { "GET", "POST", "PUT", "DELETE", "OPTIONS" });
        context.Response.Headers.Add("Access-Control-Allow-Credentials", new string[1] { "true" });
        
        return context.Response.WriteAsync("");
    }

    return next.Invoke();
});

//this is important! Without it, it didn't work (probably because the middleware was too late)
app.UseStageMarker(PipelineStage.Authenticate);

you need to insert this code somewhere in one of your OWIN startup classes. It's important to call app.UseStageMarker(PipelineStage.Authenticate) because otherwise the preflight check failed. Further infos for UseStageMarker -> https://docs.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-middleware-in-the-iis-integrated-pipeline

It's also important that you need to explicitly define the allowed headers. It will fail if you use * as a placeholder.

Maybe it helps somebody.

Solution 11 - Iis 7.5

I understand this is an old question with several possible solutions (as well as more questions), but in case anyone else comes across this, IIS CORS 1.0 is available as of Nov '17:

https://blogs.iis.net/iisteam/introducing-iis-cors-1-0

https://docs.microsoft.com/en-us/iis/extensions/cors-module/cors-module-configuration-reference

You can download it through IIS Windows Platform Installer (WPI). This should resolve many of your CORS authentication issues. Enjoy!

Solution 12 - Iis 7.5

This IIS extension (IIS CORS Module) helped me to solve the 401-Unauthorized preflight request to an IIS-hosted app with Windows Authentication enabled. After installing this module I did IISRESET and in the Web.config file of my web-application I added the following:

<configuration>
	<configSections>
		<!-- ... (configSections must be the first element!) -->
	</configSections>
	<system.webServer>
		<cors enabled="true">
			<add origin="http://localhost:3000" allowCredentials="true" maxAge="120">
				<allowHeaders allowAllRequestedHeaders="true"/>
				<!-- Added 'allowMethods' just in case. -->
				<allowMethods>
					<add method="HEAD"/>
					<add method="GET"/>
					<add method="POST"/>
					<add method="PUT"/>
					<add method="DELETE"/>
					<add method="OPTIONS"/>
				</allowMethods>
			</add>
		</cors>
	</system.webServer>
</configuration>

Here you can find more information on how to configure the IIS CORS Module: Getting started with the IIS CORS Module.

Solution 13 - Iis 7.5

public static void Register(HttpConfiguration config)
{
    var cors = new EnableCorsAttribute("*", "*", "*");
    cors.SupportsCredentials = true;
    config.EnableCors(cors);
}
  • add this lines to your web.config:

more info. in this article.

  • add xhrFields: {withCredentials: true} to your ajax call.

Solution 14 - Iis 7.5

Enabling SupportCredentials on EnableCorsAttribute in WebApiConfig.cs did the trick for me:

public static void Register(HttpConfiguration config)
{        
    //enable cors request just from localhost:15136 
    var cors = new EnableCorsAttribute("http://localhost:15136", "*", "*");
    cors.SupportsCredentials = true;
    config.EnableCors(cors);

    //other stuff
}

https://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Make sure you send credentials when calling from javascript ({withCredentials :true})

Recommended Ntlm Solutions

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondariusrigginsView Question on Stackoverflow
Solution 1 - Iis 7.5Jan RemundaView Answer on Stackoverflow
Solution 2 - Iis 7.5StuView Answer on Stackoverflow
Solution 3 - Iis 7.5dariusrigginsView Answer on Stackoverflow
Solution 4 - Iis 7.5Luke BasilView Answer on Stackoverflow
Solution 5 - Iis 7.5mrplatinaView Answer on Stackoverflow
Solution 6 - Iis 7.5Subodh WasankarView Answer on Stackoverflow
Solution 7 - Iis 7.5ShiroyView Answer on Stackoverflow
Solution 8 - Iis 7.5Sathish NagaView Answer on Stackoverflow
Solution 9 - Iis 7.5Philip PatrickView Answer on Stackoverflow
Solution 10 - Iis 7.5ArikaelView Answer on Stackoverflow
Solution 11 - Iis 7.5Inphinite PhractalsView Answer on Stackoverflow
Solution 12 - Iis 7.5Danik-AView Answer on Stackoverflow
Solution 13 - Iis 7.5Stas Steve LimanView Answer on Stackoverflow
Solution 14 - Iis 7.5Miroslav AdamecView Answer on Stackoverflow