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?
13 Answers
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
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!
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.
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.
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>
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>
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.
- Enabled Windows Integrated and Anonymous Authentication on IIS Web Site.
- 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.
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!
Extending the answer provided by @dariusriggins. Check this post: Microsoft | Developer: Putting it all together – CORS tutorial
For IIS Configurations:
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
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:
- CORS on server (see step 11): http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
- CORS on client: http://brockallen.com/2012/12/15/cors-and-windows-authentication/
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.
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!
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.
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}
)