1

我正在使用omniauth-facebookgem 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'
4

3 回答 3

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

更新

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

在RailsCasts的后续剧集中描述的行为

于 2013-07-26T06:09:19.370 回答
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,如果没有,只需按照文档中的建议创建即可

于 2015-02-15T06:03:58.573 回答
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则将返回它,然后用户可以登录(不会更新)。

于 2014-07-07T10:49:03.123 回答