Skip to content

CORS support in WebAPI, MVC and IIS with Thinktecture.IdentityModel

June 28, 2012

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.

125 Comments leave one →
  1. June 28, 2012 11:19 am

    THANK YOU

  2. Shildrak permalink
    June 29, 2012 5:33 am

    Very useful. Thank you!

  3. Markus permalink
    August 2, 2012 9:22 am

    Excellent

  4. August 3, 2012 5:50 am

    Implemented this yesterday after many other false positives – brilliant stuff. Incidentally if anyone runs into issues when deployed, try installing WIF.

    • August 3, 2012 8:52 am

      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 :)

  5. August 23, 2012 1:47 pm

    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!

    • August 24, 2012 12:21 pm

      I changed my target framework to .NET 4.0 and worked

      • August 24, 2012 5:51 pm

        Sorry Federico — I was on vacation so I didn’t respond to your messages. Glad you got it working.

  6. tabishsarwar permalink
    August 27, 2012 4:06 pm

    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

  7. tabishsarwar permalink
    August 28, 2012 1:32 pm

    (corsConfig.RegisterGlobal(httpConfig)). This thing is still throwing exceptions related to ArrayTypeMismatchException . Any idea Thanks ?

    • August 28, 2012 1:51 pm

      I sent you an email asking for a sample that reproduces the problem.

      • JJ Kennedy permalink
        August 30, 2012 11:56 pm

        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();

        }
        }

        • August 31, 2012 12:40 am

          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.

    • fbrem permalink
      October 2, 2012 7:58 am

      I could solve this with a update of the reference assembly “System.Net.Http” to version 4. Also, in web.config :

      • November 15, 2012 3:45 pm

        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

        • June 29, 2013 12:29 pm

          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.

  8. Matt permalink
    September 13, 2012 5:05 pm

    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!

    • September 14, 2012 7:48 pm

      @Matt — Not currently, no. But you have the source, so feel free to build your own.

  9. francesco permalink
    September 17, 2012 12:42 pm

    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.

    • September 17, 2012 2:16 pm

      Email me with the details. Usually I find that the CORS library is working fine and that the problem is with configuring IIS.

    • francesco permalink
      September 18, 2012 3:32 am

      I worked it out: simply use UrlBasedCorsConfiguration.Configuration to configure Cors on WebApi, and NOT MvcCorsConfiguration.Configuration, which I was using.

  10. Igor permalink
    September 21, 2012 10:08 am

    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

    • September 21, 2012 11:10 am

      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.

  11. Alexandre permalink
    September 28, 2012 5:33 am

    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).

    • October 18, 2012 11:56 am

      @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.

  12. Rush permalink
    October 4, 2012 8:30 am

    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! :)

    • October 18, 2012 11:58 am

      @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.

  13. October 17, 2012 9:33 pm

    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?

    • October 17, 2012 9:42 pm

      1) Uninstall WebDAV from IIS.
      2) Make sure the ExtensionlessUrlHandler is configured to accept the OPTIONS method.

      • October 17, 2012 9:59 pm

        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.

      • gmetzker permalink
        October 17, 2012 11:20 pm

        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.

      • gmetzker permalink
        October 17, 2012 11:21 pm

        Here was my final config FYI:

        http://screencast.com/t/SkyQIW7V9ltZ

    • October 18, 2012 11:59 am

      @gmetzker — glad you got it working.

  14. gmetzker permalink
    October 18, 2012 11:42 am

    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?

    • gmetzker permalink
      October 18, 2012 12:00 pm

      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.

      • October 18, 2012 12:05 pm

        @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.

    • October 18, 2012 12:03 pm

      @gmetzker — Yep, it’s the AllowResponseHeaders API on the CorsConfiguration class(es).

      • gmetzker permalink
        October 18, 2012 12:08 pm

        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.

  15. Tom permalink
    October 22, 2012 2:03 am

    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.

    • Tom permalink
      October 22, 2012 2:05 am

      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 “<".

    • October 22, 2012 7:53 am

      @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.

  16. Giancarlo permalink
    October 27, 2012 6:40 pm

    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

    • October 27, 2012 7:56 pm

      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:

      CORS, IIS and WebDAV

    • October 27, 2012 7:57 pm

      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.

  17. Bruce Callen permalink
    October 31, 2012 1:32 pm

    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!

  18. mosabusan permalink
    November 22, 2012 1:22 am

    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.

  19. November 23, 2012 7:36 am

    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?

    • November 23, 2012 7:37 am

      The error message was cut… it’s Origin http://server1 is not allowed by Access-Control-Allow-Origin

    • November 23, 2012 10:01 am

      @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.

  20. omexus permalink
    November 29, 2012 10:37 pm

    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.

  21. Von permalink
    December 14, 2012 11:06 am

    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?

    • December 15, 2012 5:07 pm

      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.

      • Josh permalink
        January 7, 2013 8:55 pm

        The issue that Von and omexus describe is exactly what I’m getting too. Did anyone ever get this figured out?

        • January 9, 2013 11:48 pm

          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) {

          }
          });

  22. January 4, 2013 4:04 pm

    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

  23. January 9, 2013 3:14 pm

    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?

  24. Melik permalink
    January 18, 2013 5:56 pm

    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.

  25. Stevo permalink
    February 26, 2013 5:04 pm

    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.

    • February 26, 2013 6:24 pm

      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

      • Stevo permalink
        February 27, 2013 3:58 pm

        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));

        • March 2, 2013 11:43 am

          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.

      • Stevo permalink
        March 2, 2013 10:25 am

        hello? any advice? :)

  26. March 27, 2013 2:07 pm

    Could we get a strong named assembly in the NuGet package?

    • March 28, 2013 12:55 am

      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.

  27. Victor permalink
    April 3, 2013 4:41 pm

    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.

    • April 3, 2013 4:52 pm

      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.

      • Victor permalink
        April 4, 2013 1:33 pm

        Exactly what I was looking for. I implemented a callback for DynamicConfigurationCallback.

        Thanks.

  28. April 25, 2013 3:13 pm

    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.

  29. pedroramirezsuarez permalink
    May 14, 2013 2:58 pm

    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?

    • May 14, 2013 6:19 pm

      Best thing is to do http network traces to start to debug it.

    • bingoh8 permalink
      May 31, 2013 1:25 am

      I use http ‘GET’ and ‘POST’ are current working . It’s ok. but use http ‘delete’ doesn’t work.
      please help me. thank you.

    • May 31, 2013 8:22 am

      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.

  30. bingoh8 permalink
    May 31, 2013 1:18 am

    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.

    • May 31, 2013 1:23 am

      Yes, your server needs to implement CORS and allow the DELETE for the origin making the request.

      • bingoh8 permalink
        May 31, 2013 1:49 am

        The Web api server on global.asax . I write
        Sub Application_Start()

        CorsConfig.RegisterCors(GlobalConfiguration.Configuration)
        End Sub
        It is right?

    • bingoh8 permalink
      May 31, 2013 1:29 am

      hello brockallen , thank you for your reply. Can you descripe detail step?
      thank’s a lot.

  31. Jeff Dugas permalink
    June 4, 2013 2:03 pm

    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?

    • Jeff Dugas permalink
      June 4, 2013 4:22 pm

      Please disregard…found and fixed.

  32. June 27, 2013 2:02 am

    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.

  33. July 3, 2013 2:42 pm

    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?

    • July 3, 2013 2:52 pm

      Dunno — I’d have to debug it. Have you tried using Request.CreateResponse(HttpStatusCode.Unauthorized, …)?

  34. July 23, 2013 8:51 am

    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?

    • July 23, 2013 10:35 am

      The MDN docs are not quite correct and often misleading. My implementation follows the spec (well, at least it tries to).

  35. July 23, 2013 8:53 am

    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();

    • July 23, 2013 10:34 am

      You can set the DefaultCacheDuration on the CorsConfiguration.

    • July 23, 2013 10:47 am

      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.

  36. August 26, 2013 2:19 pm

    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.

  37. September 20, 2013 12:25 pm

    Awesome news! Thanks for doing this.
    Paul

  38. October 30, 2013 6:37 am

    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! :)

  39. sylviedaeyaert permalink
    January 4, 2014 12:30 pm

    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

  40. January 15, 2014 10:02 am

    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.

  41. Von permalink
    January 17, 2014 5:20 am

    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?

    • January 17, 2014 10:11 am

      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.

      • Von permalink
        January 17, 2014 5:50 pm

        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.

        • January 17, 2014 7:59 pm

          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.

          • Von permalink
            January 20, 2014 12:30 am

            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.

          • January 20, 2014 9:22 am

            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.

  42. Phillip permalink
    April 22, 2014 11:56 pm

    Thank you! Works great.

  43. February 19, 2015 10:06 am

    @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?

    • February 23, 2015 7:16 pm

      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.

  44. Mohanraj permalink
    September 4, 2015 8:15 am

    If the request is with in the same domain, its not adding the header. Is there any other way to resolve this problem?

    • Mohanraj permalink
      September 4, 2015 8:27 am

      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?

    • October 1, 2015 5:02 pm

      If the request is from the same origin, there’s no need to send the header.

  45. George Dyrrahitis permalink
    September 30, 2015 6:58 am

    Thank you!! Works great!!!

  46. Alonso permalink
    November 29, 2015 9:25 am

    Thanks!!!

  47. Zahid permalink
    January 20, 2017 11:08 am

    I created an MVC Web Api which I am hosting in IIS. Which method should I follow? Thanks.

Trackbacks

  1. CORS support in WebAPI, MVC and IIS with Thinktecture.IdentityModel | www.leastprivilege.com
  2. ¿Están tus servicios REST en otro servidor? - Burbujas en .NET
  3. CORS open source contribution to ASP.NET and System.Web.Cors | brockallen
  4. Make AJAX requests between an ASP.NET Azure hosted Web Site and Web Role? - oneuptime | oneuptime
  5. Zangari Answers
  6. IIS / ASP.NET / CORS – Thinktecture.IdentityModel | Yourim.net
  7. CORS on ASP.NET – IndaSoft

Leave a comment