How to Troubleshoot CORS Issue in ASP.NET Core

It can be difficult to configure cross-origin resource sharing correctly. Common CORS problems that arise when configuring an ASP.NET Core Web API local development environment are covered in this post.

This article explains how to enable cross-origin requests in a development environment exclusively by using CORS settings.

It is not a security feature to enable CORS! Your application’s overall security posture is weakened when you include exceptions for cross-origin resource sharing. You should leave it disabled if your website still works properly without CORS support enabled.

Thanks to ASP.NET’s integrated middleware support for CORS, setting it up was a breeze! Compared to earlier ASP.NET versions, this was considerably simpler. Microsoft even provides extensive setup instructions for this middleware.

Even with this strong middleware in place, though, I still had to solve a few problems in order to fully utilize CORS.

Cross-origin resource sharing enables pages that were served from a UI with a different FQDN to call a Web API that is REST based. When developing, this can be especially helpful because your UI application’s port might not match your API’s. When you need to permit subdomains you trust to make direct calls to your API, it is also useful.

Pro Tip: It is preferable to have third-party websites call your API through their own server and not through CORS. This can be achieved by having the third-party websites call your API through their server. In this manner, the browser does not impose any CORS restrictions.

Initially, I defined a straightforward policy in my startup to configure the middleware.Method cs ConfigureServices:

private readonly string AllowedOriginPolicy = "_AllowedOriginPolicy";

public void ConfigureServices(IServiceCollection services)
{ 
    services.AddCors(options =>
    {
        options.AddPolicy(AllowedOriginPolicy,
            builder =>
            {
                var corsOrigins = new String[1]{ "https://localhost:1234" };

                builder.WithOrigins(corsOrigins);
            });
    });

    services.AddControllers();
}

As you can see, a string array is being used to create this policy (there can be multiple allowed origins). When you wire up the policy in the Configure method, the read-only string AllowedOriginPolicy identifies it:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseCors(AllowedOriginPolicy);

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

After doing this, I assumed that my local JavaScript-based UI and API would be able to use CORS. However, I was mistaken!

Troubleshooting The issues I discovered hindering cross-site requests in my local development environment were as follows:

1. Response headers and necessary methods were prohibited. You must permit the server to return any requested headers that the fetch is requesting in order for CORS to work properly. A select few are returned by default; however, the server will reject any requests from the client that are not on this list. Any HTTP methods (GET, POST, PUT, DELETE, OPTIONS) that you anticipate being called must also be allowed.

I’ve allowed all methods and all headers because I’m in a local development environment:

services.AddCors(options =>
{
    options.AddPolicy(AllowedOriginPolicy,
        builder =>
        {
            var corsOrigins = new String[1]{ "https://localhost:1234" };

            builder.WithOrigins(corsOrigins)
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
});

2. Cookies with credentials were prohibited. Credential cookies are not sent on a CORS request by default in the browser, for user and site security. You must enable this feature if your website employs authentication and the credential cookies must be sent with the CORS request:

services.AddCors(options =>
{
    options.AddPolicy(AllowedOriginPolicy,
        builder =>
        {
            var corsOrigins = new String[1]{ "https://localhost:1234" };

            builder.WithOrigins(corsOrigins)
                .AllowCredentials()
                .AllowAnyHeader()
                .AllowAnyMethod();;
        });
});

3. Credentials are not sent by the fetch API. The Browser Fetch API does not, by default, send credentials to an alternate origin. In this instance, a web application was sending local REST requests to the ASP.NET Core Web API, which was operating on a different port, via the Fetch API. The web application’s credentials were not being sent by the browser in response to the request.

It was decided to let Fetch send the credentials cookie by setting the following option:

const response = await fetch(url, { credentials: 'include' });

This instructs the browser to make a cross-origin request, but still include the credential cookies in the fetch request.

Useful Fetch documentation is available on MSDN.

With the UI using one port and the API using another (different FQDNs), the application was now effectively performing CORS calls in my development environment.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *