Cross-domain AJAX

March 17, 2010 by: admin

As mashups are getting more and more common a problem that keeps arising is how to include data from external domains in a safe way.

The reason why this is difficult is because the current implementations of the XMLHttpRequest API disallows access to other domains as a security measure (to avoid CSRF/XSRF).

Even though newer browsers are getting support for this using the crossdomain.xml policy file, this is still a feature that older browsers are unable to use.

So how can this be done with easyXDM? Well, it’s actually quite easy as easyXDM is actually shipped with a /cors/ (index.html) interface that exposes an easyXDM.Rpc instance that again exposes the following interface

request: function(config, fn){
	.....
}

To take us of this on the consumer you simply need the following code

var xhr = new easyXDM.Rpc({
    remote: "http://other.domain/cors/"
}, {
    remote: {
        request: {} // request is exposed by /cors/
    }
});

With this up and running you can use

xhr.request({
    url: "pathRelativeToRemote/getrest/",
    method: "POST",
    data: {foo:"bar"}
}, function(response) {
    alert(response.status);
    alert(response.data);
});

There you go, cross domain ajax!

The /cors/ implementation will adhere to the CORS standard and will verify the related headers before returning the response. If you want to skip this, then you can simply white-list all domains in the interface.

Note: This is for your security – you don’t want to go exposing all the resources on your domain to anyone now do you?

For more information: see the readme at github.

