HTML5 apps created using a third party backend run into same-origin restrictions. Browsers have historically implemented these restrictions to increase security by disallowing connections to potentially malicious external websites. In the backend-as-a-service model, the app and the backend are hosted on two different domains. Even though the domains trust each other, the browser’s same-origin policy implementation is still triggered.
One way developers get around the problem is by creating a simple proxy on the app hosting domain, which forwards requests to the backend domain. This is not a great solution as the app hosting then has to worry about a different request volume and pattern that may not be their specialty. It also makes debugging a production issues much harder. A better approach is to use JSONP, however, this makes the application code more clunky and confusing.
Luckily, WebKit-based browsers like Mobile Safari and the Android browser have support for W3C’s Cross-Origin Resource Sharing specification, making it possible for the app to cleanly make requests against third party domains without any additional logic necessary in the client app. What is important in this case, however, is for the backend server to properly handle CORS request headers and send the right responses.
Over the winter holidays we at Kinvey added CORS support to our API. Here are the main points to keep in mind.
1. Preflight (OPTIONS) requests
Preflights are necessary to ensure that the actual request will be allowed. They are normally not required for simple methods like GET and POST. However, since Kinvey and most backends require authentication, and the HTTP Authorization header is not a simple header, preflight will be performed by the browser. This means that you should not require authentication on preflight requests, as this leads to a catch-22 problem, as described here.
When your server gets an HTTP OPTIONS request, you need to look for the Origin header. The value of that header is set by the browser to the domain where the app came from. Your server should then decide whether to allow that request or not and this is typically done by looking up some whitelist containing your customers’ domains. The second thing to do is to specify what method and headers would be allowed in the actual request for the resource.
You should also set the Access-Control-Allow-Credentials header to true, since you are expecting an Authorization header on the actual request. Access-Control-Allow-Methods header has to be set to contain the methods allowed for the preflighted resource. For example, if you only implement GET and POST for users accounts (and not DELETE, but rather inactivate via PUT), you should respond with ‘Access-Control-Allow-Methods: GET,POST’. If the developer performs a DELETE request, it will be stopped by the browser. In Access-Control-Allow-Headers, specify the headers you allow, for example ‘Content-Type’. Finally, use Access-Control-Max-Age to control the caching interval for the preflight request. You should set it to some large value depending on how soon your response might change, for example, when the origin gets excluded from the whitelist, or you add a new method in your API.
2. Actual requests
On actual request with Origin headers you should also set the Access-Control-Allow-Origin and Access-Control-Allow-Credentials headers. Remember, some requests will not be preflighted by the browser, so if you don’t set those headers, the browser will throw an error. Also, even with a preflight, the browser will look at the headers again before propagating the response up to XHR.