Knowledge
Here is the case
You are using devise as the authentication solution in a Rails application. Then you need to allow user to merge their Facebook account with their existing account in your application.Here is the workflow
A user arrive at your home page then she click "Sign in using Facebook", she allow Facebook to connected to your application then she redirected to the sign in page which display a message that she could continue the sign in process and merge her Facebook account with her application account.Here is how
You should start from here. There you will get some overview and understand the concept. Addomniauth into your Gemfile
gem 'omniauth'then declare the provider in your
config/initializers/devise.rb
config.omniauth :facebook, "<APP_ID>", "<APP_SECRET>"and make your model omniauthable
devise :omniauthable
Sign in using Facebook
First of all you want to addfacebook_uid column to table users so you can store user's Facebook uid there
In your config/routes.rb
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
create the controller to handle the callback app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
user = User.find_for_facebook_oauth(env["omniauth.auth"], current_user)
if user && user.persisted?
flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Facebook"
sign_in_and_redirect user, :event => :authentication
else
session["facebook_data"] = env["omniauth.auth"]
redirect_to new_user_session_url
end
end
end
add this method in your app/models/user.rb
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token['extra']['user_hash']
if user = User.find_by_facebook_uid(data["id"])
user
end
end
When user click the 'Sign in with Facebook' link on the Sign in or Sign up page, Facebook authentication and permission page will displayed and user will redirected back to the sign in page. You want to modify the sign in page devise/sessions/new.html.erb
<% if facebook? %>
<h2>Welcome <%= session["facebook_data"]["user_info"]["first_name"] %>, you almost done</h2>
<% else %>
<h2>Sign in</h2>
<% end %>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<p><%= f.label :email %><br />
<%= f.text_field :email %></p>
<p><%= f.label :password %><br />
<%= f.password_field :password %></p>
<% if devise_mapping.rememberable? && !facebook? -%>
<p><%= f.check_box :remember_me %> <%= f.label :remember_me %></p>
<% end -%>
<p><%= f.submit facebook? ? "Merge Account" : "Sign in" %></p>
<% end %>
Did you notice facebook? in the snippet above? it is a simple helper method to detect Facebook session
def facebook? session["facebook_data"] endNow add this method below (in your ApplicationController) will handle the merge process
def after_sign_in_path_for(resource)
# Merge data from Facebook with her current account
if session["facebook_data"] && current_user.facebook_uid.nil?
current_user.facebook_uid = session["facebook_data"]["uid"]
current_user.save(:validate => false)
end
# Countermeasure against session fixation
session.keys.grep(/^facebook\./).each { |k| session.delete(k) }
super
end
