TAG cors

CORS with Sencha Touch

A while back I was working on the mobile version of techup.ch which you can find on m.techup.ch. The web application is built with Sencha Touch. I did not want to deploy to m.techup.ch yet, which is where I ran into issues with the same origin policy.

In order to prevent web applications gaining access to things they should not have access to there is a same origin policy, which means that AJAX requests can only be made to the same host and port that the website is hosted on. This prevents CSRF attacks, as an attacker cannot make your browser do arbitrary actions on remote websites.

Our Sencha Touch app however relies on a JSON API, that is served from techup.ch. Since the app itself is not hosted on that domain (yet), it cannot access the API.

A workaround: JSONP

While AJAX is restricted by the same origin policy, script tags are not. It is possible to include script tags to a remote site, which is often used to embed widgets from sites like Facebook or Twitter. It can also be abused to fetch data from a remote domain.

This works by adding a script-tag proxy pointing to the API, including a callback parameter in the query string, for example:

1
http://techup.ch/api/events/upcoming.json?callback=processData

The API will detect the callback parameter and pad the response with a javascript call to the data it returns.

Conventional Reponse

{
    "events": [
        {
            "id": 361,
            "name": "Linalis LPI 201"
        }
    ]
}

JSONP Response

processData({
    "events": [
        {
            "id": 361,
            "name": "Linalis LPI 201"
        }
    ]
});

And this is what the “P” in JSONP is. JSON with padding.

Of course the processData function has to exist. In fact, jQuery’s AJAX function has built in support for JSONP. If you have callback=? in the requested URL, it will automatically use JSONP for the request. Sencha Touch also supports this through Ext.data.ScriptTagProxy.

But this is more a hack than a real solution. Enter CORS.

Cross-Origin Resource Sharing

CORS is a W3C Working Draft which describes an extension to HTTP for allowing browsers to issue AJAX request across domains. The server serving this request can send additional headers notifying the client of these additional permissions.

The only header that needs to be set is:

Access-Control-Allow-Origin: *

This will allow AJAX requests from any domain. Instead of the wildcard, you could also specify allowed domains explicitly. Only set the wildcard if it is safe to do so. You need to be aware that any site could get any user to make a request to those resources on your site that have this header, which can very easily lead to CSRF attacks.

Now, Sencha Touch (as well as many other AJAX libraries) will also send a X-Requested-With header, allowing the server to detect that the request was sent through JavaScript. CORS forces the server to specify which headers can be sent by the client. That is done as folllows:

Access-Control-Allow-Headers: x-requested-with

This is a comma-delimited list, so you can add more headers if needed.

If you want to allow other HTTP methods than GET, you’ll have to specify that explicitly with yet another header:

Access-Control-Request-Method: GET,POST

Authentication

By default the browser will not send any cookies with CORS requests. You can however set the Access-Control-Allow-Credentials header with a value of

1
true

, which will allow cookies to be sent. In this case you may however want to explicitly define a range of allowed origin domains.

Alternatively you can handle authentication yourself. In this case the user will have to provide username and password to the app, which will then make a request to the API. The API will respond with an authentication token, which can be used in subsequent authenticated requests to the API. This way you do not have to rely on cookies. You can store this token in localStorage, so you don’t loose it on page reloads.

Browser support

A recent article describes the browser support as follows:

  • Webkit browsers: good
  • Gecko browsers: good
  • Trident browsers (Internet Explorer 8+): good with some gotchas
  • Opera: very sucky a.k.a. non existent

Since we are targeting mobile here, which is in this case pretty much webkit only, we can just use it.

Conclusion

CORS is a nice solution for remote API access from mobile web applications.

Check out enable-cors.org.

November 3, 2011 by Igor Wiedler in Development // Tags: , , 13 Comments