为现有用户连接Facebook

时间:2013-07-26 05:28:19

标签: ruby-on-rails facebook devise omniauth

我正在使用omniauth-facebook gem with devise在我的用户模型中的rails应用程序中使用Facebook进行身份验证

def self.from_omniauth(auth)
  # immediately get 60 day auth token
  oauth = Koala::Facebook::OAuth.new("App Key",  "App secrets" )
  new_access_info = oauth.exchange_access_token_info auth.credentials.token
  new_access_token = new_access_info["access_token"]
  new_access_expires_at = DateTime.now + new_access_info["expires"].to_i.seconds
  begin
    where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.username = auth.info.first_name
      user.lastname =auth.info.last_name
      user.email =auth.info.email
      user.authentication_token = new_access_token 
      user.oauth_expires_at = new_access_expires_at 
      user.save!
    end
  rescue  ActiveRecord::RecordInvalid
  end
end

#interact with facebook
def facebook
  @facebook ||= Koala::Facebook::API.new(authentication_token)
  block_given? ? yield(@facebook) : @facebook
rescue Koala::Facebook::APIError => e
  logger.info e.to_s
  nil # or consider a custom null object
end


def self.new_with_session(params, session)
  if session["devise.user_attributes"]
    new(session["devise.user_attributes"], :without_protection=> true) do |user|
      user.attributes = params
      user.valid?
    end
  else
    super
  end
end

在我的omniauth_callbacks控制器上我有这个方法:

def all
  user = User.from_omniauth(request.env["omniauth.auth"])
  if user.persisted?
    flash.notice = "Signed in!"
    sign_in_and_redirect user
  else
    session["devise.user_attributes"] = user.attributes
    redirect_to new_user_registration_url
  end
end
alias_method :facebook, :all

这些方法用于通过Facebook从头开始验证用户。如果他们通过正常的设计注册方法注册,我需要一种方法将现有用户与他们的Facebook帐户连接而不是新帐户

当现有用户尝试通过Facebook登录时,会发生以下错误:

A `NoMethodError` occurred in `omniauth_callbacks#facebook`:

undefined method `persisted?' for nil:NilClass
 app/controllers/omniauth_callbacks_controller.rb:4:in `all'

3 个答案:

答案 0 :(得分:4)

您可以先email找到用户,只需更新provideruid字段,以防他已存在。

所以你的User.from_omniauth看起来可能是这样的:

def self.from_omniauth(auth)
  # immediately get 60 day auth token
  oauth = Koala::Facebook::OAuth.new("",  "" )
  new_access_info = oauth.exchange_access_token_info auth.credentials.token
  new_access_token = new_access_info["access_token"]
  new_access_expires_at = DateTime.now + new_access_info["expires"].to_i.seconds

  user = where(provider: auth.provider, uid: auth.uid).first
  unless user
    # in that case you will find existing user doesn't connected to facebook
    # or create new one by email
    user = where(email: auth.info.email).first_or_initialize
    user.provider = auth.provider # and connect him to facebook here
    user.uid = auth.uid           # and here
    user.username = auth.info.first_name
    user.lastname = auth.info.last_name
    user.authentication_token = new_access_token
    user.oauth_expires_at = new_access_expires_at
    # other user's data you want to update
    user.save!
  end

  user
end

<强> UPD

如果您遇到密码验证错误,您可以覆盖User#password_required?方法以跳过用户通过Facebook登录的验证。

以下episode RailsCasts

中描述的行为

答案 1 :(得分:1)

仅为rails 4添加更新版本并设计3.4.x

这就像更新的omniauth看起来像

  def self.from_omniauth(auth)
    if !where(email: auth.info.email).empty?
      user = where(email: auth.info.email).first
      user.provider = auth.provider # and connect him to facebook here
      user.uid = auth.uid           # and here
      user.save!
      user
    else
      where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
        user.email = auth.info.email
        user.password = Devise.friendly_token[0,20]
        user.first_name = auth.info.name   # assuming the user model has a name
        user.avatar = process_uri(auth.info.image) # assuming the user model has an image
      end
    end
  end

只需通过电子邮件查找用户,如果有,只需添加提供商和uid,如果没有,只需按照documentation

中的建议创建

答案 2 :(得分:0)

您的from_omniauth可以是这样的:

def self.from_omniauth(auth)
    where(auth.info.slice(:email)).first_or_create do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0,20]
      user.username = auth.info.name
      user.description = auth.extra.raw_info.bio
    end
end

通过这种方式,如果现有用户的电子邮件与Facebook帐户相同,那么first_or_create将返回该帐户,然后用户就可以登录(它不会更新)。