CORS for Asp.NET APIs
Is CORS worth the effort!?
“CORS is perhaps one of those measures which could come across as not developer friendly, while adding not much value on the consumer side, and by that I mean there could be better alternatives to achieve more with less pain for developers”. This is not from Mr Martin Fowler, and is of-course my personal opinion. You might disagree, but I have wasted a ton of time fixing CORS issues, and personally I am not a fan). But as it may not go away any time soon, I decided to write about it, and share some solutions to the pains you might face.
By the way, this topic belongs to the series to set up an Asp.NET API for production use.
- API Route Versioning
- Configuration Management
- Secret Management
- Monitoring
- Database
- Documentation
- CORS
- Request Validation
- Global Exception Handling
- URL Rewriting
- Deploy .NET API to Azure App Service
What is CORS?
It’s a HTTP header-based mechanism that allows restricted resources to be requested from another domain outside the domain from which the first resource was served (Origin: Domain, Scheme, or Port ). Let’s say you are on https://www.sample.com, and by pressing a button, you make a XHR request (ajax) to fetch/post some data from/to https://api.sample.com. In this situation the browser will look at how the server responds and based on the information from response, it may block the request as a security risk.
Let’s dive a bit deeper, and explain the technical process following the above example. When modern browsers receive an AJAX requests with side effects on the server (other than Get or Post with certain MIME types), they send a pre-flight request with the below headers:
OPTIONS /
Host: api.sample.com
Origin: http://www.sample.com
Access-Control-Request-Method: POST
This is asking the server (https://api.sample.com), in this pre-flight request with OPTIONS request method, if it is OK to send the actual request to https://api.sample.com from https://www.sample.com subsequently. If the server decides to serve the request, it will respond with something like:
Access-Control-Allow-Origin: http://www.sample.com
Access-Control-Allow-Methods: POST, DELETE
Which indicates to the browser that is is OK to send the actual request from https://www.sample.com with POST and DELETE methods. The browser can also receive a response to pre-flight request like below which indicates requests from all origins are accepted.
Access-Control-Allow-Origin: *
Once the browser receives the permission from the server, it can then send the actual request.
Using Wildcard (*) Origins
Unless the request is a public resource and is intended to be used by everyone, you should try to be as specific as possible about the origins of the requests, otherwise you will lose the benefit of implementing CORS in your applications. Later on, I will talk about how you define CORS policies in your Asp.NET 5.0 APIs and specify all these elements.
Here is the screenshot of the pre-flight request along with the actual request that happened upon receiving acceptance from the server:
CORS vs JSONP
CORS is the modern alternative to JSONP, which supports more request methods (as opposed to just GET), while JSONP was more suited toward older and legacy browsers. CORS is now supported by the majority of modern browsers.
CORS Errors
Other than acceptance response from the server, CORS failures are pretty common, specifically when you are developing your application. It could fail because:
- 1. Server CORS Policies did not allow the specific resource to be served based on the domain, scheme, port, header, credential or method.
- 2. Error on Server: There was an error on the server, and preflight request failed because the server couldn’t serve the pre-flight request.
- 3. API Gateway: Although your web server is sending acceptance back in response to pre-flight requests, your API gateway is not configured properly to relay those responses to the browser.
- 4. Credentials: need to be sent such as cookies or Http Authentication.
CORS failures result in errors that for security reasons are vague, and the real reason is not exposed to JavaScript client. Hence it could fail because of any of the above scenarios with the same message, and that makes it a pain point for developers to debug and troubleshoot errors with generic CORS error codes.
Figure 3, shows the CORS error message you could receive, and Figure 4 shows you how it looks like when you look at DevTools.NetworkTab.
Configuration
Now lets dive into the configuration, and then common solutions to some of the problems we talked about.
1. Packages
You will need the below packages when defining CORS configuration and policies:
2. Configure Services
When defining CORS configuration on the server side for Asp.Net 5.0, one of the ways is to define policies:
What I am saying in the above snippet is to define a policy called CORSPolicy, which accepts traffic from the sources I have specified in my appsettings.json file, and allows requests with any headers, method, or credentials. (To learn more about how I access my appsettings.json and application configurations, refer to API Configuration Management).
As you can see I am allowing https://localhost:6500 in my test environment (appsettings.json), and similarly I will define with the same structure, the domains that are to be allowed in production (appsettings.prod.json).
3. Middleware
Now that I have defined the policy, I’ll need to inject it in my request pipeline by calling it in the middleware configuration:
Order Matters
You need to make sure the above order is maintained, as the request pipeline executes those statements in the same order.
Troubleshooting CORS
There are a number of ways you can get CORS errors, and they may or may not be directly related to CORS problems or any errors in code. I will explain them some of them here.
1. Investigate Server Side Errors
You might get a CORS problem because simply your sever side has run time errors. So check your server side logs, and make sure it works fine. You might also have some problems with Dependency Injection, and you API might be even failing to start. This will cause pre-flight requests to fail.
2. API Gateway
I have spent a full half-day thinking the CORS problem was coming from my API, and it was failing even when I was allowing all origins, methods, and headers. It turned out that the problem was with the configuration of Azure API Manager (API Gateway), which was not allowing my domains in its CORS configuration.
To access CORS configuration in your Azure API gateway, access the settings for all APIs:
Click on CORS policies in inbound policies, and configure your CORS policies, the same way as you configured your API:
Note
Configuration, plumbing and troubleshooting your software foundation take a considerable amount of time in your product development. Consider using Pellerex which is a complete foundation for your enterprise software products, providing source-included Identity and Payment functions across UI (React), API (.NET), Pipeline (Azure DevOps) and Infrastructure (Kubernetes).
3. Is Pre-Flight Failing?
Check if both pre-flight and actual requests are failing or if pre-flight is successful. A successful pre-flight may indicate problems in your CORS configuration, which makes the actual request fail with the CORS error.
Pellerex Foundation: For Your Next Enterprise Software
How are you building your current software today? Build everything from scratch or use a foundation to save on development time, budget and resources? For an enterprise software MVP, which might take 8–12 months with a small team, you might indeed spend 6 months on your foundation. Things like Identity, Payment, Infrastructure, DevOps, etc. they all take time, while contributing not much to your actual product. These features are needed, but they are not your differentiators.
Pellerex does just that. It provides a foundation that save you a lot development time and effort at a fraction of the cost. It gives you source-included Identity, Payment, Infrastructure, and DevOps to build Web, Api and Mobile apps all-integrated and ready-to-go on day 1.
Check out Pellerex and talk to our team today to start building your next enterprise software fast.