Update on Lanyon + Cross-domain AJAX POSTs with CORS

Series: worklog August 21, 2011

An update is long past due on Lanyon (my Jeykll WYSIWYG editor project) so here is a recap since the last post.

As of my last update, I had figured out how to make a commit with the Github API. Everything was working with curl, but when I hooked up the code to call the API from the browser I ran into issues regarding same-origin policy when doing cross-domain AJAX requests.

For GET requests, there were no issues since I can use JSONP. By including a callback function in my JavaScript code, the server will respond with my function with inlined arguments populated by the requested data. This function is then injected into the DOM and wrapped in a <script> tag.

To trigger this behavior, I just needed to set the dataType option of the jQuery $.ajax() call to be jsonp and set the jsonpCallback option to my callback function.

For POST requests, it’s a whole different game. If you try to just do the POST normally, you will find yourself with a “same origin policy” error. Searching the web will reveal a ton of information about different work-arounds - the most popular being to use an <iframe>, use some other plugin like easyXDM, or write your own server proxy.

The <iframe> approach relies on setting the frame’s domain to be the same as your target and then submitting an injected form to the desired URL. I started going down this path but I found that you couldn’t get the response from the POST, which was critical in my case.

The easyXDM route involves communicating between your main window and an <iframe> using the postMessage API supported in some browsers. I didn’t really grok how it was supposed to work, but I believe it would require changes to both my domain and my target domain. And since I can’t control Github’s domain, I don’t think it would fit my needs.

The last route, a server proxy, seemed to be most feasible. I could throw together a little Sinatra app that proxied my requests over to api.github.com and since I would control the server, I could perform all of the requests at once and have control over the cross-domain policies.

The problem with this approach was that it would need maintenance and extra resources. The whole point of the project was to make the requests on the client side so that anyone could drop the script into their blog. Now I would have to keep the server up and running and now people have to trust that I’m not storing their credentials on my server (I wouldn’t trust someone else with them).

After doing more research, I found that, at one point, the Github API supported CORS, which is a spec that allows for exemptions to the same-origin policy. I sent a message to Rick at Github and, to my surprise, he worked with me to re-enable CORS support! One restriction is that your origin needs to be registered as an OAuth app, but that was a non-issue.

I had some trouble with jQuery when making the request, but I finally got the right combination of settings and it started working. Here is some sample code for making the POST:

$.ajax({
    type: 'POST',
    url: 'API URL goes here',
    data: JSON.stringify(data),
    dataType: 'json',
    contentType: 'application/x-www-form-urlencoded',
    success: callback,
    beforeSend : function(xhr) {
	    xhr.setRequestHeader("Authorization", 
            "Basic " + that.base64encode(user + ':' + pw));
    }
});

For whatever reason, using the username and password options weren’t working and my authorization headers weren’t being sent so I had to manually set the header and base64 encode my credentials.

I still need to do a bit more work related to setting the YAML front matter for the Jekyll posts and to make some of my calls more generic (everything is hard coded to do the git operations in a test repo of mine). But I was able to write a post and get it committed and pushed to a repository from the browser.

And then onward to editing posts and making the UI look pretty!


built with , Jekyll, and GitHub Pages — read the fine print