DEV Community

Cover image for Keep cors error under control⚒️
Smitter
Smitter

Posted on

Keep cors error under control⚒️

Welcome to part 3. Follow along this series to develop a feature-packed 3-tier Authentication System outlined in successive parts.

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✩.

Let's start here with an example case.

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

Tellingly, you have run into CORS error.

Table of Contents
  1. Why CORS Error?
  2. What is CORS?
  3. Enable CORS
  4. Enable CORS for credentialed requests
  5. Conclusion

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 👊.

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.

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.

So,

What is CORS?

CORS is an abbreviation for Cross-Origin Resource Sharing.

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:

d="server" && mkdir $d && cd $d
Enter fullscreen mode Exit fullscreen mode

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

// ...
"scripts": {
    "start": "node src/index.js",
}
// ...
Enter fullscreen mode Exit fullscreen mode

Run npm install express --save to install express web framework.

Then create src directory(mkdir src) inside this server directory. Below is the overall directory structure.

Directory structure

📂server/
    ├── 📄package.json
    └── 📂src/
        ├── 📄index.js
+       └── 📂config/
+            └── 📂cors/
+                └── 📄cors.js
Enter fullscreen mode Exit fullscreen mode

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.

To reduce the work of setting these headers manually, we will use an npm library called cors. So be sure to install it:

npm install cors --save
Enter fullscreen mode Exit fullscreen mode

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

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));
Enter fullscreen mode Exit fullscreen mode

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(*):

cors error

CORS error when Access-Control-Allow-Origin header is a wildcard

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). Create the constituent directories if you do not have.

Paste the following code inside cors.js file:

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;
Enter fullscreen mode Exit fullscreen mode

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"));.

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:

credentials: true,
Enter fullscreen mode Exit fullscreen mode

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:

exposedHeaders: ["WWW-Authenticate"],
Enter fullscreen mode Exit fullscreen mode

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.

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) and include these options in cors middleware as shown:

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

// ...

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

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.

These specific headers can be set using third-party libraries like cors which is well battle-tested and effective in its job. However, you can still set the headers manually(which creates room for errors 🤕).

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


We can connect @twitter and share ideas.

Top comments (0)