Keep cors error under control⚒️
Tags: webdev, beginners, programming, tutorial
Published: 2023-03-19 09:19:11

<small>Welcome to <b>part 3</b>. Follow along this series to develop a <em>feature-packed</em> 3-tier Authentication System outlined in successive parts.</small>

_**Summary:** In this article we will learn to configure an express backend application to understand CORS Protocol_.

> _**Note 🔔**_:  
> _If you would like to jump ahead to the final Authentication System put together, the complete Git Repo can be found on [Github](https://github.com/hane-smitter/MERN_login_system)✩._

Let's start here with an example case.<br />
You have built a stunning user interface 😎. Now you are ready to pull data from external API and inject in the UI for display. You have scripts that make HTTP requests to API. But something is standing in the way 🛑. The console blazes fiery red and ranting🗣️:

![access-control-allow-origin header not present](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhgpc1u099l1p7bxyjv1.png)

Tellingly, you have run into _CORS error_.

<details>
<summary><b>Table of Contents</b></summary>
<ol>
<li><a href="#why-cors-error">Why CORS Error?</a></li>
<li><a href="#what-is-cors">What is CORS?</a></li>
<li><a href="#enable-cors">Enable CORS</a></li>
<li><a href="#enable-cors-for-credentialed-requests">Enable CORS for credentialed requests</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ol>
</details>

## Why CORS Error?

Using the example above, this error occurs because UI is loaded on a different port, _i.e http://localhost:3000_ and the scripts in it are requesting to a different port _i.e http://localhost:8000_. So scripts are making cross-origin requests. Well, _Same-origin_ policy is violated! And therefore the browser blocks such a request 👊.<br />
_**Note:** Two URLs have the **same origin** if the **protocol**, **port** (if specified), and **host** are the same for both_.

It is a security motive that browsers restrict cross-origin HTTP requests initiated from scripts. **"XMLHttpRequest"** and **"Fetch"** are browser APIs that adhere to _same-origin policy_. This means that a web application using these APIs can _only_ request resources from _the same origin the application was loaded from_. In layman's language, _**scripts in a web page can "peacefully" request to the URL shown in the address bar** without being hurled with confrontations by the browser_. This way, the nightmarish _CORS error_ is well tucked out of sight.<br />
**But with special configurations, cross-origin requests can succeed**. However, these configurations need to be done on the backend rather than the frontend. These configurations are what we will be looking at in this article.

<!-- _unless the response from other origins have CORS enabled(include CORS headers)_. -->

So,

## What is CORS?

_CORS_ is an abbreviation for _Cross-Origin Resource Sharing_.<br />
**It is a standard that allows a server to moderate _same-origin policy_ in browsers**, so cross-site requests made to it can be allowed. And this involves sending appropriate CORS headers that will be verified in the browser.

Ultimately this means that if you run into a CORS error, the fix lies in backend configuration.

## Getting started

This is how we organize the nodejs project.

Start by creating a `server` directory and change into it:

```bash
d="server" && mkdir $d && cd $d
```

Inside `server` directory, run `npm init -y` to create a `package.json` file. Add/edit the `package.json` file to include the following script:

```js
// ...
"scripts": {
    "start": "node src/index.js",
}
// ...
```

Run `npm install express --save` to install express web framework.<br />
Then create `src` directory(`mkdir src`) inside this `server` directory. Below is the overall directory structure.

### Directory structure

```diff
📂server/
    ├── 📄package.json
    └── 📂src/
        ├── 📄index.js
+       └── 📂config/
+            └── 📂cors/
+                └── 📄cors.js
```

This is the structure with focus on the `src` directory. The `src` directory will contain files with code we write for our project. We will add code in these files as we progress through the article. You can always double check to create these files in their correct locations.

## Enable CORS

We are going to configure CORS protocol on our express backend. The CORS protocol simply consists of a set of headers that indicates whether a response can be shared with the origin that requested.

The set of headers that can be set on response to a CORS request are listed [here](https://fetch.spec.whatwg.org/#http-responses).

To reduce the work of setting these headers manually, we will use an npm library called [cors](https://npmjs.com/package/cors). So be sure to install it:

```bash
npm install cors --save
```

Then open the entry point of our application file, i.e `index.js`, located at `server/src/`([refer](#directory-structure "section: directory structure")). Add the `cors` middleware as shown in section `2`:

```javascript
const express = require("express");
const cors = require("cors");

/* 
   1. INITIALIZE EXPRESS APPLICATION 🏁
*/
const app = express();
const PORT = process.env.PORT || 8000;

/* 
   2. APPLICATION MIDDLEWARES AND CUSTOMIZATIONS 🪛
*/
app.use(cors()); // Enable Cross Origin Resource Sharing

/* 
   3. APPLICATION ROUTES 🛣️
*/

/* 
   4. APPLICATION ERROR HANDLING 🚔
*/

/* 
   5. APPLICATION BOOT UP 🖥️
*/
app.listen(PORT, () => console.log("App listening on port " + PORT));
```

_**Note:** The file `index.js` has commented sections that are numbered. Some sections have logic defined in other parts of the series_.

Yes, dead simple as that. The CORS headers will be set on HTTP response by the `cors()` middleware when the browser sends a preflight request and the actual request.

By default, `cors` library sets `Access-Control-Allow-Origin` header with a wildcard(`*`) value on the actual response.

While this configuration of `cors` library works, it will fail for Cross-Orign requests that include **credentials**. Because a request that includes credentials(e.g `Cookie` header), requires additional headers configured and that the headers are set explicitly(_not wildcarded_). Therefore a header including a wildcard(`*`) value will fail.

> **Credentials** are _cookies_, _authorization headers_, or _TLS client certificates_.

Below is an example of a failure message when a request with credentials receives a response with `Access-Control-Allow-Origin` header set with a wildcard(`*`):

<figure>
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ylgqt0vpbblcror0jrkm.png" alt="cors error" />
<figcaption>
CORS error when <code>Access-Control-Allow-Origin</code> header is a wildcard
</figcaption>
</figure>

`cors` library needs additional configuration to set CORS headers appropriately for requests including credentials.

## Enable CORS for credentialed requests

Like said earlier, credentialed requests are requests that include credentials, _i.e cookies,authorization headers or TLS client certificates_.

The Authentication System we are building in this series will make use of cookies, so it's important to configure our backend to understand CORS protocol for credentialed requests.

Open the file `cors.js` which should be available at path `server/src/config/cors/`([refer](#directory-structure "section: directory structure")). Create the constituent directories if you do not have.

Paste the following code inside `cors.js` file:

```javascript
const allowlist = ["http://localhost:3000", "http://localhost:8000"];
const corsOptions = {
	origin: function (origin, callback) {
		if (allowlist.includes(origin)) {
			callback(null, true);
		} else {
			callback(new Error("Not allowed by CORS"));
		}
	},
	credentials: true,
	exposedHeaders: ["WWW-Authenticate"],
};

module.exports = corsOptions;
```

We are allowing an `origin` that is in `allowlist` to access our API. Anything else is locked out; represented by passing a first argument to the `callback` function: `callback(new Error("Not allowed by CORS"));`.<br />
This cors option will **explicitly set `Access-Control-Allow-Origin` response header** to the value of origin that has been cross-checked to exist in the `allowlist`.

The next property option is:

```javascript
credentials: true,
```

This cors option will **set `Access-Control-Allow-Credentials` response header to _true_**, which basically gives an okay that our server will accept cookies from a cross-origin request(_Client application_). The browser requires that this header exists in response to preflight request and before setting a cookie in client device as directed by `Set-cookie` header of a response received.

Then we have:

```javascript
exposedHeaders: ["WWW-Authenticate"],
```

This cors option will **set `Access-Control-Expose-Headers` response header to _WWW-Authenticate_**.
This is not mandatory but if you need to access this header(_or any other_) with scripts running in the browser, then you need to set it.

You may set other options you may need. The options we have set here should be able to understand the CORS protocol for credentialed requests.<br />
What is remaining, is to include these options to the CORS middleware from the entry file of our application. Therefore open `index.js` found at `server/src/`([refer](#directory-structure "section: directory structure")) and include these options in cors middleware as shown:

```javascript
const corsOptions = require("./config/cors/cors.js");

// ...

/* 
  2. APPLICATION MIDDLEWARES AND CUSTOMIZATIONS 🪛
*/
app.use(cors(corsOptions)); // Enable Cross Origin Resource Sharing
// ...
```

Congrats! This is the way! Now you can seamlessly make cross-site requests to your backend. Whether the request include credentials or not, CORS error should not intrude.

## Conclusion

We have looked at how to solve the CORS error you may run into, why it happens and what it really is. CORS error happens because the browser is reminding you to implement security in your application. We have seen how to we can configure CORS in an application that intends to work with requests that include credentials such as cookies.

If you can control the backend, then you can fix CORS error, otherwise the fix is out of your control. You still may opt for hacky workarounds such as proxying requests.

Fixing CORS error generally implies setting response headers.<br />
These specific headers can be set using third-party libraries like [cors](https://www.npmjs.com/package/cors) which is well battle-tested and effective in its job. However, you can still set the headers manually(<small><i>which creates room for errors 🤕</i></small>).

Did I leave out something? You can put it in the comments section.

---

_We can connect [@twitter](https://twitter.com/intent/user?screen_name=smitterhane) and share ideas._