10

CORS not working properly for domain and subdomain. I've a single NodeJS server hosted at https://api.example.com I've two ReactJS Clients

Client 1. https://example.com

Client 2. https://subdomain.example.com

I've configured Client 1 to force www so that a single origin is used for Client 1. When I open Clien1 that works fine. When I open Client2 I get the error that

Access to XMLHttpRequest at 'https://api.example.com/someAPI' from origin 'https://subdomain.example.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://www.example.com' that is not equal to the supplied origin

When I press Ctrl+F5 then it works fine but after that If I refresh Client1 then the error comes at Client1

Access to XMLHttpRequest at 'https://api.example.com/someAPI' from origin 'https://www.example.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://subdomain.example.com' that is not equal to the supplied origin.

Now When I press Ctrl+F5 at Client1 then it works fine but the same error goes to the Client2.

My nodeJS server is configured as follows

var whitelist = ['https://www.example.com', 'https://subdomain.example.com'];
    getCorsCoptions(req, callback) {
    var getOriginValue = () => {
      if (whitelist.indexOf(req.header('origin')) !== -1) {
        return true;
      } else {
        log.error(__filename, 'Not allowed by CORS', origin);
        return false;
      }
    }
    var corsOptions = {
      origin: getOriginValue(),
      methods: ['GET', 'POST'],
      credentials: true,
      preflightContinue: false,,
      allowedHeaders:["Content-Type","X-Requested-With","X-HTTP-Method-Override","Accept"]
    }
    callback(null, corsOptions)
  }

app.use('*', cors(this.getCorsCoptions));
app.options('*', cors(this.getCorsCoptions));

I'm using axios at React Client sides as follows

get(url: string) {
    return Axios.get(url, {
      withCredentials: true
    }).then((res: any) => {
      if (res) {
        return res.data;
      }
      return {};
    });
}

post(url: string, data: any) {
    let requestHeaders = { 'Content-Type': 'application/json' };
    return Axios.post(url, data, {
      headers: requestHeaders
      , withCredentials: true
    }).then((res: any) => {
      if (res) {
        return res.data;
      }
      return {};
    });
}
Cory Danielson
  • 14,314
  • 3
  • 44
  • 51
Inaam ur Rehman
  • 470
  • 1
  • 6
  • 23

3 Answers3

11

I found a solution to this problem. The browser was caching origin. Solved this problem by adding the following header in response

Cache-Control: no-store,no-cache,must-revalidate

I've also added Vary: Origin but i think it didn't solve the problem I've managed cors as follows

var whitelist = ['https://www.example.com', 'https://subdomain.example.com'];
router.all("*", (req, res, next) => {
    var origin = req.headers.origin;
    if (whitelist.indexOf(origin) != -1) {
      res.header("Access-Control-Allow-Origin", origin);
    }
    res.header("Access-Control-Allow-Headers", ["Content-Type","X-Requested-With","X-HTTP-Method-Override","Accept"]);
    res.header("Access-Control-Allow-Credentials", true);
    res.header("Access-Control-Allow-Methods", "GET,POST");
    res.header("Cache-Control", "no-store,no-cache,must-revalidate");
    res.header("Vary", "Origin");
    if (req.method === "OPTIONS") {
    res.status(200).send("");
      return;
    }
    next();
});

where router is express.Router()

I hope that someone find this helpful.

Inaam ur Rehman
  • 470
  • 1
  • 6
  • 23
0

It seems like you're using express. Based on their docs, you can set origin to an array of strings or RegExp which should allow the origins that you want.

origin: Configures the Access-Control-Allow-Origin CORS header. Possible values:

  • Array - set origin to an array of valid origins. Each origin can be a String or a RegExp. For example ["http://example1.com", /.example2.com$/] will accept any request from “http://example1.com” or from a subdomain of “example2.com”.

Based on that, updating getOriginValue() to the following might work for you:

var getOriginValue = () => {
  if (whitelist.indexOf(req.header('origin')) !== -1) {
    return whitelist; // Return array of strings
  } else {
    log.error(__filename, 'Not allowed by CORS', origin);
    return false;
  }
}
Cory Danielson
  • 14,314
  • 3
  • 44
  • 51
0

It's because you're calling your function, you need to just pass it to the middleware

var corsOptions = {
      origin: getOriginValue, // Not origin: getOriginValue()<-no parens,
      methods: ['GET', 'POST'],
      credentials: true,
      preflightContinue: false,,
      allowedHeaders:["Content-Type","X-Requested-With","X-HTTP-Method-Override","Accept"]
    }
djheru
  • 3,525
  • 2
  • 20
  • 20