使用 Devise / Omniauth 成功验证后 CallbacksController#facebook 中的 RuntimeError Rails

RuntimeError in CallbacksController#facebook after successful authentication with Devise / Omniauth Rails

我正在使用 omniauth facebook gem 进行设计以构建注册系统。用户可以使用电子邮件注册或使用他们的 Facebook 帐户连接。 如果一个邮箱注册的用户使用facebook账号登录,我会检查邮箱地址是否已经注册并连接这两个账号,然后让用户登录。

整个场景已经可以运行了。使用 facebook 的新 omniauth 数据更新用户条目。我得到这个错误,当 facebooks 发送回调时(来自 facebook 的数据已成功保存到 db):

RuntimeError in CallbacksController#facebook

找不到 true 的有效映射

提取的源代码(第 5 行附近):

class CallbacksController < Devise::OmniauthCallbacksController
  # Define each provider for omniauth here (def twitter...)
  def facebook
    @user = User.from_omniauth(request.env["omniauth.auth"])
    sign_in_and_redirect @user
  end
end

user.rb 模型看起来像这样:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :omniauthable, :omniauth_providers => [:facebook]

  def self.from_omniauth(auth)
    user = where(provider: auth.provider, uid: auth.uid).first
    unless user
      user = where(email: auth.info.email).first_or_initialize
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.info.email
      user.name = auth.info.name
      user.nickname = auth.info.nickname
      user.first_name = auth.info.first_name
      user.last_name = auth.info.last_name
      user.location = auth.info.location
      user.description = auth.info.description
      user.image = auth.info.image
      user.phone = auth.info.phone
      user.urls = auth.info.urls
      user.password = Devise.friendly_token[0,20]
      user.save!
    end
  end
end

routes.rb

Rails.application.routes.draw do
  devise_for :users, :controllers => { :omniauth_callbacks => "callbacks" }
  resources :auctions do
    resources :comments
  end

  root 'welcome#index'
  get '/', :to => 'welcome#index'
end

您的 User class 中的 self.from_omniauth 方法是 returning true,而不是 User 模型。这是因为return Ruby is the result of the last line evaluated, and in this case user.save! is the last line that runs. The "Could not find a valid mapping for true" error is a result of passing true into sign_in_and_redirect; you can see in the Devise source that the first argument is passed into Devise::Mapping.find_scope!中方法的值。

解决方案很简单 - 确保您 return 在 from_omniauth:

中构建您的 user 模型
def self.from_omniauth(auth)
    user = where(provider: auth.provider, uid: auth.uid).first
    unless user
      user = where(email: auth.info.email).first_or_initialize
      user.provider = auth.provider
      user.uid = auth.uid
      user.email = auth.info.email
      user.name = auth.info.name
      user.nickname = auth.info.nickname
      user.first_name = auth.info.first_name
      user.last_name = auth.info.last_name
      user.location = auth.info.location
      user.description = auth.info.description
      user.image = auth.info.image
      user.phone = auth.info.phone
      user.urls = auth.info.urls
      user.password = Devise.friendly_token[0,20]
      user.save!
    end
    user # Make sure we return the user we constructed.
  end