Access-control-allow-origin with multiple domains
For IIS 7.5+ and Rewrite 2.0 you can use:
<system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" /> <add name="Access-Control-Allow-Methods" value="POST,GET,OPTIONS,PUT,DELETE" /> </customHeaders> </httpProtocol> <rewrite> <outboundRules> <clear /> <rule name="AddCrossDomainHeader"> <match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" /> <conditions logicalGrouping="MatchAll" trackAllCaptures="true"> <add input="{HTTP_ORIGIN}" pattern="(http(s)?://((.+\.)?domain1\.com|(.+\.)?domain2\.com|(.+\.)?domain3\.com))" /> </conditions> <action type="Rewrite" value="{C:0}" /> </rule> </outboundRules> </rewrite> </system.webServer>
Explaining the server variable RESPONSE_Access_Control_Allow_Origin
portion:
In Rewrite you can use any string after RESPONSE_
and it will create the Response Header using the rest of the word as the header name (in this case Access-Control-Allow-Origin). Rewrite uses underscores "_" instead of dashes "-" (rewrite converts them to dashes)
Explaining the server variable HTTP_ORIGIN
:
Similarly, in Rewrite you can grab any Request Header using HTTP_
as the prefix. Same rules with the dashes (use underscores "_" instead of dashes "-").
There can only be one Access-Control-Allow-Origin
response header, and that header can only have one origin value. Therefore, in order to get this to work, you need to have some code that:
- Grabs the
Origin
request header. - Checks if the origin value is one of the whitelisted values.
- If it is valid, sets the
Access-Control-Allow-Origin
header with that value.
I don't think there's any way to do this solely through the web.config.
if (ValidateRequest()) { Response.Headers.Remove("Access-Control-Allow-Origin"); Response.AddHeader("Access-Control-Allow-Origin", Request.UrlReferrer.GetLeftPart(UriPartial.Authority)); Response.Headers.Remove("Access-Control-Allow-Credentials"); Response.AddHeader("Access-Control-Allow-Credentials", "true"); Response.Headers.Remove("Access-Control-Allow-Methods"); Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");}
In Web.API this attribute can be added using Microsoft.AspNet.WebApi.Cors
as detailed at http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api
In MVC you could create a filter attribute to do this work for you:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]public class EnableCorsAttribute : FilterAttribute, IActionFilter { private const string IncomingOriginHeader = "Origin"; private const string OutgoingOriginHeader = "Access-Control-Allow-Origin"; private const string OutgoingMethodsHeader = "Access-Control-Allow-Methods"; private const string OutgoingAgeHeader = "Access-Control-Max-Age"; public void OnActionExecuted(ActionExecutedContext filterContext) { // Do nothing } public void OnActionExecuting(ActionExecutingContext filterContext) { var isLocal = filterContext.HttpContext.Request.IsLocal; var originHeader = filterContext.HttpContext.Request.Headers.Get(IncomingOriginHeader); var response = filterContext.HttpContext.Response; if (!String.IsNullOrWhiteSpace(originHeader) && (isLocal || IsAllowedOrigin(originHeader))) { response.AddHeader(OutgoingOriginHeader, originHeader); response.AddHeader(OutgoingMethodsHeader, "GET,POST,OPTIONS"); response.AddHeader(OutgoingAgeHeader, "3600"); } } protected bool IsAllowedOrigin(string origin) { // ** replace with your own logic to check the origin header return true; }}
Then either enable it for specific actions / controllers:
[EnableCors]public class SecurityController : Controller { // *snip* [EnableCors] public ActionResult SignIn(Guid key, string email, string password) {
Or add it for all controllers in Global.asax.cs
protected void Application_Start() { // *Snip* any existing code // Register global filter GlobalFilters.Filters.Add(new EnableCorsAttribute()); RegisterGlobalFilters(GlobalFilters.Filters); // *snip* existing code}