ruby-on-railsdeviseomniauth-facebook

sign_in_and_redirect after facebook auth is recalling itself


I have omniauth-facebook to sign up/sign in. After signin, I want to redirect the user to the page where they hit the auth block. I'm using sign_in_and_redirect but it seems to be calling the last url, which in this case is

http://localhost:3000/auth/facebook?callback_url=localhost%2Fauth%2Ffacebook%2Fcallback

So it keeps doing: Redirected to http://localhost:3000/auth/facebook?callback_url=localhost%2Fauth%2Ffacebook%2Fcallback

And it keeps calling this callback in a loop until it crashes. Here is the omniauth controller:

   def facebook
     generic_callback( 'facebook' )
   end

   def generic_callback( provider )
    @identity = Identity.find_for_oauth env["omniauth.auth"]
    if @identity.user != nil
      @user = @identity.user || current_user
    else
      @user = User.from_omniauth(request.env["omniauth.auth"])
    end

    if @user.nil?
      @user = User.create( email: @identity.email || "" )
      @identity.update_attribute( :user_id, @user.id )
    end

    if @user.email.blank? && @identity.email
      @user.update_attribute( :email, @identity.email)
    end

    if @user.persisted?
      @identity.update_attribute( :user_id, @user.id )
      @user = User.find(@user.id)
      sign_in_and_redirect @user, event: :authentication
      return
    else
      session["devise.#{provider}_data"] = env["omniauth.auth"]
      redirect_to new_user_registration_url
      return
    end
  end

The devise helper:

  def sign_in_and_redirect(resource_or_scope, *args)
    options  = args.extract_options!
    scope    = Devise::Mapping.find_scope!(resource_or_scope)
    resource = args.last || resource_or_scope
    sign_in(scope, resource, options)
    redirect_to after_sign_in_path_for(resource)
  end

Routes

  resources :users
  devise_for :users, controllers: { registrations: "users/registrations", sessions: "users/sessions",  :omniauth_callbacks => "users/omniauth_callbacks" }, path: '', path_names: { sign_in: 'sign_in', sign_out: 'logout', sign_up: 'sign_up'}
  match '/auth/:provider/callback', to: 'sessions#create', via: [:get, :post]

How can I make it sign in regularly?


Solution

  • Ok, here's the problem. This line creates the recursion:

    match '/auth/:provider/callback', to: 'sessions#create', via: [:get, :post]
    

    Change it to:

    get 'auth/facebook/callback', to: 'users/omniauth_callbacks#facebook'
    

    If you'll refactor #generic_callback method a bit to use params[:provider] instead of the provider argument, you'll then be able to get rid of hard-coding of the omniauth providers in your routes:

    get 'auth/:provider/callback', to: 'users/omniauth_callbacks#generic_callback'
    

    To clarify the problem:

    In the implementation described in the question users/omniauth_callbacks#generic_callback is not called at all. Instead you're trying to call sessions#create which is used to sign in users with the app credentials (usually, email and password), whereas in the omniauth case user should be created and authenticated in a specific way (via generic_callback and sign_in_and_redirect in your case).

    Simply put:

    You don't have to call sessions#create to create user session here, #generic_callback with #sign_in_and_redirect does just that.