I have a WebAPI 2 REST service running with Windows Authentication. It is hosted separately from the website, so I've enabled CORS using the ASP.NET CORS NuGet package. My client site is using AngularJS.
So far, here's what I've been through:
- I didn't have withCredentials set, so the CORS requests were returning a 401. Resolved by adding withCredentials to my $httpProvider config.
- Next, I had set my EnableCorsAttribute with a wildcard origin, which isn't allowed when using credentials. Resolved by setting the explicit list of origins.
- This enabled my GET requests to succeed, but my POST issued a preflight request, and I hadn't created any controller actions to support the OPTIONS verb. To resolve this, I've implemented a MessageHandler as a global OPTIONS handler. It simply returns 200 for any OPTIONS request. I know this isn't perfect, but works for now, in Fiddler.
Where I'm stuck - my Angular preflight calls aren't including the credentials. According to this answer, this is by design, as OPTIONS requests are designed to be anonymous. However, the Windows Authentication is stopping the request with a 401.
I've tried putting the [AllowAnonymous] attribute on my MessageHandler. On my dev computer, it works - OPTIONS verbs do not require authentication, but other verbs do. When I build and deploy to the test server, though, I am continuing to get a 401 on my OPTIONS request.
Is it possible to apply [AllowAnonymous] on my MessageHandler when using Windows Authentication? If so, any guidance on how to do so? Or is this the wrong rabbit hole, and I should be looking at a different approach?
UPDATE: I was able to get it to work by setting both Windows Authentication and Anonymous Authentication on the site in IIS. This caused everything to allow anonymous, so I've added a global filter of Authorize, while retaining the AllowAnonymous on my MessageHandler.
However, this feels like a hack...I've always understood that only one authentication method should be used (no mixed). If anyone has a better approach, I'd appreciate hearing about it.
Dave,
After playing around with the CORS package, this is what caused it to work for me: [EnableCors(origins: "", headers: "", methods: "*", SupportsCredentials=true)]
I had to enable SupportsCredentials=true. Origins,Headers, and Methods are all set to "*"
This is my solution.
Global.asax*
I have struggled for a while to make CORS requests work within the following constraints (very similar to those of the OP's):
My final configuration is the following:
web.config - allow unauthenticated (anonymous) preflight requests (OPTIONS)
global.asax.cs - properly reply with headers that allow caller from another domain to receive data
CORS enabling
This is a much simpler solution -- a few lines of code to allow all "OPTIONS" requests to effectively impersonate the app pool account. You can keep Anonymous turned Off, and configure CORS policies per normal practices, but then add the following to your global.asax.cs:
Other solutions I found on the web didn't work for me or seemed too hacky; in the end I came up with a simpler and working solution:
web.config:
Project properties:
Windows Authentication
Anonymous Authentication
Setup CORS:
This requires Microsoft.Owin.Cors assembly that is avaliable on NUget.
Angular initialization:
In our situation:
we found that the solution was elsewhere:
In Web.Config all we had to do was to add runAllManagedModulesForAllRequests=true
We ended up to this solution by looking into a solution on why the Application_BeginRequest was not being triggered.
The other configurations that we had:
in Web.Config
in WebApiConfig
BTW "*" cors origin is not compatible with Windows Authentication / SupportCredentials = true
https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#pass-credentials-in-cross-origin-requests