Getting started with CORS in ASP.NET Core

Published: Tuesday 25 February 2020

If you are using an API in your application, you probably have come across CORS at some point. And no, we are not talking about the successful Irish band that had many top hits in the 90s. That's a different spelling anyways. We are talking about Cross-Origin Resource Sharing (CORS).

The purpose of CORS is to return additional headers to browsers when making a request. The headers dictate whether the browser's request is allowed. This is particularly important if you are integrating an API application. You don't want any referrer hitting your server!

Browsers will put CORS into action when making a HTTP request through the use of JavaScript. As a result, if CORS hasn't been set up, or configured incorrectly from the request server, the browser tends to throw a JavaScript error back, similar to the following:

Access to fetch at '{Request}' from origin '{Origin}' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

It's important to note that CORS always gets set up on the server-side. Not the client-side! So if you are not in control of the server you are requesting from, you need to get in contact with the people who are and get them to make the changes.

So what can you customise with CORS? Well you can customise the following from the referrer:

  • Origin - The host name of the referrer (e.g. https://www.roundthecode.com)
  • Verb - The HTTP verb used in the request e.g. GET/POST
  • Header - Any HTTP headers used in the request

We are now going to talk through how we would set this up in an ASP.NET Core.

Integrate CORS in our ASP.NET Core Application

You will be pleased to hear that CORS is already integrated into ASP.NET Core. It is hidden away in the "CorsMiddlewareExtensions" class which is in the "Microsoft.AspNetCore.Cors" assembly.

Then it's a case of integrating CORS into your "Startup" class. Here is a example of how you would integrate it:

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

	app.UseHttpsRedirection();

	app.UseRouting();

	app.UseAuthorization();

	app.UseCors(policy =>
		policy.AllowAnyOrigin()
		.AllowAnyMethod()
		.AllowAnyHeader()
		.AllowCredentials());

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

As you can see from above, the "IApplicationBuilder" interface includes a "UseCors" extension method within it. In the above example, we are basically allowing any client to use our server.

That's fine if you don't want any restrictions. But, API's probably want to restrict where the requests come from, and what they are doing with the request. We will now go through the different "IApplicationBuilder" extension methods that are available to customise our restrictions.

WithOrigins

A string array with all the origins that are allowed to make requests to our application:

// Startup.cs
app.UseCors(policy =>
	policy.WithOrigins(new string[] { "https://host1", "https://host2" })
);

WithVerbs

A string array with all the verbs allowed to be used when making requests to our application:

// Startup.cs
app.UseCors(policy =>
	policy.WithMethods(new string[] { "GET", "POST" })
);

WithHeaders

A string array with all the headers that have to be specified when making requests to our application:

// Startup.cs
app.UseCors(policy =>
	policy.WithHeaders(new string[] { "Must-Contain-This-Header", "And-This-Request" })
);

Policy Names

You can also set up policies by giving them a name. The way it works is that you set up the CORS policy like above, give it a name, and then use that name when calling the "UseCors" extension method in "IApplicationBuilder".

The only difference with this is that you must specify the origin of the referrer when doing it this way. The "AllowAnyOrigins" extension method does not work!

The "ConfigureServices" method in the "Startup" class passes a "IServiceCollection" parameter. It's this parameter that contains the method to set up a policy with a name. The "AddCors" extension method inside "IServiceCollection" allows us to create a name and set up the policy. We then call the policy name inside our Configure method. Here is an example in our Startup class:

// Startup.cs
public class Startup
{
	public Startup(IConfiguration configuration)
	{
		Configuration = configuration;
	}

	public IConfiguration Configuration { get; }

	public void ConfigureServices(IServiceCollection services)
	{
		services.AddControllers();

		services.AddCors(setup =>
		{
			// Set up our policy name
			setup.AddPolicy("AllowRoundTheCode", policy =>
			{
				policy.WithOrigins(new string[] { "https://www.roundthecode.com" }).AllowAnyMethod().AllowAnyHeader().AllowCredentials(); // Allow everyone from https://www.roundthecode.com (you're very kind)
			});
		});
	}

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

		app.UseHttpsRedirection();

		app.UseRouting();

		app.UseAuthorization();

		app.UseCors("AllowRoundTheCode"); // Calling our "AllowRoundTheCode" policy set up in the "ConfigureServices" method

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

It's Not Working

You may find that even when you've set up CORS and made a request to your server, you still get this JavaScript error:

Access to fetch at '{Request}' from origin '{Origin}' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

o what's going on here? Well it's probably where you've put your "UseCors" extension method inside the "Configure" method. If you put it below the "UseEndpoints" method, it doesn't work. It would seem it has to go above this method to be successfully configured.

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

	app.UseHttpsRedirection();

	app.UseRouting();

	app.UseAuthorization();


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

	app.UseCors("AllowRoundTheCode"); // This will not work. This has to sit above UseEndpoints to work
 
}

It might be the case of altering the position of the "UseCors" extension method in the "Configure" method to test at what position it works.

This is Not Security

It's worth pointing out that this will not secure your application. CORS is used on all major web browsers, but it's easy enough for someone to write their own application and ignore the headers that are returned from the server's response. However, it will help you integrate an API with a single-page application using a JavaScript framework, such as React.