.NET CORS Guide:
What It Is and
How to Enable It

stackhawk

StackHawk|January 20, 2022

Learn how to enable .NET CORS, why would you do do it, and what the implications are for your app in today's post.

When working with a front-end app that accesses a .NET API, you might face an error saying that access was blocked due to the CORS policy. Someone tells you that to make the error go away, you need to enable CORS. But what exactly is CORS? Why do you have to enable it? What are the implications of doing so?

These—and more—are the questions we'll answer today. We'll open the post with the fundamentals, defining CORS and explaining what problem it solves.

Then we'll proceed to a practical .NET CORS example. I'll walk you through setting up a super silly API, trying—and failing—to consume it using a React app, and then enabling CORS to solve the problem.

Let's get started.

Requirements

The post should work as just a piece of reading, but if you want to follow along with the guide, there are a few requirements:

  • You must have the .NET 6.0 SDK installed.

  • Some familiarity with C#/.NET is assumed.

  • Also, you must have Node.js installed, though JavaScript/React knowledge isn't necessarily required.

  • I assume you're comfortable working the command line, since we'll be running some commands along the way.

Additionally, I'll ask you to clone a repo on GitHub. If you're not comfortable with Git, though, you can download the project as a .zip file.

Finally, a caveat along the way. I'm currently on Windows while writing this tutorial, but I'll do my best to ensure the commands work correctly for Linux as well. If you're on OS X, you should be fine, though I make no guarantees since I don't own a Mac.

With that out of the way, let's begin.

.NET CORS Fundamentals

The StackHawk blog already features a quite comprehensive guide on CORS, and I highly recommend you read it, as it explores CORS in much more depth than I will in this post. Regardless, here's a brief introduction to the topic.

What Is CORS?

CORS means cross-origin resource sharing. You'll see more in just a minute, but in a nutshell, CORS is a mechanism—an HTTP protocol, to be exact—that allows web applications to access resources hosted on different domains (or origins.)

But wouldn't that be a security problem? Here comes the second part.

Why Is CORS Needed?

To understand why you'd need CORS, you have to understand this thing called same-origin policy. Here's how the MDN Web Docs defines it:

The same-origin policy is a critical security mechanism that restricts how a document or script loaded by one origin can interact with a resource from another origin.

In other words, the same-origin policy prevents a script from interacting with a resource from a different origin. Two URLs have the same origin when the protocol, domain, and port (if available) all match. For instance, the following URLs share the same origin:

  • https://www.stackhawk.com

  • https://www.stackhawk.com:443

  • https://www.stackhawk.com/why-stackhawk/

The following URLs don't all share the same origin; the first and the second don't share the same protocol, and the second and third ones don't share the same host name.

  • http://www.stackhawk.com

  • https://www.stackhawk.com

  • https://stackhawk.com/why-stackhawk/

The same-origin policy is critical for the web's safety. It puts malicious scripts inside a "cage," preventing them from interacting with unauthorized information.

However, there are valid reasons to allow a script to talk to resources hosted on a different origin. A client accessing a back-end API is probably the most famous example. That's where CORS comes in handy: it's a mechanism to relax—albeit in a safe and controlled way—the restriction imposed by the same-origin policy.

.NET CORS: The Hands-On Guide

I hope you're dying to roll up your sleeves and do some work, because that's what we'll do right now. Let's dig in.

Creating a Sample .NET API

To demonstrate CORS in practice, you'll need an application. Run the following commands to create a new .NET API.

mkdir netcorsdemo
cd netcorsdemo
dotnet new webapi

Then, you can run your newly created application with:

dotnet run

If everything went right, the API will be served at https://localhost:7246. Using a tool like Postman or cURL—or even your browser—send a GET request to https://localhost:7246/WeatherForecast. You should obtain the following result:

[{"date":"2022-01-16T10:00:43.9724527-03:00","temperatureC":49,"temperatureF":120,"summary":"Warm"},{"date":"2022-01-17T10:00:43.9724609-03:00","temperatureC":-16,"temperatureF":4,"summary":"Warm"},{"date":"2022-01-18T10:00:43.9724612-03:00","temperatureC":-14,"temperatureF":7,"summary":"Scorching"},{"date":"2022-01-19T10:00:43.9724613-03:00","temperatureC":29,"temperatureF":84,"summary":"Chilly"},{"date":"2022-01-20T10:00:43.9724615-03:00","temperatureC":53,"temperatureF":127,"summary":"Chilly"}]

