Ruby, JavaScript, Sass, iOS. Stinky Cheese & Beer Advocate. Working at CustomInk and loving it!

RESTful AJAX with Forgery Protection

Writing the new HomeMarks has been a great exercise. I've learned that the AJAX Head Design Pattern Implementation is more akin to developing a service-oriented application (SOA) since I have moved all client-side coupling from the controllers, like RJS, and only respond with HEAD or JSON data. Today I learned about Halcyon which is self described as a JSON web application framework built on Rack. If you take a look at their example, it looks a lot like HomeMarks v2, pretty cool!

Getting To The Point

Last week I finally got around to doing the controller CRUD for the user's column objects. The core homemarks/app.js JavaScript class contains the one (and only one) AJAX dispatcher/handler for the entire application. One problem that I ran into was how to continue to use forgery protection in rails to protect from CSRF attacks while relying solely on ad-hoc JavaScript requests to a RESTful back-end. I could have turned off protect_from_forgery but that did not feel right. I wanted RESTful AJAX with forgery protection.

My Solution

Since the HomeMarks v2 app has one AJAX dispatcher/handler for the entire app, all I had to do was to get the authenticity token into each and every request. It took a bit of digging in the rails source to find the methods I wanted but here both the ERB snippet I am using in my layout and the helper method found in my application_helper.rb file.

<script type="text/javascript">
  <%= auth_params_js_var %>
</script>
def auth_params_js_var
  unless RAILS_ENV == 'test'
    %|var authParams = $H({#{request_forgery_protection_token}:#{form_authenticity_token.inspect}});|
  end
end

What does this do? It creates a JS variable called authParams at the window level (right term?) that all other functions can use. That variable returns a JavaScript hash that will have a single key/value pair for the rails authenticity token, see below. Basically at this point all you have to do is merge this hash with your parameters option for your AJAX object. You can reference the HomeMarks app.js file in the resources section if you want to see how I did this on my app, code snippet below too.

doAjaxRequest: function(elmnt,finishMethod) {
  // ...
  var method = elmnt.method || 'post';
  new Ajax.Request(elmnt.action,{
    onComplete: function(request){
      this.completeAjaxRequest(request);
      if (finishMethod) { finishMethod.call(this,request) };
    }.bind(this),
    parameters: parameters.merge(authParams),
    method: method
  });
}

Resources

  • The Latest HomeMarks app.js Class. See doAjaxRequest() function.