Skip to content

mattvanhorn/devise_facebook_connectable

 
 

Repository files navigation

DEVISE / FACEBOOK CONNECTABLE

Devise << Facebook Connect

What is Devise?

http://github.com/plataformatec/devise

What is Devise Facebook Connect?

Simple:

A very straightforward Facebook Connect authentication/linking with the ease of Devise and power of Facebooker. If I may say it myself: The easiest way to get a Rails app authorized and connected with Facebook Connect. Authentication in Rails should be straightforward, right? Let’s build awesome web-apps instead of re-inventing the authentication!

Dependencies

You’ll need:

For Ruby 1.9 support:

  • rails 2.3.5 which contains a required Ruby 1.9 patch that Facebooker relies on. See Important for more info.

Installation

Gem

  $ sudo gem install devise_facebook_connectable

…and in config/environment.rb:

  config.gem 'devise_facebook_connectable'

Dependencies

Note: Should be installed automatically with the gem.

  $ sudo gem install devise facebooker

…and in config/environment.rb:

  config.gem 'devise'
  config.gem 'facebooker'

Setup

Devise: Setup

See Devise documentation for instructions on how to setup Devise.

Facebook Connectable Facebooker: Setup

Installs the Facebooker stuff required Facebook Connect javascript helpers…

  $ ./script/generate devise_facebook_connectable --api API_KEY --secret SECRET_KEY
    dependency  xd_receiver
        create    public/xd_receiver.html
        create    public/xd_receiver_ssl.html
        create  config/facebooker.yml
        create  public/javascripts/devise.facebook_connectable.js

Note: The --api and --secret arguments only makes sense if you already got Facebook application details ready; can be configured later (see Configuration).

Facebook Connectable: Migration

  create_table :users do |t|

    t.facebook_connectable

    ...

  end

…and indexes (optional):

  add_index :users, :facebook_uid, :unique => true

…and then don’t forget: $ rake db:migrate.

Facebook Connectable: Model

  class User < ActiveRecord::Base

    devise :facebook_connectable, ...

  end

Note: All modules must be specified on the same line since Devise 1.0.0, otherwise Devise will only load the last call devise [MODULES] – which will cause FacebookConnectable to fail to initialize.

Configuration

Create a Facebook app

..if you haven’t already:

Create Facebook Application

…with settings:

Application settings > Connect > Facebook Connect Settings > Connect URL: http://localhost:3000 (for testing purposes)
Application settings > Advanced > Advanced Settings > Sandbox Mode: Enabled

…and for next step, copy-paste: API Key and Application Secret

config/facebooker.yml

…should look something like this:

  # ...
  defaults: &defaults
    api_key: YOUR_APP_API_KEY
    secret_key: YOUR_APP_SECRET_KEY
  # ...

Note: For Facebook Connect only api_key, secret_key, and Connect URL (configured in Facebook Application settings manager) are required.

I18n

Sign in/out link labels, and Flash error messages can be set using I18n:

  en:
    devise:
      sessions:
        facebook_invalid: "Could not sign in. Invalid account."
        facebook_timeout: "Facebook session expired., please sign in again to continue."
        facebook_authenticity_token: "Something went wrong. For security reasons, please sign in again."
        facebook_actions:
          sign_in: "Sign in"
          sign_out: "Sign out"

Note: The usage of :sign_in/:sign_out depends on configration, e.g. not used for the traditional and default Facebook Connect button.

Usage

In app/views/layouts/application.html.*, something like (showing only the relevant parts):

  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
           "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

  <!-- REQUIRED: Include Facebook XMLNS (XML namespace). -->
  <html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">
    <head>
      ...
      <!-- REQUIRED: Include Facebook Javascript (with current locale). See Facebooker documentation. -->
      <%= fb_connect_javascript_tag(:lang => ::I18n.locale) %>

      <!-- REQUIRED: Include devise_facebook_connectable Javascripts. -->
      <%= javascript_include_tag :defaults, 'devise.facebook_connectable' %>
      ...
    </head>
    <body>
      <!-- REQUIRED: Init Facebook Javascript (using jQuery). See Facebooker documentation. -->
      <%= init_fb_connect :XFBML, :js => :jquery %>
      ...
      <%= yield %>
      ...
    </body>
  </html>

View:

…add the sign in/out (connect) link somewhere – auto-detects scope:

  <%= facebook_link %>

…or with explicit scope:

  <%= facebook_link :customer %>

…or even more explicit, something like:

  <% unless signed_in?(:user) %>
    <%= facebook_sign_in_link :user %>
  <% else %>
    <%= facebook_sign_out_link :user %>
  <% end %>

etc.

Model:

…if you want to fetch and populate any data from Facebook before connect (account creation), and/or after connect (not saved automatically):

  class User < ActiveRecord::Base
    devise :facebook_connectable

    def before_facebook_connect(fb_session)

      fb_session.user.populate(:locale, :current_location, :username, :name, :first_name, :last_name,
                                :birthday_date, :sex, :city, :state, :country)

      self.locale             = my_fancy_locale_parser(fb_session.user.locale)
      self.time_zone          = fb_session.user.current_location.try(:city)
      self.country            = fb_session.user.current_location.try(:country)

      self.username           = fb_session.user.username

      self.profile.real_name  = fb_session.user.name
      self.profile.first_name = fb_session.user.first_name
      self.profile.last_name  = fb_session.user.last_name
      self.profile.birthdate  = fb_session.user.birthday_date.try(:to_date)
      self.profile.gender     = my_fancy_gender_parser(fb_session.user.sex)

      self.profile.city       = fb_session.user.hometown_location.try(:city)
      self.profile.zip_code   = fb_session.user.hometown_location.try(:state)
      self.profile.country    = fb_session.user.hometown_location.try(:country)

      ...
    end

    def after_facebook_connect(fb_session)
      ...
    end

  end

DONE!

  $ ./script/server

Note: If you experience any issues with connecting and/or signing in/out now, then I simply must have forgot something in these instructions. Please file a GitHub-issue in such case! =)

Example:

Checkout my little live example.

Advanced Configuration

In initializer config/initializers/devise.rb:

  Devise.setup do |config|
    # ...
    config.facebook_uid_field = :facebook_uid
    config.facebook_session_key_field = :facebook_session_key
    config.facebook_auto_create_account = false
    # ...
  end

Documentation (RDoc)

Rdoc.info/devise_facebook_connectable

Important

Stuff that you need to be aware of:

  • Just to be clear: You need to use the devise_facebook_connect-helpers to sign in/out, otherwise it won’t work.
  • If you sign in a resource using Facebook Connect (with this extension), you need to sign out using the the Facebook Connect sign out helpers available. If not you will end up being logged into Facebook but not to the account, and vice-versa. Messy. Would be cool with some kind of polling script that logs the user out of Facebook if the account is signed out – but not really needed now; just a bit of common sense and use the helpers and this will not be an issue.
  • For Ruby 1.9 support you should AVOID installing Rails 2.3.4. This version of rails contains a bug in a session helper that Facebooker relies on. More info about the bug/patch here. Rails 2.3.4.1 (patch release) should work.

References

Examples:

Documentation:

Repos:

Similar projects:

TODO / Known Issues

Priority:

  • Timeoutable module should timeout Facebook sessions as well – configurable.
  • Specs/Features would be great. Skipped tests so far as I built this using experimenting as I wasn’t sure how Devise and Warden was working – trial-n-error style. Would appreciate any help on this. Looking into using Cucumber watircuke / culerity.
  • Disconnect link helper would makes sense, i.e. disconnect a Facebook account from the app/site (a.k.a. delete the account Facebook Connect style).
  • Connect existing accounts using Connect.registerUsers.
  • Option: Facebooker vs. MiniFB – a more slimmed implementation if Facebooker feels to heavy – for those who don’t want to depend on Facebooker – want something more light-weight. Most probably implemented using MiniFB.
  • Review controller logic – some of my Facebooker hacks in controller might not be best practice. They seem to do the job though. =)

Maybe:

  • Expired sessions aka Facebooker::Session::SessionExpired appears less now after some controller filter hacks – but needs thorough testing.
  • HAML vs ERB clash – that I right now consider as a HAML bug – breaks the view helpers if both ERB and HAML is used in the same project. Can be avoided by either using HAML gem/plugin and use HAML views only, and vice-versa. Note: Something with the form_for-helper causing this issue.

License

Released under the MIT license.
Copyright © 2009-2010 Jonas Grimfelt

About

Devise << Facebook Connect

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Ruby 94.2%
  • JavaScript 5.8%