eZ Community » Blogs » Gaetano Giunta » Easy mashups with eZPublish part II -...

By

Easy mashups with eZPublish part II - solving the same-origin problem for ajax calls

Tuesday 25 September 2012 11:21:10 pm

  • Currently 3 out of 5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

In the previous installment of this series, I have shown how easy it is to integrate data coming from external services in your eZ Publish website.

Now, it is time to delve in the possibility of using ajax to access the remote service, and have a look at the same-origin problem and the different tools at our disposal to solve it.

Goal

Wouldn't it be great if we could just use javascript to load the weather data from the Weather Underground webservices and use it to decorate our eZ Publish site?

Clear advantages include:

  • if the remote server responds slowly, the rest of the page server by eZ still loads fast
  • we reduce the amount of work done by the eZ Publish server

Problem

Well, this is in fact possible, and I am pretty sure you are familiar both with the term xhr and the ajax facilities provided by javascript libraries such as jQuery and YUI.

In fact both jQuery and YUI are included in eZ Publish and can be loaded in a snap using the eZJSCore extension, so you might be wondering whether there is any problem at all?

The sad truth is that there is indeed a problem, and it is called "same origin request policy".

If you are not familiar with the concept, it is the part of the http protocol which specifies that web pages can not send arbitrary http requests to servers on different domains from the one which is serving them.

A web page on my.domain cannot send an xhr request to your.site.com, only GET requests are allowed.

This is a cornerstone of the web security model and a very desirable feature, but also a great pain in the neck to deal with when developing mashup applications: as you all know by now, REST is all bout proper usage of the http protcol, including POST, PUT and DELETE calls.

Workarounds

Common workarounds include jsonp and a plethora of nasty hacks. There's even a w3c specification called CORS, which sounds great in theory but is seldom used in practice (as it needs lots of support on the remote server).

JSONP is the most common workaround, is supported by jQuery in an almost transparent way, and is even supported by the Weather Underground services.

So why go further in our quest for a solution?

1. For starters, JSONP is not secure. If the remote server suffers an XSS attack, the attack propagates automatically to your site

2. Then, you need the remote server to support JSONP. Some don't.

3. JSONP is also subject to XSRF attacks. This is not something you should worry too much when consuming webservices, but it might be a concern for the developer implementing it

So, is there another solution?

Solution: the "proxy"

Since the browser can not access services on a different domain, we'll just use ajax to consume a service offered by our eZ Publish site: a "proxy" service which will forward all calls to the remote server. In images:

browser ---> eZ Publish ---> Remote server

Advantages of this solution:

  • access to remote server is triggered by an ajax call: it does not slow down your webpages
  • you keep your API key hidden within eZ Publish config files. It can not be copied and then abused by any visitor of your site
  • proper access control is enforced. You might have user groups who can access the remote services, and some who don't
  • if the remote server is not public but enforces authentication, this will be easier to set up (all calls to the service will come from the webserver hosting eZ and not from end users browsers)
  • all accesses to the remote server's api are traced/logged on your server, in case you need to do some forensic analysis
  • you might introduce a caching layer so that the calls to the remote server are less frequent and keep your usage within the data plan you are paying for

Disadvantages:

  • more http requests to the eZ Publish server
  • ajax calls will be just a wee bit slower

Implementation

Step 1: configure in wsproviders.ini.append.php acces to the WU server (see previous blog post)

 

Step 2: configure, using the Roles & Policies page in the Administration interface, which user accounts will have access to sending calls to the WU server.

Module: webservices

Policy: proxy

Limitation: wunderground

 

Step 3: rewrite your page template to use ajax instead of making direct calls to the WU server

{ezscript_load( array( 'ezjsc::jquery', 'ggwstemplate::jquery::json', 'ggwstemplate::jquery::jsonrpc' ) )}
<script type="text/javascript">
{literal}
$(document).ready(function()
{
    jQuery.wsproxy(
        'wunderground',
        'forecast/q/Norway/Oslo.json',
        [],
        {
            success: function(r){
                alert(r.responseJSON.content.forecast.txt_forecast.date);
            },
            error: function(r){
                alert(r.statusText);
            }
        }
    );
});
{/literal}
</script>

 

Step 4: there is no step 4. It's done!

Debriefing

  • you will need version 0.13 or later off ggwebservices for this to work
  • the jQuery.wsproxy function is where all the magic happens. You just need to load the appropriate javascript library using ezcss_load or ezcss_require to make it available to your web page
  • The YUI version is called Y.io.wsproxy. To have it available, load ggwstemplate::yui3::jsonrpc
  • Both functions access the webservices/proxy view in eZ
  • There is no need to "stub" (reimplement, or "wrap") the api of the remote server: it is completely available and at your disposal. Note that this might also be a security issue: take care about authenticated users sending rogue calls to webservices/proxy
  • The protocol used between the browser and eZ is in fact JSONRPC, which gets converted to REST by the extension when contacting the backend server. Responses get transparently transcoded as well
  • Currently, only GET requests can be forwarded to the backend, not PUT/POST/DELETE ones. But in a future release, who knows...
Proudly Developed with from