Devise, OmniAuth, Facebook and Rails 4.2

rails

I work on a lot of ruby on rails web applications. Usally, to get the ball rolling, we start with just a regular email and password authentication system using devise. This is a great way to get started since it is well supported, familiar, and allows us to conveniently create and log in with various test accounts.

However, in 2015, nearly every web application will need to have authentication via some OAuth21 service. Several large networks allow this - Google, Facebook, GitHub, Twitter, and LinkedIn are the most popular, though there are strong arguments that developers really should support systems like OpenID as well. Thankfully, getting authentication going from a variety of OAuth providers is pretty easy with Devise and Omniauth. However, there are a few gotchas.

Initial Setup of Omniauth-Facebook

Assuming you already have Devise installed and set up, you’ll need to add the omniauth gem to your gemfile, and a gem for one of the omniauth authentication providers. For this example, we’ll use omniauth-facebook.

Currently, Facebook is pushing developers to not use the traditional redirect-based authentication system, and instead use Facebook’s JavaScript SDK 2. Setting this system up is a bit different than a classic OAuth setup, and was new to me.

You’ll need to create an app on developers.facebook.com (you’ll need a Facebook account). When you do so, select ‘website’ as the app type. The nice onboarding page will prompt you to ‘Setup the Facebook SDK for JavaScript’; I copied this code into a partial so that it does not pollute my views. It’ll then prompt you to add a like button - this is a reasonably good test of whether the Facebook Javascript SDK is working properly. Don’t forget that you will likely have to add an override to your ad blockers.

Once this is going, you can follow the instructions that Facebook provides on setting up the JavaScript SDK and login.

Gotcha, Turbolinks!

You will find that Rails' use of turbolinks prevents the login button from being set up correctly when the page is loaded; the JavaScript you included from Facebook only runs when the page is loaded. There is, however, a solution from Nick Reed.

Mr. Reed uses CoffeeScript, so I’ve translated his solution to regular JavaScript. Just remove the script Facebook had you put at the start of your <body> element and put this script in one of your regular javascript files in /app/assets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
var fb_root = null;
var fb_events_bound = false;

$(document).ready(function() {
  loadFacebookSDK();
  if(!fb_events_bound) {
    bindFacebookEvents();
  }
});

function bindFacebookEvents() {
  $(document).on('page:fetch', saveFacebookRoot);
  $(document).on('page:change', restoreFacebookRoot);
  $(document).on('page:load', function() {
    if(FB) {
      FB.XFBML.parse();
    }
  });

  fb_events_bound = true;
}

function saveFacebookRoot() {
  fb_root = $('#fb-root').detach();
}

function restoreFacebookRoot() {
  if($('#fb-root').length > 0) {
    $('#fb-root').replaceWith(fb_root);
  } else {
    $('body').append(fb_root);
  }
}

function loadFacebookSDK() {
  window.fbAsyncInit = initializeFacebookSDK;
  $.getScript("//connect.facebook.net/en_US/all.js#xfbml=1");
}

function initializeFacebookSDK() {
  FB.init({
    appId: '<your app id>',
    status: true,
    cookie: true,
    xfbml: true
  });
}

With this, you’re almost done. You have the user’s browser sending requests to Facebook’s servers, receiving authorization, and then returning the bits of information you need to log the user in. All you need to do is get that authentication information onto your server - which is pretty standard Omniauth.

This post will be updated once this process is complete.


  1. OAuth and OAuth2 are open standards for authenticating against trusted third party servers.

  2. No doubt to complete their mission to destroy the web.