覆盖 omniauth_callbacks_controller 中的 Devise 更新操作

overriding Devise update action inside omniauth_callbacks_controller

参考文献:

Rails 5.0.1
omniauth 1.8.1
omniauth-facebook 5.0.0

我有用户使用电子邮件+密码登录,其他用户使用 facebook 帐户登录。

主要问题是让 Facebook 的用户无需使用密码即可编辑自己的帐户,因为他们根本没有密码。 this post 中的解决方案,所以我可以添加这个 class:

  def update
    if current_user.provider == "facebook"
      params.delete("current_password")
      resource.update_without_password(user_params)
    else
      resource.update_with_password(user_params)
    end
    redirect_to root_path
  end

所以我有这个:

routes.br

devise_for :users, controllers: { omniauth_callbacks: "omniauth_callbacks" }

并在 omniauth_callbacks_controller.rb 中添加此 update 函数:

class OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def update
    if current_user.provider == "facebook"
      params.delete("current_password")
      resource.update_without_password(user_params)
    else
      resource.update_with_password(user_params)
    end
    redirect_to root_path
  end

  def facebook
    user = User.find_for_facebook_oauth(request.env['omniauth.auth'])
    if user.persisted?
      sign_in user
      redirect_to root_path
      set_flash_message(:notice, :success, kind: 'Facebook') if is_navigational_format?
    else
      session['devise.facebook_data'] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end

  def user_params
    params.require(:user).permit(:first_name, :last_name, :phone, :picture, :facebook_picture_url)
  end

end

问题是设备不读取这个函数。所以如果我们在里面添加一个byebug,它不会就此停止。

覆盖此更新功能的唯一方法是在下面添加此配置。

routes.br

devise_for :users, controllers: { registrations: 'registrations' }

registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController

  def update
    if current_user.provider == "facebook"
      params.delete("current_password")
      resource.update_without_password(user_params)
    else
      resource.update_with_password(user_params)
    end
    redirect_to root_path
  end

  def facebook
    user = User.find_for_facebook_oauth(request.env['omniauth.auth'])
    if user.persisted?
      sign_in user
      redirect_to root_path
      set_flash_message(:notice, :success, kind: 'Facebook') if is_navigational_format?
    else
      session['devise.facebook_data'] = request.env['omniauth.auth']
      redirect_to new_user_registration_url
    end
  end

  def user_params
    params.require(:user).permit(:first_name, :last_name, :phone, :picture, :facebook_picture_url)
  end

end

第二种方法的唯一问题是,如果我尝试使用 facebook 登录,我会收到此错误消息:

The action 'facebook' could not be found for Devise::OmniauthCallbacksController

我的设备配置:

devise.rb

Devise.setup do |config|

  config.omniauth :facebook, ENV["FB_ID"], ENV["FB_SECRET"],
    scope: 'email',
    info_fields: 'email, first_name, last_name',
    image_size: 'large',  # 50x50, guaranteed ratio
    secure_image_url: true

...

end

以防其他人遇到类似问题。问题是 routes.rb 需要指定 both 被覆盖的控制器,例如:

devise_for :users, controllers: { registrations: 'registrations' , omniauth_callbacks: 'omniauth_callbacks' }