Enabling CORS only for specific domains in ASP.NET

Enable cross origin resquests only for certain domains in ASP.NET

  • Share

Cross-origin resource sharing (CORS) means that page from other domain can make request to some resource which is on other domain. For example, if you try to invoke some WEB API method which is running on different domain you will get exception in the script.

By default CORS are disabled in ASP.NET bot you can easily enable them just by modifying web.config for IIS7 and newer versions pf IIS.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>
    

Of course there are programmatic ways to enable CORS and the approaches are a bit different regrading whether you are using MVC and WEB API or you are still using HttpModules to provide the REST service data.

As simple this solution is, less extensible it is. This setting can only switch off and on CORS for either all domains that access resource or just one specific. I you for instance have more than one client that will access resource and you do not want to enable resource for every domain, you will have to dig into the code.

Code implementation is pretty simple as you just need to add some custom headers.

System.Web.HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "*");
    
Note

To check header values in response you can use "Http Response Browser" extension for Firefox which can be found at https://addons.mozilla.org/en-US/firefox/addon/http-response-browser/

Since it is possible to do it pragmatically from C# code, depending on technology you use here two approaches:

1. Enable CORS grammatically in Web API

Web API has one thing that I really like and those are attributes. Basically you can apply different rules to your controller or actions in controller by just adding attributes. You can write your own attributes and then use them to affect behavior of controllers or specific actions in a controller.

Note

There is a package for CORS which you can install using NuGet console "Install-Package Microsoft.AspNet.WebApi.Cors". For some reason I could not make this one work, so I wrote my own for the purpose to try to make it simpler and to understand how it works from ground up

The following is a custom action attribute that adds CORS headers

namespace CORSTestService.Attributes
{
    public class EnableCORS : ActionFilterAttribute
    {
        private string origins;
        private string methods;
        public EnableCORS(string origins = "*", string methods = "*")
        {
            this.origins = origins;
            this.methods = methods;
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (!string.IsNullOrWhiteSpace(this.origins))
            {
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", this.origins);

                if (!string.IsNullOrWhiteSpace(this.methods))
                {
                    HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", this.methods);
                }
            }
        }
    }
}
    

To use it just add attribute to an action in WebAPI controller

namespace CORSTestService.Attributes
{
    public class EnableCORS : ActionFilterAttribute
    {
        private string[] origins;
        private string methods;

        public EnableCORS(string[] origins = null, string methods = "*")
        {
            this.origins = origins;
            this.methods = methods;
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (this.origins == null || this.origins.Any())
            {
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "*");
            }
            else if (this.origins.Contains(HttpContext.Current.Request.Url.Host, StringComparer.InvariantCultureIgnoreCase))
            {
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Url.Host);
            }

            if (!string.IsNullOrWhiteSpace(this.methods))
            {
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", this.methods);
            }
        }
    }
}
    

This custom attribute will do the same thing as the config section mentioned in the beginning which means it will enable CORS to for every request. It will not solve the limitation of just few domains.

Since headers value cannot have multiple domains we need to do a simple hack. Basically attribute needs to check whether request domain is in domains list and add it to header value.

namespace CORSTestService.Attributes
{
    public class EnableCORS : ActionFilterAttribute
    {
        private string[] origins;
        private string methods;

        public EnableCORS(string[] origins = null, string methods = "*")
        {
            this.origins = origins;
            this.methods = methods;
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (this.origins == null || !this.origins.Any())
            {
                //Allow all
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", "*");
            }
            else if (this.origins.Contains(HttpContext.Current.Request.Url.Host, StringComparer.InvariantCultureIgnoreCase))
            {
                //Allow only if matching
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Url.Host);
            }

            if (string.IsNullOrWhiteSpace(this.methods))
            {
                //Allow all
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", "*");
            }
            else
            {
                //Allow only specified
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", this.methods);
            }
        }
    }
}
    

Now to invoke method with enabled CORS you need to declare an array of domains as a constructor parameter in attribute declaration for controller action

namespace CORSTestService.Controllers
{
    public class ServiceController : ApiController
    {
        [EnableCORS(origins: new string[] { "localhost" }, methods: "*")]
        public DateTime GetDateTime()
        {
            return DateTime.Now;
        }
    }
}
    

1. Enable CORS pragmatically using HttpModule

Http modules are often used to modify the response especially in ASP.NET WebForms web applications and websites. The same approach from the Web API colution from above can be used.

Although it is commonly used in WebForms it still can be used in MVC application, it's just not so elegant as the one which is used above.

namespace CORSTestService.Modules
{
    public class CORSModule : IHttpModule
    {
        private string[] origins = null;
        private string methods = "*";

        public void Init(HttpApplication context)
        {
            //Load from config (hardcoded only for demo)
            this.origins = new string[] { "localhost" };

            context.PostRequestHandlerExecute += context_PostRequestHandlerExecute;
        }

        void context_PostRequestHandlerExecute(object sender, EventArgs e)
        {
            if (this.origins != null && this.origins.Any() &&
              this.origins.Contains(HttpContext.Current.Request.Url.Host, StringComparer.InvariantCultureIgnoreCase))
            {
                HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Url.Host);

                if (!string.IsNullOrWhiteSpace(this.methods))
                {
                    HttpContext.Current.Response.AppendHeader("Access-Control-Allow-Methods", this.methods);
                }
            }
        }

        public void Dispose()
        {

        }
    }
}
    

To enable it, you just need to reference it in web.config

<?xml version="1.0" encoding="utf-8"?>  
<configuration>
  <system.webServer>
    <modules>
      <add name="CORSModule" type="CORSTestService.Modules.CORSModule"/>
    </modules>
  <system.webServer>
<configuration>
    

  • Share

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.

Comments for this article

comments powered by Disqus