Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
John Barker

Facebook and GooglePlus Javascript SDK sign in with Devise + RoR

John Barker
Friday, January 18, 2013

Recently I added a modal sign in and sign up dialog to a Rails application that allowed for sign in using Facebook or Google as well as via email. This dialog can appear any time a user attempts to perform a protected action, allowing them to sign in and continue without losing any data.

To make this work I had to implement Google and Facebook sign in using the new javascript SDK provided for both platforms. The old style authentication redirects when successful which means any in memory session state is completely lost. This means forms are cleared, event handlers are rebound and any work in progress has to be done again.

Before I explain the difficulties we had getting this to work with I’ll explain briefly how OAuth works with respect to devise.

Typical OAuth workflow

Most sites implement this kind of workflow by opening a popup window pointing at their oauth request url (e.g: /oauth/facebook/). This sets up the initial state of the session using a cookie and redirects to the login page providing a url to it to redirect the user again once they’ve authenticated.

If authentication is successful, an oauth token is generated and stored in a cookie and the user is redirected to the callback url. The callback url hits the devise stack and verifies that the token is real by asking Facebook to verify it. If everything checks out execution dips into your application code and the user is created or looked up by some identifiable piece of information.

Client Side workflow

The new SDKs provided by Facebook and Google have changed this and allow for much greater flexibility. Facebook documents its new login workflow here http://developers.facebook.com/docs/howtos/login/getting-started/ and Google documents theirs here: https://code.google.com/p/google-api-javascript-client/wiki/Authentication.

Both APIs have a simple method which expects a callback. The callback is executed indicating whether authentication was successful and it’s up to you what you do with that information. This allows for greater flexibility and smoother transitions into an authenticated step.

Getting it to work with Devise

There are a number of gotchas with using the client side approach, some of them related to possible bugs and my difficulty in interpreting just how the OAuth devise gems work.

OAuth Gem Version

Omiauth OAuth2 version 1.1 recently introduced CSRF validation for the authentication workflow. Unfortunately this breaks client side validation since there is no request component.

For now I suggest downgrading those gems like so:

# NOTE: omniauth-oauth2 breaks client authentication, see here:
# https://github.com/intridea/omniauth-oauth2/issues/20
gem 'omniauth-oauth2', '~> 1.0.0'
# NOTE: omniauth-facebook 1.4.1 breaks SDK authentication, see here:
# https://github.com/mkdynamic/omniauth-facebook/issues/73
gem 'omniauth-facebook', '= 1.4.0'

The comments will give any future reader of your Gemfile an indication of what they need to do to lift the version restrictions on the omniauth dependencies.

Google OAuth2 Token Validation

The current omniauth-google-oauth2 gem will try to validate your access token with a different server and request format to the one required by the new javascript SDK. For the time being you can use our the forked version (here)[https://github.com/pivotal-geostellar/omniauth-google-oauth2/tree/client_login].

Performing Authentication

For the Facebook SDK I authenticate like so:

FB.login(function(response) {
  if(response.authResponse) {
    $.ajax({
      type: 'POST',
      url: '/users/auth/facebook/callback',
      dataType: 'json',
      data: {signed_request: response.authResponse.signedRequest}
      success: function(data, textStatus, jqXHR) {
        // Handle success case
      },
      error: function(jqXHR, textStatus, errorThrown {
        // Handle error case
      })});
  }
, scope: 'email'});

For Google:

var scope = 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile';
gapi.auth.authorize({client_id: 'client id', scope: scope}, function(result) {
    if(result && !result.error) {
      $.ajax({
        type: 'POST',
        url: '/users/auth/google_oauth2/callback',
        data: {code: result},
        dataType: 'json',
        success: function(data, textStatus, jqXHR) {
          // Handle authorized case
        },
        error: function(jqXHR, textStatus, errorThrown) {
          // Handle error case
        }
      });
    } else {
      // Handle unauthorized case
    }
});

Both of these calls hit the OAuth callback endpoint to verify the access tokens obtained by the user. If authentication succeeds you’ll get the typical devise+OAuth workflow and a session omniauth.auth cookie with the appropriate details.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 1/21/2011: Rubymine helpful hint of the day

Pivotal Labs
Friday, January 21, 2011

Ask for Help

“What do you recommend for Devise user invitations?”

Interesting Things

  • Did you know that Rubymine can search files found with a previous search? Do cmd-shift-f and do a search. Do cmd-shift-f again. In the dialog, select ‘custom’. In the drop down list is ‘Files in Previous Search Result’
  • Former Pivot Alex Chaffee is teaching a javascript class, and there are still a few openings.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 1/20/2011: Admin flag with Devise?

Pivotal Labs
Friday, January 21, 2011

Ask for Help

“Any suggestions for analytics on iPad events?”

Questions like, how many times has this video been played or how many times has this object been tapped.

One suggestion was to hit a web server somewhere with some details and use splunk to analyze it.

“Any experience using a flag to indicate an administrator in Devise?”

Some indicated that this is difficult to do with Devise, and recommended against it. Someone else pointed out that the Device site actually includes this as an example.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
David Stevenson

Standup 11/22/2010: How can a rails engine provide migrations?

David Stevenson
Monday, November 22, 2010

Ask for help

  • We just upgraded one project from Devise 1.1 to Devise 1.2 and reported “many problems which blew up all sorts of stuff”. It was bad enough we had to rollback. Are there others with failure or success stories for this upgrade?
  • Can you rate limit EC2 nodes using an Elastic Load Balancer? We’d like to cap the amount of traffic that can be sent to an app instance. I’m thinking advanced use cases like this are probably why you run your own haproxy instead of using ELBs.
  • How are people running database migrations in their engine gems? I know rails 3.1 promises to bring this to the table, but is there a backport gem we can use?

Interesting

  • iOS 4.2 is out! We’re looking forward to trying it on the iPad (finally).
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mike Gehard

Devise 1.1.3 gotcha…

Mike Gehard
Friday, November 5, 2010

In the continued saga that is the Rails 2.3.10 to Rails 3.0.1 upgrade, I found this little nugget today…you want to add a custom action to a controller that inherits from a Devise controller. Here are the steps that you need to follow:

1) Create a custom controller that overrides some out of the box Devise actions as well as adds a new method.

class RegistrationsController < Devise::RegistrationsController
  def update
     # do something different here
  end

  def deactivate_owner
    # deactivate code here
  end
end

2) You have to tell Devise to use the new controller in routes.rb as well as add the new route to the new action.

devise_for :owners, :controllers => { :registrations => "registrations" } do
  post "deactivate_owner", :to => "registrations#deactivate_owner", :as => "deactivate_owner_registration"
end

When we initially implemented this, our route definition looked like this:

post "deactivate_owner", :to => "registrations#deactivate_owner", :as => "deactivate_owner_registration"
devise_for :owners, :controllers => { :registrations => "registrations" }

When it was implemented this way, we kept getting a AbstractController::ActionNotFound exception. Once we passed the block to devise_for seen in #2, everything worked fine.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (778)
  • rails (113)
  • testing (87)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (54)
  • techtalk (44)
  • rspec (38)
  • activerecord (29)
  • productivity (29)
  • gogaruco (29)
  • ironblogger (29)
  • git (28)
  • nyc (27)
  • rubymine (25)
  • bloggerdome (22)
  • mobile (22)
  • cucumber (20)
  • process (19)
  • pivotal tracker (19)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • tdd (13)
  • selenium (12)
  • css (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to devise Feed
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >