Devise << Facebook Connect
http://github.com/plataformatec/devise
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!
You’ll need:
- devise 1.0.6 for authentication – based on warden.
- facebooker for Facebook API integration.
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.
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'
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.
Create a Facebook app
..if you haven’t already:
…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.
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! =)
Checkout my little live example.
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
Rdoc.info/devise_facebook_connectable
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.
Examples:
- Facebooker Showcase – not using Devise, but same-same.
Documentation:
Repos:
Similar projects:
- authlogic_facebook_connect – Facebook Connect for Authlogic by Joakim Ekberg (using Facebooker)
- authlogic_facebook Facebook Connect for Authlogic by Rusty Burchfield (using MiniFB)
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.
Released under the MIT license.
Copyright © 2009-2010 Jonas Grimfelt