CORS support in WebAPI, MVC and IIS with Thinktecture.IdentityModel
My second contribution to the Thinktecture.IdentityModel security library is a full-featured CORS implementation. Many other sample implementations only emit the Access-Control-Allow-Origin header, but there’s more to it than that. The implementation in Thinktecture.IdentityModel follows the W3C Working Draft 3 from April 2012. There is a rich configuration API to control the various settings that are involved with CORS. These settings include which resource you want to configure, which origins are allowed, which HTTP methods are allowed, which request and/or response headers are allowed and are cookies allowed.
In this first release there is support for WebAPI, ASP.NET MVC and IIS. For WebAPI you configure your settings per controller. For MVC you can configure the settings per controller or for specific controller actions. For IIS you configure the settings per URL. If there’s enough interest, then perhaps in a future version I can add support for WCF REST and WCF Data Services.
I won’t bother explaining CORS since there are already enough posts on it elsewhere. Instead I’ll just show how to get started with the library. First, reference the NuGet package. Next, depending on the type of application (WebAPI, MVC or IIS) you need to configure how you want CORS support. Below shows each of the different environments:
WebAPI
In WebAPI the implementation is a delegating handler. This allows the CORS settings to be global or per-route (which is forthcoming post-RC). For example if you were to configure it globally then in global.asax‘s Application_Start you would have a call out to the configuration class passing the global HttpConfiguration object (this follows the new style of factoring out configuration to separate classes in the App_Start folder):
protected void Application_Start() { ... CorsConfig.RegisterCors(GlobalConfiguration.Configuration); }
And then in App_Start/CorsConfig.cs:
public class CorsConfig { public static void RegisterCors(HttpConfiguration httpConfig) { WebApiCorsConfiguration corsConfig = newWebApiCorsConfiguration(); // this adds the CorsMessageHandler to the HttpConfiguration's // MessageHandlers collection corsConfig.RegisterGlobal(httpConfig); // this allow all CORS requests to the Products controller // from the http://foo.com origin. corsConfig .ForResources("Products") .ForOrigins("http://foo.com") .AllowAll(); } }
In WebAPI resources are identified by the controller name as in the above example for the “Products” controller.
MVC
In MVC you need to register a HttpModule to enable CORS support, so in web.config:
<system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="MvcCorsHttpModule" type="Thinktecture.IdentityModel.Http.Cors.Mvc.MvcCorsHttpModule"/> </modules> </system.webServer>
And then again in global.asax you would configure the settings:
protected void Application_Start() { ... RegisterCors(MvcCorsConfiguration.Configuration); } private void RegisterCors(MvcCorsConfiguration corsConfig) { corsConfig .ForResources("Products.GetProducts") .ForOrigins("http://foo.com") .AllowAll(); }
In MVC resources can either be identified just by the controller name (with just “Controller” for the resource name) or by the controller and action (as with the above sample with the “Controller.Action” syntax).
IIS
In IIS you need to register a HttpModule (different than the one for MVC), so in web.config:
<system.webServer> <modules> <add name="CorsHttpModule" type="Thinktecture.IdentityModel.Http.Cors.IIS.CorsHttpModule"/> </modules> </system.webServer>
And then again in global.asax you would configure the settings:
protected void Application_Start(object sender, EventArgs e) { ... ConfigureCors(UrlBasedCorsConfiguration.Configuration); } void ConfigureCors(CorsConfiguration corsConfig) { corsConfig .ForResources("~/Handler1.ashx") .ForOrigins("http://foo.com", "http://bar.com") .AllowAll(); }
In IIS resources are identified by the application relative path (thus the “~/path/resource” syntax).
Other Configuration Options
While the above samples show a minimal amount of code to get CORS enabled and running in your app, these are some of the least restrictive settings. Typically more thought should go into the settings and so there is a rich API for configuring the various CORS settings. Here are some more examples:
public static void ConfigureCors(CorsConfiguration corsConfig) { // this allows http://foo.com to do GET or POST on Values1 controller corsConfig .ForResources("Values1") .ForOrigins("http://foo.com") .AllowMethods("GET", "POST"); // this allows http://foo.com to do GET and POST, pass cookies and // read the Foo response header on Values2 controller corsConfig .ForResources("Values2") .ForOrigins("http://foo.com") .AllowMethods("GET", "POST") .AllowCookies() .AllowResponseHeaders("Foo"); // this allows http://foo.com and http://foo.com to do GET, POST, // and PUT and pass the Content-Type header to Values3 controller corsConfig .ForResources("Values3") .ForOrigins("http://foo.com", "http://bar.com") .AllowMethods("GET", "POST", "PUT") .AllowRequestHeaders("Content-Type"); // this allows http://foo.com to use any method, pass cookies, and // pass the Content-Type, Foo and Authorization headers, and read // the Foo response header for Values4 and Values5 controllers corsConfig .ForResources("Values4", "Values5") .ForOrigins("http://foo.com") .AllowAllMethods() .AllowCookies() .AllowRequestHeaders("Content-Type", "Foo", "Authorization") .AllowResponseHeaders("Foo"); // this allows all methods and all request headers (but no cookies) // from all origins to Values6 controller corsConfig .ForResources("Values6") .AllowAllOriginsAllMethodsAndAllRequestHeaders(); // this allows all methods (but no cookies or request headers) // from all origins to Values7 controller corsConfig .ForResources("Values7") .AllowAllOriginsAllMethods(); // this allows all CORS requests from origin http://bar.com // for all resources that have not been explicitly configured corsConfig .ForOrigins("http://bar.com") .AllowAll(); // this allows all CORS requests to all other resources that don’t // have an explicit configuration. This opens them to all origins, all // HTTP methods, all request headers and cookies. This is the API to use // to get started, but it’s a sledgehammer in the sense that *everything* // is wide-open. corsConfig.AllowAll(); }
Of course, feedback is welcome. Enjoy.
Edit: Common configuration issues when enabling CORS on IIS.
Trackbacks
- CORS support in WebAPI, MVC and IIS with Thinktecture.IdentityModel | www.leastprivilege.com
- ¿Están tus servicios REST en otro servidor? - Burbujas en .NET
- CORS open source contribution to ASP.NET and System.Web.Cors | brockallen
- Make AJAX requests between an ASP.NET Azure hosted Web Site and Web Role? - oneuptime | oneuptime
- Zangari Answers
- IIS / ASP.NET / CORS – Thinktecture.IdentityModel | Yourim.net
- CORS on ASP.NET – IndaSoft
THANK YOU
Very useful. Thank you!
Excellent
Implemented this yesterday after many other false positives – brilliant stuff. Incidentally if anyone runs into issues when deployed, try installing WIF.
Yep, Thinktecture.IdentityModel has a dependency on WIF for the claims related features, but CORS does not need it. I’m working on sorting this out. Thanks :)
Hi, I tried to implement this in a WebApi (to be honest, I copy-pasted your code) but it isn’t working. It keeps throwing an ArrayTypeMismatchException when I try to register the CORS msg handler (corsConfig.RegisterGlobal(httpConfig)). Any ideas? thanks!
I changed my target framework to .NET 4.0 and worked
Sorry Federico — I was on vacation so I didn’t respond to your messages. Glad you got it working.
I am having the same problem as Federico mentioned. But iwhen i change my target framework to .Net 4.0 my project doesn’t recognize Thinktecture dll and so it doesnt compile. I am using Vs 2012 with .Net 4.5 . My second question is do i still need to do httpmodule in IIS if i have done that in Webapi as suggested ?
Thanks
(corsConfig.RegisterGlobal(httpConfig)). This thing is still throwing exceptions related to ArrayTypeMismatchException . Any idea Thanks ?
I sent you an email asking for a sample that reproduces the problem.
Did this issue ever get resolved as I am experiencing it too. I am on the RTM release of .net 4.5. When the method RegisterGlobal is called in the WebApiCorsConfiguration class I get the following error:
Attempted to access an element as a type incompatible with the array.
public class CorsConfig
{
public static void RegisterGlobalCors(HttpConfiguration httpConfig)
{
var corsConfig = new WebApiCorsConfiguration();
corsConfig.RegisterGlobal(GlobalConfiguration.Configuration);
corsConfig.AllowAll();
}
}
Send me the smallest sample to repro and I’ll look into it. All of the others that expressed the same concern couldn’t repo. I suspect it’s some mismatch between the 4.0 and 4.5 projects.
I could solve this with a update of the reference assembly “System.Net.Http” to version 4. Also, in web.config :
just curious how did you update the reference to System.Net.Http to version 4? I am having the same problem here. Keep getting “Attempted to access an element as a type incompatible with the array” error
Sorry for posting to such an old thread but I had the same problem with the ArrayMismatch. The issue was I had an old reference to a .net assembly in my web.config. When I created a new webapi project and copied the 4 assembly references over into my broken project, the error went away. Below is a screen shot showing the difference.
Any plans to make the CORS support available as an independent NuGet package? It seems isolated enough that it may be nice to be able to consume it independently.
Thanks!
@Matt — Not currently, no. But you have the source, so feel free to build your own.
I’m sorry, but with preflight (OPTIONS) requests, it does not work. It does not set:
Access-Control-Allow-Headers:X-Requested-With
Access-Control-Allow-Origin:*
so the browser does not proceed to asking for the real resource.
Can this be solved? If so, how?
Thank you.
Email me with the details. Usually I find that the CORS library is working fine and that the problem is with configuring IIS.
I worked it out: simply use UrlBasedCorsConfiguration.Configuration to configure Cors on WebApi, and NOT MvcCorsConfiguration.Configuration, which I was using.
Hello Allen.
I’m trying to make this sample working (in thinktecture.Identitymodel WebApiSamples)
but althought corsConfig.AllowAll() is present, Authorization header did not sent
can you give me an advice?
Igor
If you can send me the smallest example that repros the issue, I will look at it. But what I’ve found with other people that have had this issue is that their web server has been blocking the headers or somehow they have other code that’s handling the OPTIONS request. So it’s usually a configuration problem.
Hi,
It works great, but i cannot access to the User.Identity property (because .ASPXAUTH cookie is http-only cookie and i suppose that xmlHttpRequest.withCredentials=true do not copy this cookie).
What do you suggest to do the authentication of the cors request and retrieve the User.Identity ? (i use FormsAuthentication).
@Alexandre CORS doesn’t do authentication so there’s no use identity that will be surfaced as a result of a successful CORS invocation. Also, the cookie option in CORS is not about sending a cookie from one origin to another. It’s indicating if a cookie from the target origin is even allowed to be sent back to the target origin.
Very interresting.. its been 10 hours im looking for a simple solution to this. Haven’t tried the solution but will like to hear user experience about using this solution. Do it fix the OPTIONS problems ive seen around ?
@brockallen : When you say “But what I’ve found with other people that have had this issue is that their web server has been blocking the headers or somehow they have other code that’s handling the OPTIONS request. So it’s usually a configuration problem.” could you explain it a bit more ?
Thanks! :)
@Rush — Yes :) The two things that I find that people didn’t or need to do are:
1) Uninstall WebDAV from IIS.
2) Make sure the ExtensionlessUrlHandler is configured to accept the OPTIONS method.
WebDAV wants to process OPTIONS requests, and it doesn’t know what to do for CORS. Also, the HTTP handler for .NET code (the extentionless handler) by default only allows GET, POST and HEAD (IIRC). So we want it to also process OPTIONS, so this needs to be configured.
I’m trying to get CORS working in a web API project I have. This seems to work on my local IIS8 (win8) IIS Express. The response to the OPTIONS returns the expected CORS headers. However when I publish this to AMZN ElasticBeanstalk which is running under IIS7.5 the responses don’t contain any of the CORS headers.
Also if I build a custom CORS message handler I get the same behavior, where locally it sends down the right headers, but on IIS7.5 it doesn’t event get to this code.
It’s as if IIS is responding to the OPTIONS request before my code can handle it.
Any ideas?
1) Uninstall WebDAV from IIS.
2) Make sure the ExtensionlessUrlHandler is configured to accept the OPTIONS method.
I was using this config: http://screencast.com/t/WYzf8tuIPiFk
Does this look good?
OPTIONS verb appears to be in the ExtentionlessUrlHandler list.
I don’t entirely understand the WebDAV part. But it in the /modules/remove part in the config.
I finally got this to work in IIS7.5. As you said, I had to remove the WebDAVModule in the web.config. The trick was I had to set runAllManagedModulesForAllRequests=true (other wise IIS still handled OPTIONS). Thanks much.
Here was my final config FYI:
http://screencast.com/t/SkyQIW7V9ltZ
@gmetzker — glad you got it working.
Is there some trick to getting this to send down the “Access-Control-Expose-Headers” for any of my custom headers that are in my WebAPI responses?
Looks like .AllowResponseHeaders(arrayOfCustomHeaders) sends down the Access-Control-Expose-Headers.
This is cool but it would be nice if it was something like “.AllowAnyResponseHeaders()” and then have it send down any of the non-“simple” headers that my web api is trying to send in the response.
@gmetzker — Yea, but the problem is that there could be an infinite number of headers, so it needs to be explicit. And, IIRC, there’s no “*” in the CORS spec for the Expose-Headers.
@gmetzker — Yep, it’s the AllowResponseHeaders API on the CorsConfiguration class(es).
I added a message handler that iterated over the response headers and just added them to the “Access-Control-Expose-Headers”. It seems like this would be possible as a fluent method on your config.
After several hours of researching, trouble shooting, cursing, and flipping off my code, I finally got my cross domain ajax DELETE submission to work using your approach outlined above.
I initially tried just the WebAPI approach, and it didn’t work. I then removed everything I was trying and followed the IIS approach and everything works. I’m not sure why. I only added this line of code to Web.Config and nothing else:
That’s the only configuration I did and I can do cross domain deletes. I’m assuming other verbs will work, I’ve only tested with Delete. So, I’m grateful that I can get my code to work, but am wondering why it’s working at all given that I only did part of the configuration as cited above.
I’m ajaxing a delete request to a WebAPI controller. So, I’m assuming the WebAPI approach above would have worked. I don’t understand why that single line of configuration in Web.Config and nothing else is now allowing me to do cross domain ajax delete requests in Firefox, Safari, and Chrome.
Thanks for this approach, but I really would appreciate insight into my configuration experience.
I’m not sure why my code snippet didn’t make my post, so here it is:
modules runAllManagedModulesForAllRequests=”true”
add name=”CorsHttpModule” type=”Thinktecture.IdentityModel.Http.Cors.IIS.CorsHttpModule”/
/modules
Could have been the “<".
@Tom — not sure why it wasn’t working. Unfortunately, using a CORS library like this only make doing it easier — I think you still have to have a good idea what CORS is and some of those details might have helped you debug it sooner. Anyway, glad you got it working.
Hi this is really awesome, thanks so much for you contribution! I’m having a problem where this works in the development environment (two different instances of VS2012 running IISExpress) but not in production. Note the following response in development environment when OPTIONS request is made:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/11.0.0.0
Date: Sat, 27 Oct 2012 22:23:36 GMT
X-AspNet-Version: 4.0.30319
Access-Control-Allow-Origin: http://localhost:51113
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: origin,accept,content-type
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Length: 0
Connection: Close
This is the response from production environment when OPTIONS request is made:
HTTP/1.1 200 OK
Allow: OPTIONS, TRACE, GET, HEAD, POST
Server: Microsoft-IIS/7.5
Public: OPTIONS, TRACE, GET, HEAD, POST
X-Powered-By: ASP.NET
Date: Sat, 27 Oct 2012 22:23:59 GMT
Content-Length: 0
This is inside my CorsConfig.cs within App_Start using WebAPI:
corsConfig.ForAllResources().ForAllOrigins().AllowAllMethods().AllowAll();
In addition to this we’d like to deploy to Azure so I was wondering if you were aware of any gotchas. I appreciate any help you can offer!
Giancarlo
This looks like some other piece in the pipeline is handling the OPTIONS request and not the CORS library. Check this post for common issues:
https://brockallen.com/2012/10/18/cors-iis-and-webdav/
Oh and for Azure — only thing I’d be worried about is the same thing — some other code in the pipeline thinking that it should handle the OPTIONS and not the CORS library.
I feel that this is the greatest thing since sliced bread. I have been fighting this problem for a couple days. I am using IIS 7.5, ASP.NET 4.5, and this is a WebApi project. I followed the directions for the WebApi setup and removed WebDAV as you suggested using the configuration shown by gmetzker here: http://screencast.com/t/SkyQIW7V9ltZ . It worked first time right out of the box. So huge thanks!
Many thousands thanks to you, Brock, for this excellent dll. I’ve been working non-stop for 2 days and nights on this cors/wcf/iis7.5 stuffs and about to give up. This worked right away. None of the many proposed solutions elsewhere ever worked 100%. Some worked with Chrome but failed in FF, etc.Some just broke my WCF app. You’re a life saver. God bless.
Hi Brock,
I’ve been struggling for a couple of days with getting CORS to work in my application.
I have a Web Application which contains both WebAPI controllers and MVC controllers.
I need to be able to fetch data from my Web API controllers and html from my MVC controllers and I need to be able to do so from another machine (on the same local network). I followed your instructions for both MVC and Web API but I am unable to call my controllers from a javascript context. I want to fetch data from my Web API controller by using $.ajax but when I do so I get an error saying Origin http:// is not allowed by Access-Control-Allow-Origin. Am I missing something?
The error message was cut… it’s Origin http://server1 is not allowed by Access-Control-Allow-Origin
@Tinebo — I’d suggest downloading the code from github and running the JavaScript unit tests to show that it works and illustrate a setup/configuration that you can then modify to suit your needs. If the unit tests don’t work, then it’s an environmental problem.
Hi Brock,
I’ve been playing with the sample ‘Client’ projects in the WebApiSecurity sln (from the web api sample folder) and can successfully run the ‘SessionTokenClient’ (http://localhost:XXXXX/SessionTokenClient.html) but when I try to run the ‘JavascriptClients’ (cross domain) I got the infamous
XMLHttpRequest cannot load https://localhost/webapisecurity/api/token?_=1354245524884. Origin http://localhost:XXXX is not allowed by Access-Control-Allow-Origin.
I am able to see via Fiddler that the token is being retrieved correctly:
{
“access_token”: “eyJ0eXAiOi…etc.”,
“expires_in”: 1354282143
}
but the ‘success’ handler (jquery ajax call)does not get triggered. Instead, it goes to the error handler with status ‘0’ and statusText = ‘error’
If I use ‘jsonp’ as a parameter (which I think it should not be necessary – the sample does not have it) to the ajax call, the error handler instead shows status 200 (success)… Chrome console shows ‘Uncaught SyntaxError: Unexpected token : ‘. Fiddler still shows the token being sent to the client.
I’ve tried all suggestion above removing the WebDAVModule, etc. but no luck. I have the default in my global.asax: corsConfig.AllowAll();
Not sure is the jquery part (since the DOS client works well) or some configuration
Would I be missing something obvious?
I’ll appreciate any feedback.
I have trouble making this work. I have detailed my issue here (http://stackoverflow.com/questions/13874472/asp-net-web-api-using-thinktecture-sessiontokenauthentication-returns-status-200).
The weird thing is I’m getting a response data, or so it seems like, checking the response with Fiddler. By the way I’m also using the Session token js example. So each time I submit a request I’m getting a status 200 but the ajax call falls on the error function. I set the configuration to AllowAll. Is there something that I missed?
I think it would be best to see the HTTP trace logs. Use fiddler and email them to me and I can try to help diagnose the problem.
The issue that Von and omexus describe is exactly what I’m getting too. Did anyone ever get this figured out?
I was able to make this work by making an adjustment to my js code. My code looks like this:
jQuery.support.cors = true; // this is what I added and everything worked fine
$.ajax({
url: APIUrl + ‘members/’,
type: ‘GET’,
dataType: ‘json’,
success: callback,
error: function(xhr, errorType, exception) {
}
});
Hi Brock, I’m trying to get CORS working in a web API. This seems to work for all requests on my local IIS7.5(windows7) with all browsers. However when I publish this to dev server iis7.5 (win server 2008 R2, It works for GET requests in all browsers, however OPTIONS request works only in IE8. When i tested in crome and safari, i’m getting the error “Origin http://server1 is not allowed by Access-Control-Allow-Origin” .
I tried using IIS approach too and noticed the same behavior. Any help would be really appreciated.
My config looks same as the one here http://content.screencast.com/users/gmetzker/folders/Jing/media/150aa71c-ec6c-4a97-ab0d-40b7260fcc9b/2012-10-17_2014.png
I tried Web API method and it didn’t work. So I went towards IIS route. Now, it works in my local host (Windows 8, VS 2012) but when I push it to prod (IIS 6), it doesn’t work. Is there other setting to make it work in IIS 6?
http://stackoverflow.com/questions/14248154/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel
I got CORS on both my WebApi and MVC controllers working, but now I want to add simple authenticaton via Forms authentication. I enabled cookies but it doesn’t seem to pass the formsauth cookie back. Is there a way to use this CORS library with forms authentication?
The scenario is a I have a widget on an external site that needs to authenticate and then stay logged in while the user can browse through the external site and make continuous CORS calls to my server.
I actually figured out cookie problem by adding xhrFields: { withCredentials: true} to my jquery call, Forms Authentication now works.
A new problem popped up. Enabling MVC config section breaks the WebApi config section. Once I turn on MVC corsConfig, all WebApi cors calls return a blank response.
Looking at FireBug I found that once both are enabled, WebAPI calls return the Access-Control-Allow headers 2 times in the response, is that by design?
Yea, if you’re doing both MVC and WebAPI then there could be a little problem. MVC is handled with a http module and WebAPI is done via a message handler, so for the WebAPI calls it’d hit both layers. This is a non-issue if you use my updated version that I contributed to Microsoft:
http://aspnetwebstack.codeplex.com/SourceControl/network/forks/BrockAllen/aspnetwebstack/contribution/3769
http://aspnetwebstack.codeplex.com/SourceControl/network/forks/BrockAllen/aspnetwebstack
This might be added into the ASP.NET framework in the future.
Hi brockallen, great job this stuff. I have one question – running WEB API from inside an MVC application, and the only way I seem to get the CORS working (for web api, don’t care about MVC now) is by adding the MVC module and allowing all – MvcCorsConfiguration.Configuration.AllowAll(); I can’t seem to find a way how to lock it down to a specific WEB API controller. The MVC configuration does not seem to work when I try to work on a specific resource and if I only use the webapi configuration, it does not work at all.
If you want Cors in MVC then do the MVC config and the http module, otherwise don’t configure those. From what you’re saying, it sounds like you shouldn’t use any of the MVC config.
For WebAPI you only need the snippet above that calls RegisterGlobal. This is implemented as a message handler, so make sure you configure it earlier than any other message handler (especially any authentication).
BTW, this sample uses Cors in WebAPI: https://github.com/thinktecture/Thinktecture.IdentityModel.45/tree/master/Samples/Web%20API%20Security
thanks. If I leave out MVC Cors config completely and put the Web Api Cors config right on top of Application_Start():
var corsConfig = new Thinktecture.IdentityModel.Http.Cors.WebApi.WebApiCorsConfiguration();
corsConfig.RegisterGlobal(GlobalConfiguration.Configuration);
corsConfig.AllowAll();
when I navigate to the website it gives me:
403 – Forbidden: Access is denied.
If I move it further down the line, cors does not work.
The example code that you’ve referenced (WebHost project, Global.asax) initializes CORS differently and has all sorts of configuration, seems more complex, should I be using that?
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.AllowAll();
var corsHandler = new Thinktecture.IdentityModel.Http.Cors.WebApi.CorsMessageHandler(corsConfig, config);
config.MessageHandlers.Add(corsHandler);
// authentication configuration for identity controller
var authentication = CreateAuthenticationConfiguration();
config.MessageHandlers.Add(new AuthenticationHandler(authentication));
…
No, I don’t have any more advice for you. You’ll have to debug your project to understand why you’re getting the behavior — there are many moving parts so it’s hard to say.
hello? any advice? :)
Could we get a strong named assembly in the NuGet package?
I’ll chat with Dom about this — I might be wrong, but my understanding is that this requires a new NuGet package (due to some structural issues with NuGet), so it’s not a mild undertaking.
I am using Cors for Web Api (WebApiCorsConfiguration). Everything is working great! However, I want to change configuration settings on the fly while my website is running, not only at startup (in Global.asax.cs Application_Start()). I want to do this because I want the “origin” values to be read from a database (which is also maintained through the website). After changes or additions are made, I want to update settings without restarting my website. I tried:
– Removing the CorsMessageHandler object by calling remove on MessageHandlers and passing in the current object (calling GlobalConfiguration.Configuration.MessageHandlers.Remove). I confirmed that the object is removed from the collection.
– Then creating a new WebApiCorsConfiguration and calling RegisterGlobal again. I confirmed that the CorsMessageHandler object has been added to the collection afterward:
corsConfig.RegisterGlobal(httpConfig);
corsConfig
.ForResources(“Products”)
.ForOrigins(“http://foo.com”)
.AllowAll();
This does not seem to work. A test call from a newly added origin fails. Any ideas? Also, is something being cached?
Thanks in advance.
You can register 2 different callbacks on the CorsConfiguration.
1) StaticConfigurationAccessFilter is called when there’s a match on a config entry that youv’e declared “statically” (meaning at app start). The callback passes you in the CorsAccessRequest and CorsConfigurationAllowProperties. The CorsConfigurationAllowProperties tells you what the static config says for the CORS outcome. You can then return the modified CorsConfigurationAllowProperties with and changes you want for the CORS outcome. Return null to prevent the CORS call.
2) CorsConfigurationAllowProperties is called when there is no match in the CorsConfiguration. The callback passes you in the CorsAccessRequest and you return a CorsConfigurationAllowProperties with your desired CORS outcome. Return null to prevent the CORS call.
I’m sure one of these will allow for your dynamic scenario.
Exactly what I was looking for. I implemented a callback for DynamicConfigurationCallback.
Thanks.
Allen. I’m new to CORS and I’m having a very difficult time making it work.
My main issue is using WebDAV and then going to IIS7. This works OK with IE but with FF and Chrome the PreFlight check bombs.
I’m using Thinktecture.IdentityModel. When I call the CORS from WebDAV the ACAO headers are there. But when I call the CORS from IIS7 they disappear.
Looking at this article I noticed that you have a section for IIS where I need to add a module. So I added the module for my CORS MVC 4 Web API app’s web.config. Where do I add the “CorsHttpModule” to IIS.
Any help would be very much appreciated.
I have successfully configured CORS for ASP.Net MVC webapi, but only http GET is working, for some reason the rest of the methods are not working.
Any ideas?
Best thing is to do http network traces to start to debug it.
I use http ‘GET’ and ‘POST’ are current working . It’s ok. but use http ‘delete’ doesn’t work.
please help me. thank you.
I fixed this a long time ago but I forgot to post the answer, the problem was not the library but the limitations of asp.net mvc web api, in my case I was not sending json in the ajax call but normal parameters.
Sorry I have a question? I use ajax to get server site data in cross domain.
It’s ok. but I change the ajax type,use ‘delete’ type can’t work,have some error.
http://XXXXXXX is not allowed by Access-Control-Allow-Origin.
Do you have any idea can suggest me? thank’s.
Yes, your server needs to implement CORS and allow the DELETE for the origin making the request.
The Web api server on global.asax . I write
Sub Application_Start()
…
CorsConfig.RegisterCors(GlobalConfiguration.Configuration)
End Sub
It is right?
hello brockallen , thank you for your reply. Can you descripe detail step?
thank’s a lot.
I would suggest to read https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS – especially Preflighted Requests – and debug with that knowledge.
Check the http requests and headers being sent and your IIS config.
Fantastic stuff, saved me from implementing this myself with filters, preflight conditionals and far, far more. I opted for a pass-thru security approach to start with, so configured my API using ‘corsConfig.AllowAll()’. The POST requests (jQuery AJAX, Postman, etc.) worked just fine, but the GETs return a 405.
Oddly enough, when I call those same methods via an http POST instead of a GET, they work as expected. Kind of hoping to use a GET for simple data retrieval though, however. My reference is for Thinktecture.identitityModel version 2.6.2.0, using .Net 4.0.
Anything configuration-wise I might have missed in order to enable http GETs?
Please disregard…found and fixed.
Hi!
I have a problem here.
I’ve installed NUGET, and Thinktecture on nuget.
I tried the example above. It works fine on build but still won’t allow access.
I debugged it.
Here’s my screenshot.
first:
http://prntscr.com/1c6al0
Then when i pressed F11 (Step Into)
http://prntscr.com/1c6ati
Please help me.
I’m confused. If you want to debug the source, then download it, build it and reference it yourself:
https://github.com/thinktecture/Thinktecture.IdentityModel.40/
If I end a WebAPI call like this:-
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
Content = new StringContent(“Invalid credentials”),
RequestMessage = Request
});
Then the WebAPI delegating handler seems to fail with :-
[ArgumentNullException: Value cannot be null.
Parameter name: httpContext]
System.Web.Http.WebHost.HttpControllerHandler.EndProcessRequest(IAsyncResult result) +159
System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +282
Disabling the Cors WebAPI configuration makes this issue go away? Is this caused somehow by me manually creating the HttpResponseMessage?
Dunno — I’d have to debug it. Have you tried using Request.CreateResponse(HttpStatusCode.Unauthorized, …)?
As far as I can see, you do not add Access-Control-Allow-Methods if the requested method is simple (“if (!accessRequest.RequestedMethod.IsSimpleMethod()) …” in AddAllowedMethods in Thinktecture.IdentityModel.40 / IdentityModel / Thinktecture.IdentityModel / Http / Cors / CorsEngine.c), ever, also not with pre-flight requests.
[https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS suggests] otherwise, under the “Preflighted requests” heading, doesn’t it?
The MDN docs are not quite correct and often misleading. My implementation follows the spec (well, at least it tries to).
It seems that by default, no Access-Control-Max-Age are returned on pre-flight?
What is the easiest way to set a way long Access-Control-Max-Age, with the “sledgehammer” corsConfig.AllowAll();
You can set the DefaultCacheDuration on the CorsConfiguration.
Your question galvanized me to add an API to set the cache duration on the fluent settings class (it was an oversight that there wasn’t one before). Thx.
Hello,
I was just wondering what the differences are between this implementation and the ASP.NET System.Web.Http.Cors implementation. I really like everything with claims this project has, curious to what else it can do. Thanks for all of your work with this.
The implementation in IdentityModel is meant to work for IIS, WebForms, MVC and WebAPI. The core CORS code in IdentityModel was contributed to the ASP.NET web stack open source project and made its way into the System.Web.Http.Cors implementation, but only provided support for WebAPI:
https://brockallen.com/2013/03/13/cors-open-source-contribution-to-asp-net-and-system-web-cors/
IdentityModel has many other features, yes:
http://thinktecture.github.io/Thinktecture.IdentityModel.45/
Awesome news! Thanks for doing this.
Paul
Indeed a very nice CORS library, you’ve got there! If you could make it in WCF context also, I would be more than happy! :)
Sorry, I don’t plan on touching WCF :/
Hi,
I’m having a problem with my web API. I can receive the Bearer Token but when sending the token in the header I get the error ‘origin not allowed’
Can you help please
I’d start by reading up on CORS:
http://msdn.microsoft.com/en-us/magazine/dn532203.aspx
Hi,
I have a minimal MVC app for which I’m trying to apply your Cors library. I’ve followed you MVC instructions adding your Thinktecture.IdentityModel.Http.Cors.Mvc.MvcCorsHttpModule to configuration and the corresponding Global.aspx.cs addition:
corsConfig
.ForResources(“Simple.Index”)
.ForAllOrigins()
.AllowAll();
But when I inspect response headers, I don’t see any of the Cors headers. I’m running in the context of developer IIS, but if I deploy on my IIS it’s the same result.
Perhaps this: https://brockallen.com/2012/10/18/cors-iis-and-webdav/?
Yes. That helped me a bit further. Thanks.
I’ve posted a question on Dec 2012 about a “200 OK” issue. I managed to solved that back then. Strangely, on a new project I’m getting that error again. I’ve compared the working and new projects and they both have the same set of configurations. I’ve used the AllowAll method so that I have this in my corsconfig: “corsConfig.AllowAll();”
The authentication is triggered like this:
$.ajax({
type: ‘GET’,
url: tokenEndpoint,
beforeSend: function (xhr) {
xhr.setRequestHeader(“Authorization”, “Basic ” + token);
},
success: function (result) {
console.log(result);
},
error: function (error) {
console.log(error);
}
});
Here’s the trace from Chrome:
Request URL: tokenEndPoint
Request Method: OPTIONS
Status Code: 200 OK
Request Headers
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Access-Control-Request-Headers: accept, authorization
Access-Control-Request-Method: GET
Connection: keep-alive
Host: hostURL
Origin: callingURL
Referer: callingURL/login
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64)
Response Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: accept,authorization
Access-Control-Allow-Origin: callingURL
Cache-Control: no-cache
Content-Length: 15
Content-Type: application/json; charset=utf-8
Date: Fri, 17 Jan 2014 09:38:18 GMT
Expires: -1
Pragma: no-cache
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
X-SourceFiles: xxxxx
Request URL: tokenEndPoint
Request Headers CAUTION: Provisional headers are shown.
Accept: application/json, text/javascript, */*; q=0.01
Authorization: Basic xxxxx
Origin: callingURL
Referer: callingURL/login
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) Safari/537.36
In Fiddler I can see that a token was returned:
{
“access_token”: “xxxxx”,
“expires_in”: xxxxx
}
However the ajax call fails as shown in the Console window with the following error: XMLHttpRequest cannot load {tokenEndPoint}. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘callingURL’ is therefore not allowed access.
I have debugged IdentityModel and I am sure the call made it through the API and to the IdentityModel SendAsync method that returns the response. Since that’s the case I am thinking that the issue is with the ajax settings? When I Googled the returned error the suggestions I read hinted it could be a server error but the results I posted above indicates that the headers were setup properly. Anything I missed?
Are you using the OWIN OAuth2 authorization server middleware? If so, then this makes sense because the token endpoint is in OWIN MW and not in MVC or WebAPI. You’d need to enable CORS at the OWIN level.
I’m still using VS2012 and WebAPI 1 so I don’t think I’m using OWIN. I also checked and confirmed that I have no reference to any Owin dlls so I’m sure I’m not using OWIN. Anything else I should check to resolve my issue? Btw, thanks for the quick reply.
Check here for all the little rules about CORS (I tried to document all the tricky things).
http://msdn.microsoft.com/en-us/magazine/dn532203.aspx
Also, run in chrome since it has great debug output when CORS goes wrong.
Hi Brock, the trace I posted above is from Chrome. I also used Fiddler to compare the results, not that it will show a different result, but it helped in knowing that I got a token returned from the server – as expected. I read your blog from the link you provided up to the last section “Debugging CORS”. Everything seems correct based on your blog and comparing what is shown in the trace I got. I still don’t get it why my browser complains of “No ‘Access-Control-Allow-Origin’ header is present on the requested resource.” when in the response headers it includes “Access-Control-Allow-Origin:callingURL”.
It’s weird that I got a result, as shown in Fiddler {“access_token”: “xxxxx”,“expires_in”: xxxxx} and yet my ajax call fails.
I saw your SO post and the person who answered is right — the actual GET request is missing the CORS response headers. That’s the thing to debug. Why isn’t the CORS library you’re using getting involved in the GET.
Thank you! Works great.
@brockallen Thanks a bunch for this, excellent and very usefull!
Are there any particular configurations, to configure it to accept requests from a set of subdomains? For example, my main domain is “contoso.com” and I want my service to accept requests from a set of subdomains, but not external requests like so:
data.contoso.com ->should be allowed
ui.contoso.com->should be allowed
contoso.com -> should be allowed
data.contoso2.com -> should be rejected.
Ideally I would like to set it to allow *.contoso.com, using a wildcard… Is this possible?
IIRC, I allowed for a callback to be invoked so you could check a DB or a pattern. It’s been while so I forget the property — look for something called “callback” or some such.
If the request is with in the same domain, its not adding the header. Is there any other way to resolve this problem?
We are requesting HTML page from domain A while being in the same domain and We requested another HMTL page from domain A to Domain B(cross domain) and merged the 2 html page in domain A.
Its throwing cross origin error Because the Allow Origin header is not added. this is because one request is from the same domain. Is there anyway to resolve the problem?
I don’t follow, sorry.
You’ll have to think about this from the browser’s perspective. Perhaps read this to learn more about how CORS works: https://msdn.microsoft.com/en-us/magazine/dn532203.aspx
If the request is from the same origin, there’s no need to send the header.
Thank you!! Works great!!!
Thanks!!!
I created an MVC Web Api which I am hosting in IIS. Which method should I follow? Thanks.
If you’re using Web API 2, then use the OWIN middleware.