Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4 - Devise, guest users causes filter chain halted

I have just started working on a Rails 4 (4.2.3) app where I use Devise for user authentication. I want users to be able to play around with the app before signing upp by creating a test project and be signed in as a guest user. When the user signs up (or in) I want to assign the test project to the new current user.

I have been following this guide from Platformatec: https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user

Creating the guest user works, but when signing up or in with an active guest user session I get the following error:

Filter chain halted as :require_no_authentication rendered or redirected

If I clear my session it works. The method that manages my guest user looks like this:

def current_or_guest_user
  if current_user
    if session[:guest_user_id] && session[:guest_user_id] != current_user.id
      logging_in
      guest_user(with_retry = false).try(:destroy)
      session[:guest_user_id] = nil
    end
    current_user
  else
    guest_user
  end
end

As mentioned, creating guest users seems to work just fine. But this logic never happens:

# should delete the guest user and clear the session. 
if current_user
  if session[:guest_user_id] && session[:guest_user_id] != current_user.id
    logging_in
    guest_user(with_retry = false).try(:destroy)
    session[:guest_user_id] = nil
  end
  current_user

I'm pretty sure that my guest user session is conflicting with my new user and causes this Devise error (since the guest user never gets deleted on sign up):

Filter chain halted as :require_no_authentication rendered or redirected

The rest of my guest user logic looks more or less exactly like this linked guide: https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user. I also added the code from the Authentication paragraph / example: https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user#authentication-this-may-interfere-with-the-current_user-helper.

Any ideas on what I'm missing, and how I can get my current_or_guest_user to delete the guest user on signup and signing when using Devise?

Update

This is how my routes look currently:

devise_for :users, controllers: { sessions: "users/sessions", registrations:  "users/registrations" }

root :to => 'public#index'

resources :apps

get 'users/show', to: "users#show"
get 'users', to: "users#index"

post 'guests/receive_guest', to: "guests#receive_guest"

Update 2

The guide have the following statement:

When (and if) the user registers or logs in, we delete the guest user and clear the session variable.

It doesn't explain much how and where to do it. I'm guessing I have to call current_or_guest_user somewhere again. But I'm not sure where since I'm not that familiar with Devise.

Update 3

To make it a bit more clear. This is the steps I want to achieve.

  1. User creates a test project.
  2. Upon creation of the test project a guest user and session gets created. The guest user should get deleted once the session ends or #3.
  3. If the guest user signs up, he /she should get logged in and the test project should get assigned to the new real user.
  4. The guest user should get deleted.
like image 686
Anders Avatar asked Nov 16 '15 21:11

Anders


3 Answers

The above answer lacks of explanation, the loggin_in method is not being called because you have not defined your callback

In your ApplicationController you need to define and then set the callback like so:

define_callbacks :logging_in_user
set_callback :logging_in_user, :before, :transfer_guest_user_actions_to_current_user

def transfer_guest_user_actions_to_current_user
  # todo
end

Have you tried to use devise-guests gem? it implements out of the box this functionality, instead of do it from scratch...

Check this example controller that is implementing guest login. Hope it helps.

like image 161
svelandiag Avatar answered Sep 22 '22 03:09

svelandiag


I think the issue is caused by the guest_user warden strategy referenced in the guide. Since Devise checks for a valid warden session before logging in, it causes a redirect and a flash message to appear:

if authenticated && resource = warden.user(resource_name)
  flash[:alert] = I18n.t("devise.failure.already_authenticated")
  redirect_to after_sign_in_path_for(resource)
end

So if you do not strictly need that authenticate! method to work on the guest user, you can simply deactivate that strategy and it should work.

like image 28
skahlert Avatar answered Sep 26 '22 03:09

skahlert


Thanks for your answers skahlert and SsouLlesS. Skipping the warden strategy did help and defining callbacks seems like a clean approach. Beside that I had some other issues.

One was that my custom Devise controllers never got called. Created another question for that: Rails 4 - devise_for not using custom controllers. The solution was that I used the wrong url in my modal signup form. My form now looks like this:

= form_for(resource, as: resource_name, url: user_registration_path do |f|

Then I did as skahlert suggested and removed the warden strategy from my app.

Once my custom devise Users::RegistrationsController < Devise::RegistrationsController controller got called I decided to override the create action. It's not completely tested, but it looks like this:

# POST /resource
def create
  # Delete guest user and reset session. 
  new_user_from_guest = guest_user
  guest_user(with_retry = false).try(:destroy)
  session[:guest_user_id] = nil

  build_resource(sign_up_params)

  resource.save
  yield resource if block_given?
  if resource.persisted?
    if resource.active_for_authentication?
      set_flash_message :notice, :signed_up if is_flashing_format?
      sign_up(resource_name, resource)

      # Assign the guest users app to the new current user.
      new_user_from_guest.apps.each do |app|
        app.user_id = current_user.id
        app.save!
      end
      respond_with resource, location: after_sign_up_path_for(resource)
    else
      set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
      expire_data_after_sign_in!
      respond_with resource, location:  after_inactive_sign_up_path_for(resource)
    end
  else
    # Reset test user if signup fails, I have not tested this part yet. 
    guest_user
    guest_user.apps << new_user_from_guest.apps
    guest_user.save!

    clean_up_passwords resource
    set_minimum_password_length
    respond_with resource
  end
end

Everything seems to work now. Could do some refactoring and possibly try the callback approach that SsouLlesS suggested in his answer.

like image 27
Anders Avatar answered Sep 26 '22 03:09

Anders