Nodejs Express CORS issue with 'Access-Control-Allow-Origin'

Viewed 20967

I'm trying to create a single page app using Angular 1, Nodejs and Express. I'm using Angular's $http post feature to post a request where I pass header values in the request to an API endpoint.

I am encountering an error message in Chrome:

XMLHttpRequest cannot load 
http://localhost:7878/EIAMIDSupportREST/EIAMIDSupport/updateEIAMID. The 
value of the 'Access-Control-Allow-Origin' header in the response must not 
be the wildcard '*' when the request's credentials mode is 'include'. Origin 
'http://localhost:3000' is therefore not allowed access. The credentials 
mode of requests initiated by the XMLHttpRequest is controlled by the 
withCredentials attribute.

With this request body:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,PUT,POST,PATCH,DELETE
Access-Control-Allow-Headers: Content-Type
Allow: POST
Content-Type: text/html; charset=utf-8
Content-Length: 4
Date: Tue, 23 May 2017 23:32:15 GMT
Connection: keep-alive

To remedy the CORS issue, I've npm installed the cors library.

In my app.js, I have the add the following lines:

var cors = require('cors');
var app = express();
app.use(cors());

Here is my full app.js:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var index = require('./routes/index');
var users = require('./routes/users');

var cons = require('consolidate');
//enable CORS 
var cors = require('cors');
var app = express();
app.use(cors({ origin: 'http://localhost:3000' , credentials :  true,  methods: 'GET,PUT,POST,OPTIONS', allowedHeaders: 'Content-Type,Authorization' }));
// view engine setup
app.engine('html', cons.swig)
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', index);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

And here is my index.html page

<html>
<head>
<script 

src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript">
var app = angular.module("app", []);
   app.controller("HttpGetController", function ($scope, $http) {
       $scope.SendData = function () {
         var req = {
          method: 'POST',
          url: 'http://localhost:7878/EIAMIDSupportREST/EIAMIDSupport/updateEIAMID',
          withCredentials: true,
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Authorization': 'Basic user:password'
          }
         }
         $http(req)
           .then(function(data, status, header, config)
             {
               $scope.PostDataResponse = data,
               console.log($scope.PostDataResponse);
             })
             .catch(function(data, status, header, config)
             {
                $scope.PostDataResponse = data,
                console.log($scope.PostDataResponse);
             });

       };

     });
</script>
</head>
<body>
  <div ng-app="app">
    <div ng-controller="HttpGetController">
      <button  ng-click="SendData()" >Link Accounts</button>
      <hr />AND THE RESPONSE IS:{{ PostDataResponse }}
    </div>
  </div>
</body>
</html>

However, it still does not work.

Can anyone please advise how and where I need to update my code to fix the 'Access-Control-Allow-Origin' wildcard issue?

4 Answers

There is also an option to set origin to true to reflect the request origin, as defined by req.header('Origin').

app.use(cors({ origin: true, credentials: true }));

Just in case anyone wants to use a pure javascript based solution, here is the code that is working on my Google Chrome (80), Mozilla Firefox (72) and Microsoft Edge browsers.

Development platform

I'm using NodeJS (12) to develop a small distributed REST API. This set of services use different port numbers to separate them if they are deployed on the same machine.

In the other hand, a couple of front-end applications should interact with many services at the same time. So, I started getting this CORS error in the integration phase. The solution that I found for javascript is as follows.

Server Side code

Here, the trick is very simple. You just want to add the following lines in every response.

res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4000');

res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Accept');

res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');

NOTE: res is the HTTP Response object.

Client Side code

In this case, the request is coded as usual. The only thing that we need to know, is the browsers will send first a preflight request using the HTTP "OPTIONS" method, before each request that we want to send. This preflight request, will tell the browser whether your original request's answer will be received or not, according to its internal CORS polices.

In this example, I was sending just a JSON object using the POST method:

var xhr = new XMLHttpRequest();

xhr.open(method, requestUrl);

xhr.setRequestHeader('Content-type', 'text/plain');

OPTIONS Method Response handler

In order to handle the OPTIONS request accordingly, you have to check in the server side as follows:

if (req.method.toLowerCase() == 'options')

res.writeHead(200);

And that's it, no extra complications.

Tokens

When you need to send a token via header, then you must add "token" to your "options" response as follows:

res.setHeader('Access-Control-Allow-Headers', 'Content-Type, token');

More Information

https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

Related