Ember.js and Rails Authentication Gotchas

I’ve been working on an app that is Ember.js on the front-end and Ruby on Rails on the back. Communication between the two has been pretty painless with Ember Data’s RESTAdapter and Active Model Serializers, but I ran into some snags with user authentication. Here’s some things I learned.

CSRF tokens with Ember AJAX requests

In order to comply with with Rails’ built-in Cross-site request forgery protection, you need to send a generated CSRF token with any request. Rails is typically generous in supplying this via a hidden input when using form_for and form_tag and is added to all AJAX requests when using the jquery-rails gem. An Ember app most likely has no need for jquery-rails, so I ripped out the relevant request header piece. Just make sure <%= csrf_meta_tags %> is in your layout’s <head> and add the following to your bundled scripts:

1
2
3
4
$ ->
  token = $('meta[name="csrf-token"]').attr('content')
  $.ajaxPrefilter (options, originalOptions, xhr) ->
    xhr.setRequestHeader('X-CSRF-Token', token)

Sorcery remember_me cookie header

For my ruby authentication gem, I went with Sorcery over the popular Devise since I didn’t need a toolbox of features or its generated views. Creating signup and login methods in UsersController and SignupController respectively were easy enough, but I couldn’t get the remember_me option to be honored when submitting the form with AJAX. Upon digging through Sorcery’s source, the issue was a hard-coded option of httponly: true when setting the remember_me cookie. When this was set to false, the cookie was properly returned in the JavaScript response header. I created a pull-request for overriding this option in Socery’s initiliazer which was recently merged into master, so this shouldn’t be an issue moving forward.

Ember route before filter redirection hook

Now that Rails wasn’t barking about CSRF tokens and was retaining the current_user, I needed a way to prevent the Ember app from entering a route where a logged-out user didn’t belong. In an effort to keep it simple, I’m using a simple extension to Ember.Route:

ember-redirection.coffee
1
2
3
4
5
6
7
8
Ember.Route.reopen

  setup: ->
    beforeFilterRedirection = Em.get(@, 'beforeFilterRedirection')
    if beforeFilterRedirection? and (redirection = beforeFilterRedirection())?
      @router.transitionTo redirection
    else
      @connectOutlets.apply(@, arguments)

When the route is setup, it checks to see if the route has a function defined called beforeFilterRedirection and if it does, it evaluates it and if the function returns a value, it is assumed to be the route to redirect to and sends you over that way. Otherwise, it continues onward to connect the route’s outlets like normal. An example implementation would look like this:

router.coffee
1
2
3
4
5
6
7
...
  dashboard: Em.Route.extend
    route: "/dashboard"
    connectOutlets: (router) ->
      router.get('applicationController').connectOutlet('userDashboard', App.currentUser)
    beforeFilterRedirection: ->
      "welcome.index" unless App.currentUser

Comments