Search code examples
node.jsreactjscorsmicroservicesexpress-gateway

Restricted by CORS policy even after enabling CORS on Express and Express gateway


I'm using Express Gateway to proxy my microservices. I'm hitting the gateway from my react app with Axios. However, the call gets restricted by the CORS Policy. I have enabled CORS in the express gateway and also on my authentication service. I followed the official documentation to enable the CORS from this link for Express Gateway and this link for authentication service(Express App). Here is how I have the code.

Express Gateway config.yml

policies:
  - log
  - proxy
  - jwt
  - rate-limit
  - request-transformer
  - cors

pipelines:
  authPipeline: 
    apiEndpoints: 
      - auth
    policies: 
      -
        cors:
          -
            action:
              origin: '*'
              methods: 'HEAD,PUT,PATCH,POST,DELETE'
              allowedHeaders: ['Content-Type', 'Authorization']
      - 
        log: 
          action:
            message: 'auth ${req.method}'
      - 
        proxy:
          action: 
            serviceEndpoint: di

Authentication Service app.js

const cors = require('cors');
// Enabling CORS
const corsOptions = {
  origin: '*',
  methods: ['POST', 'GET', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
}
app.use(cors(corsOptions));

The Headers I get back from the server when I try with Insomnia (a client like Postman) can be seen in the below picture.

The Headers I get from the server

I get this error in the browser console.

Access to XMLHttpRequest at 'http://127.0.0.1:8080/api/v1/users/login' from 
origin 'http://localhost:3000' has been blocked by CORS policy: Response to 
preflight request doesnt pass access control check: No 'Access-Control- 
Allow-Origin' header is present on the requested resource.

I'm not sure what I'm missing. Please let me know if you need anything else, I'd be quick to provide. Each and every attempt that tries to explain what's wrong with my case is really appreciated.

EDIT: Adding the API call snippet

const config = {
    headers: {
      'Content-Type': 'application/json',
    },
  };
const body = JSON.stringify({ email, password });
const res = await axios.post('http://127.0.0.1:8080/api/v1/users/login', body, config);

Solution

  • Your "methods" definition is incorrect. It needs to be in the form of a YAML array:

    methods: [ "HEAD", "PUT", "PATCH", "POST", "DELETE" ]
    

    Or

    methods:
    - "HEAD"
    - "PUT"
    - "PATCH"
    - "POST"
    - "DELETE"
    

    Or

    methods:
    - HEAD
    - PUT
    - PATCH
    - POST
    - DELETE
    

    Everything else looks okay, although you may want to add "origin" to the list of allowedHeaders.

    Update I have done some further testing on this, using this mini express server, then run the API gateway and server on Docker (with appropriate hostname changes in gateway.config.yml), and everything works as I expect with testing via curl from localhost and also from the browser.

    var express = require('express')
    var app = express()
    
    // Enabling CORS
    const cors = require('cors');
    const corsOptions = {
      origin: '*',
      methods: ['POST', 'GET', 'PATCH', 'DELETE'],
      allowedHeaders: ['Content-Type', 'Authorization']
    }
    app.use(cors(corsOptions));
    
    function addPath(path) {
      app.use(path, function(req, res, next) {
        const response = `Hello from ${req.method} ${path}`;
        console.log(response);
        res.send(response);
      });
    }
    
    addPath('/api/v1/users/*');
    addPath('/api/v1/*');
    
    const port = 5000;
    app.listen(port, function () {
      console.log(`Example app listening on port ${port}`)
    })
    

    The only other thing I can suggest is to add another log message before the cors policy entry in gateway.config.yml like this:

    - log: action: message: "incoming (auth): ${req.method} ${req.path} requesting IP: ${req.ip} origin ${req.headers.origin}"

    and check the value of origin. If it is not undefined, try adding the following response-transformer before the proxy policy (adding response-transformer to the list of available policies, of course). I have had to do this once or twice but I forget the circumstances where it was necessary, so this is a shot in the dark.

      - response-transformer:
        - condition:
            name: expression
            expression: 'typeof req.headers.origin !== "undefined" && req.headers.origin === "http://localhost"'
          action:
            headers:
              add:
                access-control-allow-origin: '"http://localhost"'