Gotcha
If you just want the solution skip to the “Understanding the problem” section.
A pet peeve of mine that I have definitely mentioned before is when you seek out knowledge on a subject and every resource is full of a lot of nothing. It is the inspiration for this ongoing blog. I absolutely hate it when there is a lot of documentation or text books full of pages and words, but it doesn’t say anything worth reading. Or worse, it has a lot of useful information, but non of it is practical.
For some reason CORS explanations have this problem. I have successfully avoided dealing with CORS for a long time because I do mostly backend development, but recently I have had to connect a ReactJS component to a RESTful API. I learned several things along the way and I want to document it CLEARLY for others to benefit from when all you are trying to do is develop locally.
None of what I am showing you here is supposed to reach Production environments. This is purely for development environments where you do not have excellent control over host naming or ports. DO NOT do what I am showing here for production it is DANGEROUS.
Short refresher on CORS
For context purposes I am going to give a small refresher on CORS, but there is actually plenty of information on CORS so there is no reason for me to re-explain it in depth here. CORS stands for “Cross-Origin Resource Sharing”. The concept is simple, you want to prevent your website from accessing HTTP resources on a server you don’t own or trust. The reason is that if you don’t protect yourself against this, then you are opening yourself and your clients up to XSS or “Cross site Scripting” attacks. XSS is when your website accesses a malicious HTTP resource with the key being that it is somewhere else. This somewhere else is referred to as an “origin”.
Origins
This word gets kicked around a lot and so does referer. Fun-fact the term “referer” was actually spelled incorrectly and is now burned in forever like this. Gotta love it. The whole point of an origin is to state where a HTTP resource originated from. It is comprised of the following components:
- scheme
- subdomain
- domain
- top level domain
- port number
If any part of those components is different, then it is considered a different origin. To put it bluntly…
- http://localhost
- https://localhost
- http://localhost:6006
…all three of those examples are different origins. Therefore, if you have a webserver running on port 80, but your REST API is running on port 5000 – unless you configure CORS you will just get that oh so frustrating error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at $somesite
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors
Like most errors, it says dick basically about what’s wrong unless you completely understand what’s going on. This means you had to waste most of your day trying to understand it like I did and then you write this article to explain it in plain English.
Ok so I see my HTTP request was blocked. Stahp blocking pls?
This is where I got annoyed, I understood what the problem was, but it was very unclear on how to fix it. Mozilla’s website is full of useful information about CORS as it kept being pelted at me by the Google search engine, but no where in their text could I find a straight answer on how to fix it. Honestly, the answer might be there, but I wasn’t about to start reading for hours to get a vague idea of how to fix it either. No thank you.
Understanding the problem
So as it was stated above we understand it’s not okay for two HTTP resources to have different origins… unless specified otherwise 🤔. That’s been established. What wasn’t clear at all was WHO was complaining. I for the longest time was under the impression that the website is who was complaining. It turns out that’s not true at all, it’s the SERVER that was complaining. In my case the server means the REST API I was working with which is a Microsoft Web API project.
I started digging around differently and I stumbled across this gem: https://enable-cors.org/ it’s a little dated, but the concepts are what is important.
“Well shit” I wish I would have found this first, this makes more sense than all of that white-paper nonsense that was putting me to sleep from Mozilla’s website. Thanks for not explaining anything.
Long story short – you need to enable CORS on your server application. Ugh… Could have saved hours if that error message was a little more clear and if Mozilla would explain the solution first instead of nothing but theory.
Fixing the problem
⚠️ AGAIN ⚠️ I am reiterating what I showing here is intended for DEVELOPMENT PURPOSES ONLY. This is as bad as using a fake self-signed certificate to make HTTPS work, opening up your SQL Server’s ports publicly or turning off your Firewall. It’s okay if you do it locally for development purposes, it’s terrible if you do it in production. ☠️ You have been warned. ☠️
I am going to be describing how to fix this for Microsoft Web API, but the idea must be the same for all servers/backend code. Essentially, you need to do the following:
- Install the appropriate library – if any – to enable CORS for your application. I used https://www.nuget.org/packages/Microsoft.AspNetCore.Cors – but use the right version for your purposes.
- Open your
Startup.cs
or wherever you bootstrap your application. - Just below
.UseRouting()
you want to add in your code for enabling CORS which is.UseCors()
Of course, if that were all this would be easy. It’s never easy!
Therefore, just plopping in .UseCors()
won’t work because it’s using the default policy which is nothing – it’s empty.
Providing a loose policy
Luckily I have already gone through this, but for your own sake I recommend at the very least perusing these links:
- https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-7.0
- https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.cors.infrastructure.corspolicybuilder.allowanyorigin?view=aspnetcore-7.0
Everything I am showing here I picked up from those links.
// Example configure method. You may not be using the same one.
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
ILoggerFactory loggerFactory)
{
var isDevelopment = env.IsDevelopment();
// Lots of other configuration and stuff
// Common bootstrapping
app
.UseABC()
.UseXYZ()
.UseYoMomma()
.UseRouting();
//CORS must be set after routing and before authentication
//Optional configuration for if you deploy to a local IIS instance
//It won't be in development mode after being deployed
var enableCors = Convert.ToBoolean(Configuration["enableCors"]);
/* isDevelopment: Applies only to local development from IIS Express
* enableCors: Applies when deploying to a local IIS copy.
* Therefore `isDevelopment` will be false in this context. So use this
* config value to override the environment flag. This is for DEV only. */
if (isDevelopment || enableCors)
{
SetLooseCorsPolicyForDevelopmentPurposesOnly(app);
}
//I get this warning and I am ignoring it for now.
#pragma warning disable ASP0001 // Authorization middleware is incorrectly configured
app.UseAuthentication()
.UseAuthorization()
.UseEndpoints(endpoints => { endpoints.MapControllers(); });
#pragma warning restore ASP0001 // Authorization middleware is incorrectly configured
// Lots of other configuration and stuff
}
/// <summary>
/// This should only be used for Development purposes to satisfy browser CORS policy requirements.
/// Since this API is used in conjunction with a ReactJS component this is necessary for local development.
/// In production all origins could be identical because all of your applications should be hosted by
/// the same origin. Alternatively use a CORS Proxy (not explained here sorry - different topic).
/// </summary>
/// <returns>
/// An allow everything CORS Policy which is inappropriate for production purposes. These headers
/// are set to allow everything. The equivalent to telling CORS policy to shut up and stop complaining.
/// Access-Control-Allow-Origin: *
/// Access-Control-Allow-Methods: *
/// Access-Control-Request-Headers: *
/// </returns>
private static void SetLooseCorsPolicyForDevelopmentPurposesOnly(IApplicationBuilder app)
{
app.UseCors(policy =>
{
policy.AllowAnyOrigin();
policy.AllowAnyHeader();
policy.AllowAnyMethod();
});
}
The take away from the above is to do the following things:
- Ensure that you are only enabling this loose CORS policy for development purposes only.
- Your loose CORS policy should just allow everything including murder so it doesn’t bother you while you are developing. Of course this may not apply to everyone.
- If you deploy your application, it will no longer be running as a development application. You can optionally include an override configuration value for local IIS testing. This obviously should remain FALSE for production purposes.
I hope this saves people time. Not sure why these things can’t just be spelled out like I just did above.