fqpuug
Last Updated: February 25, 2016
·
7.232K
· irakli
Avatar ika

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

Say Thanks
Respond

12 Responses
Add your response

10071
Img 0018

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 ·
10098
Psb  1

good

over 1 year ago ·
10129
519fb76548875.preview 620

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

over 1 year ago ·
10147
Be9f8a7e49b430ac89efe1bc8aa1026d

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 ·
10160

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

over 1 year ago ·
10161
Avatar ika

@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 ·
10173
4b71cdf53bc76edd1a2ab66c446954b5

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 ·
10174
Avatar ika

@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 ·
10175

@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 ·
10176
Avatar ika

@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 ·
10180

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

over 1 year ago ·
10184
4b71cdf53bc76edd1a2ab66c446954b5

@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 ·
Filed Under