0

You know, the usual: CORS isn't working. Chrome serves me up with this:

Fetch API cannot load https://url-to-my-api-thing. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://url-to-the-origin-thing' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

I have an ASP.NET Web API 2 application which uses the OWIN pipeline and is hosted in IIS.

Here's how I (try to) set up CORS in my Configuration method:

// ... container setup above, CORS is the first middleware ...

var corsPolicyProvider = new CorsPolicyProvider(_corsOrigins, container.GetInstance<ILogger>);
app.UseCors(new CorsOptions
{
    PolicyProvider = corsPolicyProvider
});

app.UseStageMarker(PipelineStage.Authenticate);

configuration.Services.Replace(typeof(IExceptionHandler), new PassThroughExceptionHandler());
configuration.MapHttpAttributeRoutes();
app.UseWebApi(configuration);

And here's what my CorsPolicyProvider class looks like:

public class CorsPolicyProvider : ICorsPolicyProvider
{
    private readonly Regex _corsOriginsRegex;
    private readonly string _defaultCorsOrigin;
    private readonly Func<ILogger> _scopedLoggerCreator;

    public CorsPolicyProvider(string corsOrigins, Func<ILogger> scopedLoggerCreator)
    {
        _corsOriginsRegex = new Regex($"({corsOrigins})", RegexOptions.IgnoreCase);
        _defaultCorsOrigin = corsOrigins?.Split('|').FirstOrDefault();
        _scopedLoggerCreator = scopedLoggerCreator;
    }

    public Task<CorsPolicy> GetCorsPolicyAsync(IOwinRequest request)
    {
        var logger = _scopedLoggerCreator();

        var allowedOrigin = _defaultCorsOrigin;
        string[] origins;
        request.Headers.TryGetValue("Origin", out origins);
        var origin = origins?.FirstOrDefault();
        if (origin != null && Uri.IsWellFormedUriString(origin, UriKind.Absolute) && _corsOriginsRegex.IsMatch(new Uri(origin).Host))
            allowedOrigin = origins.FirstOrDefault();

        var policy = new CorsPolicy
        {
            Origins = { allowedOrigin },
            Methods = { HttpMethod.Get.Method, HttpMethod.Post.Method, HttpMethod.Put.Method, HttpMethod.Delete.Method },
            Headers = { "accept", "content-type" },
            SupportsCredentials = true,
            PreflightMaxAge = 1728000
        };

        logger?.Verbose("Resolving CORS policy: {@CorsPolicy}", policy);

        return Task.FromResult(policy);
    }
}

Why is this policy never triggered, or at best malfunctioning? If I set a breakpoint and explicitly send an OPTIONS request when testing locally, it enters the GetCorsPolicyAsync method, and also logs the request as expected. But when the API is on the server and I try to call it from a website in Chrome, it never logs the request and it just tells me there's no CORS header.

Martin Wedvich
  • 2,158
  • 2
  • 21
  • 37
  • Please don't stop there! What was the configuration problem? I am facing an issue that sounds a lot like this. – James O'Donnell Dec 13 '16 at 17:26
  • 1
    In my case, the configuration had been overwritten with the configuration from a different environment. So the app from our system test environment was attempting to talk to the API from our acceptance test environment, which won't work because the acceptance test API (correctly so) only accepts the acceptance test app as its origin. – Martin Wedvich Dec 13 '16 at 21:20

1 Answers1

0

... and like clockwork, the moment I post something on SO I find the answer. There was a CORS header, and the request was being logged, but the environment had the wrong configuration, so the origin (which was then invalid since it was a different environment) was correctly being blocked, and the logs end up on the wrong server. Fixed the configuration, and it works as expected again.

Martin Wedvich
  • 2,158
  • 2
  • 21
  • 37
  • You are experiencing Rubber Duck Debugging. https://en.wikipedia.org/wiki/Rubber_duck_debugging It may benefit you to incorporate it into your troubleshooting. – The Muffin Man Dec 13 '16 at 17:27
  • You are absolutely right. I do that a lot on Stack Overflow - write a long question, try to fill in the blanks, then stumble upon a solution by chance and delete my post again. I usually find the answer before I post... but not in this case :( – Martin Wedvich Dec 13 '16 at 21:22
  • @MartinWedvich when I do that I just go ahead and provide myself with an answer in the hopes that it helps someone else with a similar issue. My most successful example of doing this is this [question and answer](http://stackoverflow.com/q/13424079/61654) pair. – ahsteele Dec 14 '16 at 21:13