9/14/19

Nodejs CORS Handling No Access-Control-Allow-Origin


When accessing API calls from another domain or port number, we have to be mindful of the possible CORS policy restrictions.  CORS stands for Cross-Origin Resource Sharing. This is used to prevent access from one domain to another without having the policy to enable that access.  A common use case is one an application hosted on one domain tries to access the API hosted on a remote domain. When the calling domain is not whitelisted for access, an error is raised and the call is denied.



Access Error

To illustrate this problem, we can look at an app hosted on our localhost but with a different port number. We should notice that port number makes the policy applicable even when the domain is the same.


Access to XMLHttpRequest at 'http://localhost:5000/api/data' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

When making a CORS request to a different domain, there would be two requests made to the server, the preflight and actual requests. For each of these requests, the server must respond with the Access-Control-Allow-Origin header set with the name of the domain of origin (calling app) or a wildcard ‘*’ to allow all domains. Wild card are a bit too open, so this is typically not used for secured apps.

Preflight Request

A preflight or OPTIONS (HTTP verb) request is created by the browser before the actual request (PUT, POST) is sent for a resource in different domains. The goal is to have the browser and server validate that the other domain has access to that particular resource. This is done by setting the Access-Control-Allow-Origin header with the client host domain.

Actual Request

Once the preflight request has a response with the corresponding headers and HTTP 200 status, the browser sends the actual request. For this request, the server also checks the CORS policies and adds the Access-Control-Allow-Origin header with the client host domain.
NodeJS Middleware

Now that we understand about this security concern, we can take a look at how we can build a middleware with NodeJS to help us manage this integration.

Let’s start by defining a common NodeJS Express application. When we build the bootstrap code for the server, we can include a middleware module (auth-cors) which is our implementation to handle the CORS concerns. This is done by loading the module using the require directive and loading the module script relative to where the loading script is found. Let’s review the code to see how we can do that.


const express = require("express");

// middleware for cors
const cors = require("./middleware/auth-cors");

const PORT = process.env.port || config.port;
const app = express();

// initialize modules
app.use(bodyParser.json());
app.use(
  bodyParser.urlencoded({
    extended: true
  })
);

// add the cors middleware to the http pipeline
app.use(cors);

// start the server listener
app.listen(PORT, () => console.log(`Listening on ${PORT}`));

After we load the CORS module, we can associate it with the application by calling app.use(cors). This is a way to tell the server application to pass the context references to each module in the HTTP  pipeline.

Now that we understand how to include the middleware module, we can take a look at the actual implementation below. We start by exporting the module definition and implementing the RequestHandler interface contract which enables the module to receive the HTTP pipeline context with the request, response, and next references. The next context enables the code to continue the pipeline request execution to complete. This is the context that all middleware modules should return to chain the other middleware modules in the pipeline.

module.exports = (req, res, next) => {
  if (req.headers.origin) {

    // add the remote domain or only the environment
    // listed on the configuration
    res.setHeader(
      "Access-Control-Allow-Origin",
      process.env.domain || req.headers.origin
    );
  }

  // Request methods you wish to allow
  // remove the methods you wish to block
  res.setHeader(
    "Access-Control-Allow-Methods",
    "GET, POST, OPTIONS, PUT, PATCH, DELETE"
  );

  // Request headers you wish to allow
  res.setHeader(
    "Access-Control-Allow-Headers",
    "Origin,Authorization,X-Requested-With,content-type,Accept"
  );

  // Set to true if you need the website to include cookies in the requests sent
  // to the API (e.g. in case you use sessions)
  res.setHeader("Access-Control-Allow-Credentials", true);

  if ("OPTIONS" === req.method) {
    return res.sendStatus(200);
  }

  return next(); // Pass to next layer of middleware
};


What is the module really doing?

The module basically intercepts the request from the HTTP pipeline to add the headers that can enable a client application to send a request from another domain. 

The Access-Control-Allow-Origin header is added to the response header to include the remote domain. This is the area where we can whitelist some domains and not allow others. In this example, we are just adding the remote domain which should not be the normal case. The same approach is taken to Allow Methods and Headers that our application supports. For example, to block the Delete method, we do not add that value in the header. 

The other interesting header to include or exclude based on your application requirements is the  Access-Control-Allow-Credentials header. This is used for cases when you want the client application to send back credential information in the header. This is useful for session management state and authorization token information.

Summary

It is important to understand that we run an application and APIs on different domains, we have some security concerns that we need to plan for. This can enable our apps to safely use remote APIs and prevent malicious attacks to our servers. With NodeJS, building middleware components to handle this concern is fairly simple. Most of the understanding is around how the HTTP protocol works to enable us to handle this integration. 

Please let me know your feedback, and  I will add clarification to the article.

Thanks for reading


Originally published by ozkary.com