Reputation: 16502
I am using a DynamicPolicyProviderFactory to determine if a request from a domain is allowed or not in an Asp.Net Web API 2.2 application. In order make that determination I am using a couple of RegEx patterns defined in the Web.Config
<applicationSettings>
<MyApp.Properties.Settings>
<setting name="AllowedDomains" serializeAs="Xml">
<value>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>http://localhost</string>
<string>http*://*.domain1.com</string>
<string>http*://*.domain2.com</string>
</ArrayOfString>
</value>
</setting>
</MyApp.Properties.Settings>
</applicationSettings>
Then within my DynamicPolicyProviderFactory
I create a RegEx I can use:
//constructor
public DynamicPolicyProviderFactory(IEnumerable allowedOrigins)//allowedDrigins is the strings passed from the config {
_allowed = new HashSet<Regex>();
foreach (string pattern in allowedOrigins.Cast<string>()
.Select(Regex.Escape)
.Select(pattern => pattern.Replace("*", "w*")))
{
_allowed.Add(new Regex(pattern, RegexOptions.IgnoreCase));
}
if (_allowed.Count > 0)
return;
//if nothing is specified, we assume everything is.
_allowed.Add(new Regex(@"https://\w*", RegexOptions.IgnoreCase));
_allowed.Add(new Regex(@"http://\w*", RegexOptions.IgnoreCase));
}
public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
{
var route = request.GetRouteData();
var controller = (string)route.Values["controller"];
var corsRequestContext = request.GetCorsRequestContext();
var originRequested = corsRequestContext.Origin;
var policy = GetPolicyForControllerAndOrigin(controller, originRequested);
return new CustomPolicyProvider(policy);
}
//this is where the magic happens
private CorsPolicy GetPolicyForControllerAndOrigin(string controller, string originRequested)
{
// Do lookup to determine if the controller is allowed for
// the origin and create CorsPolicy if it is (otherwise return null)
if (_allowed.All(a => !a.Match(originRequested).Success))
return null;
var policy = new CorsPolicy();
policy.Headers.Add("accept");
policy.Headers.Add("content-type");
policy.Origins.Add(originRequested);
policy.Methods.Add("GET");
policy.Methods.Add("POST");
policy.Methods.Add("PUT");
policy.Methods.Add("DELETE");
policy.Methods.Add("OPTIONS");
return policy;
}
Assuming the request origin is: http://sub.domain1.com and the script location is http://sub.domain1.com:8080. This works perfectly in Chrome, IE, FireFox, and Opera, but it fails in Safari. What is strange is that the error Safari gives in the browser console is
Failed to load resource: Origin http://sub.domain1.com is not allowed by Access-Control-Allow-Origin
What could be happening?
Another oddity I observed is that if both my origin and script location are http:sub.domain1.com:8080 CORS request are made by Safari (and pass) but other browsers correctly treat this as same origin.
edit: I have found that if I put a breakpoint where GetPolicyForControllerAndOrigin
returns null, it is never hit and a policy is created and returned properly.
edit 2: I inspected the response from the server to the OPTIONS request by Safari and have found that the headers Access-Control-Allow-Headers
and Access-Control-Allow-Origin
headers are missing from the response. Also, on the request side, Safari is not adding the Host
header. Is that required? Is that why it is failing?
Upvotes: 0
Views: 517
Reputation: 16502
Issue was with the OPTIONS request by Safari. I was only allowing accept
and content-type
for allowed headers, but Safari was asking for origin
as well.
Upvotes: 2