I have Rails 4.2, Devise 4.2
I mostly follow the this Devise how-to for setting up OmniAuth:
https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
Exceptions to above how-to:
in devise.rb initializer I added# added this for facebook authentication
if Rails.env == "production"
fb_callback = "https://www.mylivesite.com/users/auth/facebook/callback"
else
fb_callback = "http://localhost:3000/users/auth/facebook/callback"
end
config.omniauth :facebook, ENV['AD_FB_APP_ID'], ENV['AD_FB_APP_SECRET'],
callback_url: fb_callback ,
info_fields: 'name,email'
The environment variables are
AD_FB_APP_ID and AD_FB_APP_SECRET I set these in my local machine's bashrc file (ubuntu linux installed) to match the values in my newly created facebook app.
my projects are located under /home/sean/projects, so when I am in a specific project directory I edit bashrc like:
nano ../../.bashrc
re-read bashrc after saving: source ../../.bashrc
Facebook App Dashboard
Facebook settings are tricky. Within https://developers.facebook.com/apps/xxxxxxxxxxxxxxx/dashboard/
Add Facebook Login product
Then Set 'Valid OAuth redirect URIs' to:
http://localhost:3000 and https://www.mylivesite.com
Under Settings: Add 'website' platform, then set app domains to www.mylivesite.com
Under App Review: make live
User Methods
After following along with the rest of the devise tutorial above, I came up with the following two methods for my user model:
def self.from_omniauth(auth)
user = User.find_by(email: auth.info.email)
# https://github.com/plataformatec/devise/wiki/How-To:-Add-:confirmable-to-Users
if user and user.confirmed?
user.provider = auth.provider
user.uid = auth.uid
return user
end
#where(auth.slice(:provider, :uid)).first_or_create do |user|
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
#user.skip_confirmation!
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.password = Devise.friendly_token[0,20]
user.first_name = auth.info.name.split(" ")[0..-2].join(" ")
user.last_name = auth.info.name.split(" ").last
#user.first_name = first_name
#user.last_name = last_name
end
end
def self.new_with_session(params, session)
super.tap do |user|
if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
user.email = data["email"] if user.email.blank?
user.first_name = data["name"].split(" ")[0..-2].join(" ") if user.first_name.blank?
user.last_name = data["name"].split(" ").last if user.last_name.blank?
end
end
end
I am using Heroku so I need to remember to set the Facebook app key and secret variables in my Heroku settings as well
I realized I need users to finish filling out their registration form to get info that facebook does not provide such as phone. However, since the password was auto generated for them on registration with facebook, I need a way to get the additional info and not bother them for a password in the process (which they do not know).
First I redirect the new signup to the registration page:
I edit my omniauths_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in @user
# there is probably a better way to do this but I need to get new accounts to finish registration
if @user.created_at > Time.now - 1.minute
redirect_to edit_user_registration_url
flash[:alert] = "Please set phone. Choose texting preference if desired."
else
redirect_to root_url
end
#sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
flash[:alert] = "Please set phone. Choose texting preference if desired."
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
Then, I add a new controller under controllers/users/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController
# allow facebook registrations to edit account without password
# http://stackoverflow.com/questions/13436232/editing-users-with-devise-and-omniauth
def update_resource(resource, params)
resource.update_with_password(params)
end
# Overwrite update_resource to let users to update their user without giving their password
def update_resource(resource, params)
if resource.provider == "facebook"
params.delete("current_password")
resource.update_without_password(params)
else
resource.update_with_password(params)
end
end
end
I add a route for it in routes.rb like so:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks", :registrations => 'users/registrations' }
Lastly, I hide the password fields on my views/devise/registrations/edit.html.rb form by wrapping all three of the password fields with
<% if (!current_user.provider=='facebook') %>
<% end %>
Update
I realized I need users to finish filling out their registration form to get info that facebook does not provide such as phone. However, since the password was auto generated for them on registration with facebook, I need a way to get the additional info and not bother them for a password in the process (which they do not know).
First I redirect the new signup to the registration page:
I edit my omniauths_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# You need to implement the method below in your model (e.g. app/models/user.rb)
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in @user
# there is probably a better way to do this but I need to get new accounts to finish registration
if @user.created_at > Time.now - 1.minute
redirect_to edit_user_registration_url
flash[:alert] = "Please set phone. Choose texting preference if desired."
else
redirect_to root_url
end
#sign_in_and_redirect @user, :event => :authentication #this will throw if @user is not activated
set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
flash[:alert] = "Please set phone. Choose texting preference if desired."
else
session["devise.facebook_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
def failure
redirect_to root_path
end
end
Then, I add a new controller under controllers/users/registrations_controller.rb:
class Users::RegistrationsController < Devise::RegistrationsController
# allow facebook registrations to edit account without password
# http://stackoverflow.com/questions/13436232/editing-users-with-devise-and-omniauth
def update_resource(resource, params)
resource.update_with_password(params)
end
# Overwrite update_resource to let users to update their user without giving their password
def update_resource(resource, params)
if resource.provider == "facebook"
params.delete("current_password")
resource.update_without_password(params)
else
resource.update_with_password(params)
end
end
end
I add a route for it in routes.rb like so:
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks", :registrations => 'users/registrations' }
Lastly, I hide the password fields on my views/devise/registrations/edit.html.rb form by wrapping all three of the password fields with
<% if (!current_user.provider=='facebook') %>
<% end %>