Last Updated: September 09, 2019
·
10.72K
· irakli

Javascript-Friendly REST API with CORS

Cross-origin resource sharing restriction is a major pain in the neck for front-end Javascript code trying to interact with APIs on other domains.

As far as any blunt generalization goes, this is what is true:

If you are building a RESTful API, enable the most permissive CORS policy to be nice to front-end Javascript code.

This is much better than spending time on implementing JSONP given that CORS is now supported by all major browsers.

The headers you would want to set in your response:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,OPTIONS,HEAD,PUT,POST,DELETE,PATCH
Access-Control-Allow-Headers: origin, x-http-method-override, accept, content-type, authorization, x-pingother, if-match, if-modified-since, if-none-match, if-unmodified-since, x-requested-with
Access-Control-Expose-Headers: tag, link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes

This is how you write corresponding Connect.js middleware for Node.js:

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,OPTIONS,HEAD,PUT,POST,DELETE,PATCH');
    res.header('Access-Control-Allow-Headers', 'origin, x-http-method-override, accept, content-type, authorization, x-pingother, if-match, if-modified-since, if-none-match, if-unmodified-since, x-requested-with');
     res.header('Access-Control-Expose-Headers', 'tag, link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes');
    next();
};

module.exports = function allowCORS() {

  return allowCrossDomain;

};

For PHP/Zaphpa: https://github.com/zaphpa/zaphpa/blob/master/plugins/ZaphpaCORS.class.php

11 Responses
Add your response

Great post! Theres some additional headers you can set, like "Access-Control-Allow-Credentials: true", which will allow different-origin servers to read cookies in the request. They're all listed @ https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS?redirectlocale=en-US&redirectslug=HTTP_access_control#The_HTTP_response_headers

over 1 year ago ·

Nice. The only thing I don't know about is x-pingother, I'll have to check that out.

over 1 year ago ·

X-Requested-With is also worth adding for interoperability with some client side JavaScript frameworks out there, not least jQuery and MooTools.

over 1 year ago ·

What about IE 7 compatibility? http://caniuse.com/#search=cors

over 1 year ago ·

@jmav, I would push back very hard on supporting archaic browsers like IE7 in 2013, but if you simply have to and it's not in your control then you will probably have to fall back to XDM for IE7: https://github.com/oyvindkinsey/easyXDM

over 1 year ago ·

This is a handy snippet, but it might be worth noting what such a "most permissive" policy means since it's not always desirable.

If you use this policy, and if you're providing an API at MyRESTService.com, then a user might browse to EvilDomain.com and receive JS from EvilDomain.com that launches requests to MyRESTService.com and (if I recall correctly) that even uses authentication cookies from MyRESTService.com. So this is not what you want, if you want to prevent foreign domains from triggering cookie-authenticated requests to your API without the user's knowledge.

If you want something more restrictive, I researched this in painful detail on nginx: https://gist.github.com/algal/5480916

over 1 year ago ·

@algal,

thanks for raising that point, but let me clarify why that is not relevant in the specific context of REST APIs.

Cookies are actually not allowed in the given example. As @awinder noted to allow cookies you would have to indicate ""Access-Control-Allow-Credentials: true", which is not part of the example given.

Furthermore, even if you did allow cookies, RESTful APIs use stateless HTTP interactions and therefore typically do not use cookies at all, let alone: for authentication. If they do: they really shouldn't be. And that's the whole point of this tip: cross-domain restriction that exists as a security measure in general isn't very relevant or meaningful for RESTful APIs.

over 1 year ago ·

@irakli, thx for easyXDM. But easyXDM still doesn't support IE7 natively (it uses flash). We still have more than 15% of users with IE7, we simply can't ignore them. Do you have any other alternative solution, other than proxy?

over 1 year ago ·

@jmav, FlashTransport is one of three fallbacks. There's also NameTransport and HashTransport that easyXDM will try to use: https://github.com/oyvindkinsey/easyXDM#how-easyxdm-works-

Do any of those work in your scenario? If not then JSONP and/or proxy may be the only options :(

over 1 year ago ·

@irakli, IE7 uses FlashTransport fallback, so JSONP will be good enough alternative.

over 1 year ago ·

@irakli sorry, you're right. I thought you were setting Access-Control-Allow-Credentials: true, but clearly you're not, and that's the super permissive setting to be a bit more careful about.

However, I think I disagree with your second point. I think this is an issue even if you're creating a RESTful API. For one, allowing credentials will allow foreign JS to launch requests that use HTTP Basic authorization credentials cached in the browser, which is quite orthodox, so this a problem even if you're not using cookies.

Second, I'm not convinced using persistent authentication cookies means you're not being RESTful. If the cookie holds all the user info, encrypted, then there's no additional server state beyond what you'd have in order to maintain a user accounts table, right? I don't see how cookies are worse than any other token you hand a client for later authentication, like a token set in a custom header. But I may be fundamentally misunderstanding how one is "supposed to" do RESTful authentication or what counts as such. I welcome education!

over 1 year ago ·