2

Part of my Express configuration looks like this and runs on different domain

app.use(function(req, res, next) {
    res.setHeader("Access-Control-Allow-Origin", 'http://localhost:3000');
    res.setHeader("Access-Control-Allow-Credentials","true");
    res.setHeader("Access-Control-Expose-Headers", "Set-Cookie");
    res.setHeader("Access-Control-Allow-Headers", "Content-Type, x-xsrf-token, X-Requested-With, Accept, Expires, Last-Modified, Cache-Control");
    res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE");
    next();
});
app.configure('development', 'production', function() {
    app.use(express.csrf());
    app.use(function(req, res, next) {
        res.cookie('XSRF-TOKEN', req.csrfToken());
        next();
    });
});

When using CORS there is OPTIONS request before anything different than a GET. The server is manually configured to respond to it with 200 every time, so it proceed to the POST or whatever. The requests come from Angular. Some said I need to add some of these lines in order to configure Angular properly.

$httpProvider.defaults.xsrfCookieName = 'XSRF-TOKEN';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.withCredentials = true;

The error which I get is 403 Forbidden, the only solution so far is to comment out the whole app.configure(..) in the server. Obviously, I have some problem with CSRF and I cannot understand what should I do.

EDITED

tottomotto
  • 2,193
  • 2
  • 22
  • 29

3 Answers3

1

This section of the doc says that

For requests without credentials, the server may specify "*" as a wildcard, thereby allowing any origin to access the resource.

Wildcard is used for completely public APIs which don't require an auth. If you want to use credentials (via cookies) you've got to set the exact list of allowed urls in Access-Control-Allow-Origin

Make sure res.setHeader("Access-Control-Allow-Credentials","true"); is used at the back end like:

 app.use(function(req, res, next) {
    res.setHeader("Access-Control-Allow-Origin", 'YOUR URL HERE');
    res.setHeader("Access-Control-Allow-Credentials","true");
    res.setHeader("Access-Control-Expose-Headers", "Set-Cookie");
    res.setHeader("Access-Control-Allow-Headers", "Content-Type, x-xsrf-token, X-Requested-With, Accept, Expires, Last-Modified, Cache-Control");
    res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
});

Also the $httpProvider.defaults.withCredentials = true; should be used at the angular side to allow sending the credentials.

angular.module('CoolModule')
  .config(['$httpProvider', function($httpProvider){
    // For Access-Control-Allow-Origin and Set-Cookie header
    $httpProvider.defaults.withCredentials = true;
  }]); 

or $http({withCredentials: true, ...}).get(...)

Hope this helps.

EDIT: Not a solution, but as a workaround to allow the OPTIONS request this piece of code could be added:

...
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
if ('OPTIONS' == req.method) { 
  res.send(200); } else { next(); 
}
...
Igor Malyk
  • 2,646
  • 2
  • 27
  • 34
  • Igor, this works partially. I've added next(); after all Access control configurations and I got 403 again, **but it sends cookies** now. Without next() the OPTIONS request is pending forever... So do I need to stop the OPTIONS request or add cookies to it? Or I am on the wrong foot again – tottomotto Mar 04 '14 at 11:54
  • 403 means 'forbidden' somehow the request gets dropped. If you use Chrome you could read the reason. At least for me Chrome indicates it was forbidden because of the CORS headers configuration. Please try this as a workaround, let's see if it helps at all --- add "if ('OPTIONS' == req.method) { res.send(200); } else { next(); }" after all the res.setHeader statements – Igor Malyk Mar 04 '14 at 12:37
  • This is getting an error, because it receives the OPTIONS request before Allow-Origin configuration. I fixed the router to respond with 200 on every OPTIONS request, but this leads to 403 again. However, I commented out the csrf configuration and now everything works. Obviously, this is not the solution, because now the server is open to csrf attacks. – tottomotto Mar 04 '14 at 13:00
  • Have you added the `if ('OPTIONS' == req.method) { res.send(200); } else { next(); }` after the `Access-Control` headers ? If yes it should return 200 OK for every OPTIONS request without errors. – Igor Malyk Mar 04 '14 at 14:07
  • Did you added necessary headers for xsrf ? – Igor Malyk Mar 04 '14 at 14:08
  • Yes, I am getting 200 from the options, this is sorted out. I've added all header configuration that you written, but it doesn't work when csrf is enabled. When it is not, everything works fine – tottomotto Mar 04 '14 at 14:26
  • Tell me please how do you enable csrf protection at both frontend and backend ? – Igor Malyk Mar 04 '14 at 15:31
  • I have edited the whole question. Take a look if you have time – tottomotto Mar 05 '14 at 09:12
0

You need to enable CORS support your angular.js app:

yourApp.config(['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.useXDomain = true;
        delete $httpProvider.defaults.headers.common['X-Requested-With'];
    }
]);
TheHippo
  • 61,720
  • 15
  • 75
  • 100
  • I tried this few times and it does not make any difference. Actually, I found out that the POST request is not sending the cookies properly. [this thread](http://stackoverflow.com/questions/20427778/cors-angularjs-not-sending-cookie-credentials-with-post-or-delete-but-get-ok) – tottomotto Mar 04 '14 at 10:34
  • Then it would be nice if you could include this kind of information in your question, which would have saved me the time of looking this up and typing this answer. – TheHippo Mar 04 '14 at 10:40
  • CORS issues are usually solved server-side, not client-side, so this answer is unlikely to be of help. – Ray Nicholus Mar 04 '14 at 14:07
0

This should sort you out.

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');

    next();
}

app.use(allowCrossDomain);
GaryDevenay
  • 2,405
  • 2
  • 19
  • 41