I'm trying to implement authentication from Facebook in my app, for a Customer model. I had already done an authentication for Customers with Devise. I had followed this guide . In the initializer devise.rb I added this row:
config.omniauth :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], scope: "email"
This is my Customer model:
class Customer < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :omniauthable,
:recoverable, :rememberable, :trackable #, :validatable
validates_presence_of :name
validates_uniqueness_of :nickname
before_save :complete_nickname
def complete_nickname
if !self.nickname?
self.nickname = self.email
end
end
def facebook
identities.where( :provider => "facebook" ).first
end
def facebook_client
@facebook_client ||= Facebook.client( access_token: facebook.accesstoken )
end
end
This is my Identity model, written as specified in the guide:
class Identity < ActiveRecord::Base
belongs_to :customer
validates_presence_of :uid, :provider
validates_uniqueness_of :uid, :scope => :provider
def self.find_for_oauth(auth)
identity = find_by(provider: auth.provider, uid: auth.uid)
identity = create(uid: auth.uid, provider: auth.provider) if identity.nil?
identity.accesstoken = auth.credentials.token
identity.refreshtoken = auth.credentials.refresh_token
identity.name = auth.info.name
identity.email = auth.info.email
identity.nickname = auth.info.name.gsub(/\s+/, "")
identity.image = auth.info.image
identity.phone = auth.info.phone
identity.urls = (auth.info.urls || "").to_json
identity.save
identity
end
end
This is my OmniauthCallbackController
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
generic_callback( 'facebook' )
end
def generic_callback( provider )
@identity = Identity.find_for_oauth env["omniauth.auth"]
@customer = @identity.customer || current_customer
if @customer.nil?
@customer = Customer.create( email: @identity.email || "", nickname: @identity.email || "" )
@identity.update_attribute( :customer_id, @customer.id )
end
if @customer.email.blank? && @identity.email
@customer.update_attribute( :email, @identity.email)
end
if @customer.persisted?
@identity.update_attribute( :customer_id, @customer.id )
# This is because we've created the user manually, and Device expects a
# FormUser class (with the validations)
@customer = FormUser.find @customer.id
sign_in_and_redirect @customer, event: :authentication
set_flash_message(:notice, :success, kind: provider.capitalize) if is_navigational_format?
else
session["devise.#{provider}_data"] = env["omniauth.auth"]
redirect_to new_customer_registration_url
end
end
end
When I click on Login in with Facebook I am not authenticated, and the app redirects me on the login form. On the server outuput I see that it tried to create a Customer, but then it fails and rollbacks:
SQL (0.3ms) UPDATE "identities" SET "accesstoken" = $1, "updated_at" = $2 WHERE "identities"."id" = $3 [["accesstoken", "CAABtFtDqAlwBAEFzpAtw2W2gUTLkpKbtjI4lqKibkIO5kyJSwNYK9TDzDG4NfEoq40oQdUzXxZADRZBwYy319KPobEU6378ULwQ3PtKT46EbEugs4eIdsQyfZC3S8yLIJoPW6uq7ZAF0AnEeepDvl97ajbazsmGsuP22rnK0G1zSTHzoBfPpGZAD4NkgW7Kp5r5TiLhxxEgZDZD"], ["updated_at", "2015-11-24 11:18:28.010402"], ["id", 2]]
(65.7ms) COMMIT
(0.2ms) BEGIN
Customer Exists (4.0ms) SELECT 1 AS one FROM "customers" WHERE "customers"."nickname" = '' LIMIT 1
(0.2ms) ROLLBACK
Maybe it's a validation control that I already had on my Customer object, but I can't figure out what it can be. This is the schema of my Customer:
create_table "customers", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "name", default: "", null: false
t.string "surname", default: "", null: false
t.string "nickname", default: "", null: false
...
end
Someone can help me? Thanks in advance
Try to this configuration
devise.rb
config.omniauth :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], scope: 'email', info_fields: 'email, name'
I solved, it seems to work with my Facebook account. I update the omniauth-facebook gem, that maybe was part of the issue, with bundle update omniauth-facebook
. I modified the initializer devise.rb in this way (no spaces in scope and info_fields):
`config.omniauth :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET'], scope: "email,public_profile", info_fields: 'email,name,first_name,last_name'`
And modified the OmniauthCallbackController:
@customer = Customer.create( email: @identity.email || "", nickname: @identity.nickname || "",
name: env["omniauth.auth"]["info"]["first_name"] || "",
surname: env["omniauth.auth"]["info"]["last_name"] || "",
password: "********", password_confirmation: "********" )
@identity.update_attribute( :customer_id, @customer.id )
Because in my Customer model name and surname are mandatory fields. I still have a problem. I pushed the app on Heroku and I can sign in with my Facebook account and with other users accounts, but for some other users Facebook doesn't return the email.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.