If your browser has an extension for formatting JSON, you might get a nice output like this:

.NET CORS Guide: What It Is and How to Enable It image

Obtaining the Sample Front-End App

What you need now is a front-end app to consume the API. For that, go to this GitHub repository and clone it using Git—alternatively, download the whole project as a .zip file and then extract it to a folder.

Time to Run Everything

Now that you have all pieces of the puzzle, let's put them together. First, go to your terminal and make sure the API is running:

dotnet run

On a new terminal window or tab, access the folder you just cloned or extracted. Run the following commands:

npm install
npm start


The first command installs the dependencies needed for the project to run. The second one starts a development server and serves the application. After running both commands, your default browser should open a new window pointing to http://localhost:3000. If for some reason that doesn't happen, do it manually.

Anyway, after you try to serve the React app, you should see an error like this:

.NET CORS Guide: What It Is and How to Enable It image

Access the developer tools of your browser and go to the Console view (if you use Chrome, press Control+Shift+J on Windows/Linux or Command+Option+J on Mac). You'll see an error message like this:

Access to fetch at 'https://localhost:7246/WeatherForecast' from origin 'http://localhost:3000'
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.

That's exactly the error message we wanted to get. Why does it happen? Well, the .NET API is being served at https://localhost:7246 and the React app at http://localhost:3000. The protocols are different, as are the port numbers. Thus, for CORS purposes, they're considered different origins.

Enable CORS and Fix The Problem

As it turns out, enabling CORS in a .NET API is quite easy, as the platform comes with built-in features to support that. So, let's do it.

Using your favorite text editor, go to the .NET project folder and open the Program.cs file. If you haven't used .NET 6 before now, you might find this file weird. It contains things that used to be stored in the Startup.cs file. Well, .NET 6 got rid of the Startup file and merged its contents into Program.cs.

First, add this to the top of the file:

var  policyName = "_myAllowSpecificOrigins";

Then, after the line that instantiates the builder variable, add this:

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: policyName,
                      builder =>
                      {
                          builder
                            .WithOrigins("http://localhost:3000") // specifying the allowed origin
                            .WithMethods("GET") // defining the allowed HTTP method
                            .AllowAnyHeader(); // allowing any header to be sent
                      });
});

The code above defines our policies for CORS. It will allow requests from the origin http://localhost:3000, using the HTTP method GET, with any header.

Finally, let's add the following line before app.UseAuthorization:

app.UseCors(policyName);

To clear any doubts, here's what the complete file should look like:

var  policyName = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: policyName,
                      builder =>
                      {
                          builder
                            .WithOrigins("http://localhost:3000")
                            //.AllowAnyOrigin()
                            .WithMethods("GET")
                            .AllowAnyHeader();
                      });
});

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseCors(policyName);

app.UseAuthorization();

app.MapControllers();

app.Run();

Now, run the API again and reload the front-end app. The result should look like this:

.NET CORS Guide: What It Is and How to Enable It image

.NET CORS: Leverage Resources, But Stay Safe

Creating a modern web application often feels like gluing a lot of parts together. Sometimes, the parts don't play together as nicely as you'd wish they did, and errors occur.

In the context of a modern web app consisting of a JavaScript front-end consuming a back-end API, CORS errors are common. They happen because of the same-origin policy, a vital security mechanism for the web.

As you've seen, safely relaxing the restrictions imposed by this policy is useful, and that's where enabling CORS comes in handy. Now you know how to enable CORS in your .NET API in a simple and easy way. However, that's just the tip of the iceberg: we encourage you to learn more about CORS in the context of .NET and in general.

Happy learning, stay safe, and until next time!

This post was written by Carlos Schults. Carlos is a consultant and software engineer with experience in desktop, web, and mobile development. Though his primary language is C#, he has experience with a number of languages and platforms. His main interests include automated testing, version control, and code quality.


StackHawk  |  January 20, 2022