Search code examples
ruby-on-railsdeviseomniauth

overriding Devise update action inside omniauth_callbacks_controller


References:

Rails 5.0.1
omniauth 1.8.1
omniauth-facebook 5.0.0

I have users logged in with email + password and others with facebook account.

The main issue is to make possible to facebook's users edit their own account without needing to use the password, because they just don't have. The solution in this post so I can add this 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

So I have this:

routes.br

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

and inside the omniauth_callbacks_controller.rb I add this update function:

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

The problem is that the devise doesn't read this function. So if we add a byebug inside it will not stop there.

The only way to override this update function is to add this configurations below.

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

The only problem with this second approach is that if I try to login with facebook I receive this error message:

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

My devise configuration:

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

Solution

  • Just in case somebody else has a similar problem. The thing was that the routes.rb needed to specify both of the controllers being overwritten, like:

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