You can find the demo here.

  • Name

    Any chance to pull data from a 3rd party server (ex. an RSS / ATOM feed) using easyXDM?

  • http://kinsey.no/ Øyvind Sean Kinsey

    No, easyXDM needs to be loaded on documents of both domains for it to work, either as served by the server or injected through a bookmarklet.
    Sounds like a good ol' proxy is what you needs.

    If you this is script you are going to provide to others, then you could host the proxy, and for instance something like the xhr sample<http://easyxdm.net/wp/2010/03/17/cross-domain-ajax/&gt;. So the page uses easyXDM back to your domain, and then your domain retrieves the 3rd party data using a server side proxy.

    • Shrekting

      hello, i am using ajax to call sharepoint webservice, which is in a differert domain server. i can’t touch the sharepoint server, so if i want to use easyXDM, how should i do?

  • Name

    I had a feeling that was the case, just wanted to double check… Thanks.

  • Nolochemical

    Whoa, this is great..

  • Dmrising

    It would be really helpful if you would use complete example code from top to bottom (including server side snippets to see what methods on the server you are calling and how its exposed) instead of chopped out …. bits everywhere – makes it really confusing to follow. Also i would not use terms used in messaging such as “post” as it can be easily confused with the http method type “POST” – as someone trying to use this technology (thanks for doing it) i must say if you want people to adopt it you really need to think about communicating the examples and code and its implementations in a much more cleaner, complete and less confusing (from the standpoint of a rcp newby).

    • http://kinsey.no/ Øyvind Sean Kinsey

      In this case there is no server side methods, it’s just a plain AJAX POST, with some form fields..

      And about the ‘POST’ issue, this is an interface which expose a method that does POST, so I don’t find anything wrong with calling it post.
      But that being said, I know that the content on this site is not as good as it should be, and I am working on it. Hopefully a new site will be up in not to long.

      In the mean time, take a look at the readme at github (http://github.com/oyvindkinsey/easyXDM), it explains the basics pretty well I think..

  • Dmrising

    whats with remoteUrl + “/../xhr.html” – do we need to append all remote url’s with “/../xhr.html”??

    • http://kinsey.no/ Øyvind Sean Kinsey

      No, xhr.html is the document referred to in the text as the one exposing the ajax interface.
      The sample javascript is copied from the example, and this constructs the url this way – should probably clean this up.

  • Christopher Terry

    Hi … noob to Javascript … but I think it’s the key to doing the following:
    I have an Iframe within which I want to run a foreign [cross-domain, authentication-required] webpage. The end-goal being that I wish to automate login and navigation to certain locations of the third-party-site, and display the result in my i-frame. Due to the cross-domain authentication issues with IFrame, essentially I need something to take incoming pages, store a copy of them renamed as a local page, and have the javascript interpret my page requests [on my localized renamed copy of the incoming page] and send them [along with the necessary authentication, be it cookies' location, or 'url'key] back to the third-party web server, take the resultant incoming page, and “back to the beginning”… easyXDM or what have you … I need this “spelled out for an idjut”.

    • http://kinsey.no/ Øyvind Sean Kinsey

      I did not quite get that ..
      ‘Incoming pages’ ?
      Interpret page requests using javascript?

      You might be able to use easyXDM for _something_ here, but I’m not quite sure for what as I don’t understand what you want in the first place.

      • Christopher Terry

        Hi,
        Thanks for the quick reply …
        I want to be able to run a third-party website within an IFrame on MY webpage. I can run google pictures inside an IFrame just fine, because google pictures doesn’t require authentication. The problem becomes that I want to have a website that uses authentication inside my iframe (such as gmail, for instance), and if I present credentials within the IFrame, instead of the username & password that I just attempted to enter inside my IFrame, any subsequent webpage request to Gmail ignores the authentication. The IFrame basically ignores the returned authentication. In order to circumvent the Identity presentation problem, I’ve got to find a way to present the credentials, and save the authentication, for presentation within each subsequent IFrame-based page request.
        My thinking was that any incoming webpage (ie, the post-authentication gmail page) could be analyzed or copied over locally, be presented within the IFrame as a local copy, and when a user clicks on the local copy, the outgoing request back to gmail could programatically include the authentication [cookie, token, or URL]
        Am I just completely missing the point?

        • http://kinsey.no/ Øyvind Sean Kinsey

          Yeah, I think you’re pretty much off base..

          Let me recap this:
          You have a page with an iframe, inside the iframe you have a page that requires authentication.
          You say that once you authenticate to the page inside the iframe, the authentication is not persisted across requests?

          This sounds like a Privacy issue (the browser refuses to store cookies set by iframed documents) and you should look into P3P.

          Either way, drop any thought of copying files etc. And there is no way to interact with a document cross domain without that documents consent.

          I think you would save a lot of time by finding someone who actually understands how web applications interact and talking this through with them – from experience – it could take some time and frustration getting getting your point across through writing..

  • Aresot

    Hi, I’m new to this library. I have HTTP signup page and trying to sign-up via AJAX call to HTTPS resource. Does example above sounds like right solution for my problem?

    Thank you.

    • Aresot

      A bit more info. I can do it 2 ways as I see:
      1) do HTTPS POST using hidden IFrame and retrieve back responce using easyXDM.Rpc. In this case no cors/index.html required (am I correct?)
      2) Use example above

      Which one would you recommend?

      • http://kinsey.no/ Øyvind Sean Kinsey

        No mather how you choose to solve this, you need to add code to the remote domain – if this is a change to an exiting document, or a new document (like the ready-made cors interface) is again up to you.
        The cors interface is made specifically to mimic the xhr feature with support for CORS – if you just want to have more flexibility – just use a regular RPC setup and have one of your methods use whatever ajax-library you like to talk to the server. easyXDM’s RPC calls fully support asynchronism (see the methods example).

        • Aresot

          Unrelated question: with your MIT license, what kind of reference we need to include or do we need to post link to your web-site?

          • http://kinsey.no/ Øyvind Sean Kinsey

            You need to keep the license header intact in the source – attributing the project in other ways are not mandatory but highly appreciated :)

    • http://kinsey.no/ Øyvind Sean Kinsey

      Take a look at the xhr example from http://consumer.easyxdm.net/current/example/

  • http://twitter.com/brrrtenev br-r-r! -tenev

    Hey, thanks for the library. Took me some time to set up, but cross-domain ajax works perfectly now!

    Got an issue though: when I create a new easyXDM.Rpc with a non-existant remote (e.g. a /cors/ of a server that is down) no error gets thrown. I.e. in firebug I can see that 404 (and other nasty errors) happen and are logged by the browser, but my js code is not aware of those.

    Even worse, if I try to use the created rpc object and call .request against it, it does not throw an error as well! Ofcourse, onReady is never called, but I cannot come up with a scenario where this notion can be used to detect broken Rpc objects :(.

    A scenario, when new easyXDM.Rpc gets created against a url that is online, but the resource that is accessed via .request is not available are handled just fine, though.

    Any suggestions on error handling?

    PS. I could try pinging the host with tag injection prior to creating an Rpc against its /cors/, but this does not feel robust enough…

    • http://kinsey.no/ Øyvind Sean Kinsey

      Unfortunately, there is no reliable way to detect a failure. What you can do is to set a timeout that you clear in the onReady function, which which would throw some error, or set the reference to the rcp object to null, in turn throwing errors on use.

      If you think such a timeout could be built in, then maybe you could add it as an issue at github?

      • http://twitter.com/brrrtenev br-r-r! -tenev

        Thanks for swift response and for an idea of onReady usage with timeout. Will give it a thought.

  • Jeff Kopmanis

    Its not entirely clear that EasyXDM is required on both consumer and producer from the instructions above, but if you forget, the Firebug errors indicate that its not present and needed. It might be worthwhile making mention of that in the tutorial above.

    • http://kinsey.no/ Øyvind Sean Kinsey

      That might be, but I cannot really explain everything in each example now can I :)
      But I see how some things could have been described more clearly..

  • bj55555

    Is there any way to set the accept header? It currently defaults to “*/*”, and I need it to be “application/json”. 

    • http://kinsey.no/ Øyvind Sean Kinsey

      Just set a ‘header’: {key:value} property and this will be set on the request.

  • Maham

    Hi , i m having problem in calling IFrame which is using cross domain contents, basically what i have to do is that there’s a authentication page on my website and it should authenticate to another 3rd party website, and after successful login it should display on the IFrame of my website.
    I tried to pass the credentials through querystring from my website to 3rd party site, and successfully it gets login and displaying their content after login, but as i have to display the contents inside the iframe in my website so it doesn’t work for it.

    Please help me out, my head is spinning by trying numerous way to solve it but invein , i m newer to development.

  • BPM

    It would be nice if you made your links have a slightly more obvious color. I sure love playing Where’s Waldo when I’m on a website.

    • http://kinsey.no/ Øyvind Sean Kinsey

      I’m a terrible designer, and this is a ready made template :)
      I’ve had plans for a major revamp of the page for ages, but have lacked the time – need to make a living you know  :)

      • BPM

        Sorry, just frustrated.

        • http://kinsey.no/ Øyvind Sean Kinsey

          Don’t be, ignore this site and instead read the readme ( https://github.com/oyvindkinsey/easyXDM/#readme ) and look through the examples :)

          • BPM

            The readme is open in the next tab. I’ve done the simple message posting between domains, and now I’m actually trying to get it to work with a rest API.

            http://cmf.convio.net/_temp/SSO/easyXDM/default.html

            I get an error…

            cmf.convio.net – 15:13:05.268:easyXDM.stack.PostMessageTransport:
            received message ‘default1368
            {“id”:1,”error”:{“code”:-32099,”message”:”INVALID_STATUS_CODE”},”jsonrpc”:”2.0″}’
            from https://secure3.convio.net

            …which doesn’t seem like its from my API. At least I don’t recognize it from when I set up DOJO.XIP.

          • http://kinsey.no/ Øyvind Sean Kinsey

            You’re not really going to have the user input his username and password on a third-party site are you?

            You should place this form in the iframe and use the container: option to display the iframe.

            The error says Invalid status code, so I’m guessing it’s not returning a 200?
            If this is the cors example, go through the cors/index.html file, you need to modify this to suit your needs anyway.

          • BPM

            Its a method with I don’t need to fool with retrieving an auth token to use, so I thought it would be easy to successfully get it to respond. I’m really just communicating between the secure and insecure domains on my own server.

            I am using CORS, and so far have just changed…

            var alwaysTrustedOrigins = [(/cmf.convio.net/)];

          • BPM

            I just checked the server API Logs and I finally see the call, but nothing is returned. Presumably this means its not easyXDM. Just a silent failure for who knows what reason. Now I remember how much I disliked the cross-domain thing when I did DOJO.

            Thanks for the help!

  • Andrea Bisello

    thanks for the library.
    i have a question.
    i have to reproduce this working get :
    ‘http://myserver.mydomain.mio:8080/topics/userpresence?role=user&username=richard’

    using javascript so i wrote (i report only hot code) :

    var xhr = new easyXDM.Rpc({
        remote: “http://myserver.mydomain.mio:8080/”

    and on the function

    url: “topics/userpresence?role=user&username=richard”

    method : “GET”

    but when i read the string with firebug i obtain :

    http://myserver.mydomain.mio:8080/?xdm_e=http%3A%2F%2F192.168.1.157&xdm_c=default2991&xdm_p=1

    ……. what about it?

    thanks

    • http://kinsey.no/ Øyvind Sean Kinsey

      That is the request for the easyXDM endpoint (should point to the CORS/ file) that is responsible for making the request. It will only be made once per Rpc object.

  • BPM

    Can you explain why in the CORS index.html file at line 145 there is this useAccessControl check which stops the status and responseText from being passed?

                                if (req.status = 300) {
                                    if (useAccessControl) {
                                        error(“INVALID_STATUS_CODE”);
                                    }
                                    else {
                                        error(“INVALID_STATUS_CODE”, {
                                            status: req.status,
                                            data: req.responseText
                                        });
                                    }
                                }

    Afterall, you can just look at the req object in FireBug to get those values, right? Why not just pass them up?

    • http://kinsey.no/ Øyvind Sean Kinsey

      I really can’t remember :)

      But it is clearly to hide such details in the case that AccessControl is used, I believe this is to avoid data leaking in the case that the consumer was really disallowed – that you can use Firebug to get to these values is irrelevant – the point is that the consumer code cannot. 
      But you are free to modify this page as you see fit..

      • BPM

        Thx for the reply, *awesome* that you do so this quickly.

        “…that you can use Firebug to get to these values is irrelevant – the point is that the consumer code cannot.”

        This is the issue I was after. I’m thinking about changing it and wondered about implications I’m not considering. What’s the worry if consumer code can see my API’s error codes? Seems like if the worry is someone probing for weakness, then isn’t firebug peeking relevant? Or is the worry that someone else will somehow use those error details along with an automated dictionary attack or something? Couldn’t someone smart enough to do that also be able to write something to peek at each one in the same way that firebug does?

        Also a little bit leery of customizing it, since it will make updating harder. (You’ve issued updates every month for the last 6 months.)

        For the same reason wouldn’t it be good to have things like alwaysTrustedOrigins located in a single config file. Who remembers to fix line 59 in a buried file after a few months?

        • http://kinsey.no/ Øyvind Sean Kinsey

          This is in order to not leak information – if a service issues one error message if signed in, but another if not, then returning the responseText would leak that information. The same goes for status codes, if doing a particular request returns different status codes depending on the user (a 404 vs a 500) then that would also leak the same info.

          Firebug is privileged code – client side scripts are not. So no, an attacker cannot do what Firebug can. This has nothing to do with probing for weaknesses, this is about attacking a user (csrf/xsrf).

          • BPM

            The wiki on csrf was interesting reading. I’d say that counts as something I hadn’t considered! Thanks.

            So why not do the AllowedOrigin check first thing, before even looking at the req.status? That way they couldn’t ever get anything back but DISALLOWED_ORIGIN.

          • BPM

            Something along the lines of…

            // define the onreadystate handler
            req.onreadystatechange = function()
            {
                if (req.readyState == 4)
                {
                    clearTimeout(timeout);
                   
                    // parse the response headers
                    var rawHeaders = req.getAllResponseHeaders(), headers = {}, headers_lowercase = {}, reHeader = /([w-_]+):s+(.*)$/gm, m;
                    while (m = reHeader.exec(rawHeaders))
                    {
                        headers_lowercase[m[1].toLowerCase()] = headers[m[1]] = m[2];
                    }

                    // confirm access is allowed
                    var errorMessage;
                    if (useAccessControl)
                    {
                        // normalize the access control values
                        var aclAllowedOrigin = (headers_lowercase["access-control-allow-origin"] || “”).replace(/s/g, “”);
                        var aclAllowedMethods = (headers_lowercase["access-control-allow-methods"] || “”).replace(/s/g, “”);

                        // determine if origin is trusted
                        if (alwaysTrusted || aclAllowedOrigin == “*” || aclAllowedOrigin.indexOf(remote.origin) != -1)
                        {
                            // determine if the request method was allowed
                            if (aclAllowedMethods && aclAllowedMethods != “*” && aclAllowedMethods.indexOf(config.method) == -1)
                            {
                                error(“DISALLOWED_REQUEST_METHOD”);
                            }
                        } else {
                            error(“DISALLOWED_ORIGIN”);
                        }
                    }

                    if (req.status = 300)
                    {
                        error(“INVALID_STATUS_CODE”,  { status: req.status, data: req.responseText });
                    } else {
                        success({ data: req.responseText, status: req.status, headers: headers });
                    }

                    // reset the handler
                    req.onreadystatechange = Function.prototype;
                    req = null;
                }
            };

          • BPM

            You could also move the parse headers bit inside of the useAccessControl block. No need to walk the headers if we aren’t using access control.

          • BPM

            Oops, wrong. The headers are passes on success regardless of useAccessControl.

          • http://kinsey.no/ Øyvind Sean Kinsey

            I really cannot say – it’s been ages since I touched this code, and it was adapted from Eli Grey’s pmxdr ( https://github.com/eligrey/pmxdr/blob/master/pmxdr-host.js).

            If you can show that your code is more correct with regards to CORS, then fork it and submit a pull request :)

            Øyvind Sean Kinsey
            oyvind@kinsey.no
            http://kinsey.no/blog/index.php/about/

          • BPM

            Thanks again for the help.

  • Andrea Bisello

    Hi. I must pass an Json object inside the body of a “Content-Type”: “application/json” call.

    I’m using this JS code

    var JSONstring=’{ … my json data … }’;
    var JSONobj =JSON.parse(JSONstring);

    xhr.request (
            {
                url: “”,
                method: “POST”,
                timeout : 60000,
                headers: { “Content-Type”: “application/json”},
                data: JSONobj

            } (… and the other code…)

    but when i debug the post using firebug i don’t see any json object but my json formatted into a string
    query=%5Bobject%20Object%5D

    how can I send my JSON object as it is?

    thanks

  • Jugglinmike

    I would like to POST a “deep” object, but it appears that all nested objects are coerced intro strings. For instance, the following data:
    {
       ”message”:{}
    }

    is sent as:

    {
       ”message”:[object Object]
    }
    is there a technical limitation behind this behavior? If so, any suggested methods for POSTing “deep” objects? Or maybe (hopefully) I’m just doing it wrong…?

    Thanks!

    • http://kinsey.no/ Øyvind Sean Kinsey

      Just serialize it yourself to JSON and send that.

  • Parimal

    Hi

    i am using mvc 3 project, my main page having 2 iframes which load content from 2 different domain. now i am getting cross domain security error when try to set src for iframe 2 from iframe1. I am new and i don’t know how to implement this library so any one help please? how i can achive this task?

  • t0mjanes

    i have a very simple demo for cross domain communication in iframes using html5′s postMessage API. it successfully works for me in all modern browsers and in IE9 as well but not in IE8 or below.
    I tried a solution of registring some html5 tags with a custom header but the window.postMessage is still something mysterious for IE8.
    Yes i know i can use various ways for cross domain in IE8 like xdr, but for cleanness somebody please comment if window.postMessage can somehow work in IE8 or below.
    Im afraid to include modernizer or your easyxdm to let my CORS remain clean. By looking at my demo plz comment if i can use html5′s cors techniques in IE browsers < ie9.

  • Armin

    first of all, thanks a lot for this great piece of software! made my coding a lot easier!
    i’m currently running into some problems though. We are using a third party desktop application to test algorithms. This software generates html testreports, which are stored on a local file. these reports are generated using a templatefile, which is the only thing i can modify in this whole process. what i’m trying to do, is to add a button to the template, which when pressed posts the contents of certain elements to php server, where the data gets parsed out and stored into a db. i tried doing this withc easyxdm (and a bunch of other solutions) however easyxdm tells me that the file protocol is not supported. Is there any way i can get around this?kind regards

    • http://kinsey.no/ Øyvind Sean Kinsey

      If post is all you need (no response), why not use a simpler approach?
      Øyvind Sean Kinsey
      San Francisco, CA

  • http://lianza.org/ Tom Lianza

    Do you know if this can be made to work with jQuery deferred objects ( 
    http://api.jquery.com/category/deferred-object/ ) ex. if I wanted to wait on 2 easyXDM-wrapped ajax calls to finish, can I get a deferred object returned from xhr.request in this example?

  • AK

    I need to implement the ajax using easyxdm but didn’t understand how can i implement the above code.Can someone show me working example ( consumer.html and provider.html) ?

  • RayDex

    It is throwing the error “unhandled error returned.” either your exlample or my code.