flyerhzm/switch_user

Switching to another user causes a duplicate request

shadydealer opened this issue · 1 comments

Switching to another account causes duplicate requests

I want to allow a caregiver to be able to switch to a child's account and then switch back. The problem is that two requests are sent before the second request gets sent the user has already been changed and so the original_user is now the user we've switched to, so there's no switching back to the caregiver's user.

Log

Started GET "/switch_user?scope_identifier=user_2" for 127.0.0.1 at 2021-08-11 12:48:58 +0300
....
Completed 302 Found in 9ms (ActiveRecord: 0.8ms | Allocations: 3011)


Started GET "/" for 127.0.0.1 at 2021-08-11 12:48:58 +0300
...
Completed 200 OK in 18ms (Views: 13.2ms | ActiveRecord: 1.0ms | Allocations: 9008)


Started GET "/switch_user?scope_identifier=user_2" for 127.0.0.1 at 2021-08-11 12:48:58 +0300
...
Redirected to http://localhost:3000/
Completed 302 Found in 9ms (ActiveRecord: 0.6ms | Allocations: 3453)


Started GET "/" for 127.0.0.1 at 2021-08-11 12:48:58 +0300
....
Completed 200 OK in 20ms (Views: 15.7ms | ActiveRecord: 0.9ms | Allocations: 8962)

What I've got is the following:

  • My erb view file:
<% current_user_children = current_user.children %>
<% unless current_user_children.empty? %>
  <% current_user_children.each do |child| %>
  <%= link_to "Switch to #{child.email}", "/switch_user?scope_identifier=user_#{child.id}" %>
  <% end %>
<% end %>
  • My config/initializers/switch_user.rb
# frozen_string_literal: true

SwitchUser.setup do |config|
  # provider may be :devise, :authlogic, :clearance, :restful_authentication, :sorcery, or :session
  config.provider = :devise

  # available_users is a hash,
  # key is the model name of user (:user, :admin, or any name you use),
  # value is a block that return the users that can be switched.
  config.available_users = { user: -> { User.all } }

  # available_users_identifiers is a hash,
  # keys in this hash should match a key in the available_users hash
  # value is the name of the identifying column to find by,
  # defaults to id
  # this hash is to allow you to specify a different column to
  # expose for instance a username on a User model instead of id
  config.available_users_identifiers = { user: :id }

  # available_users_names is a hash,
  # keys in this hash should match a key in the available_users hash
  # value is the column name which will be displayed in select box
  config.available_users_names = { user: :email }

  # controller_guard is a block,
  # if it returns true, the request will continue,
  # else the request will be refused and returns "Permission Denied"
  # if you switch from "admin" to user, the current_user param is "admin"
  config.controller_guard = ->(current_user, request, original_user) { current_user.caregivers.where(caregiver: original_user) }

  # view_guard is a block,
  # if it returns true, the switch user select box will be shown,
  # else the select box will not be shown
  # if you switch from admin to "user", the current_user param is "user"
  config.view_guard = ->(current_user, request, original_user) { Rails.env.development? }

  # redirect_path is a block, it returns which page will be redirected
  # after switching a user.
  config.redirect_path = ->(_request, _params) { '/' }

  # helper_with_guest is a boolean value, if it set to false
  # the guest item in the helper won't be shown
  config.helper_with_guest = false

  # false = login from one scope to another and you are logged in only in both scopes
  # true = you are logged only into one scope at a time
  config.login_exclusive = true

  # switch_back allows you to switch back to a previously selected user. See
  # README for more details.
  config.switch_back = true
end

Rails version 6.1.4
Ruby version 2.7.4

We managed to find out what the problem was. We're using turbolinks in our application and so wrapping the link in a data-turbolinks="false" div fixed the problem for us. Like so:

<% current_user_children = current_user.children %>
<% unless current_user_children.empty? %>
  <% current_user_children.each do |child| %>
  <div data-turbolinks="false">
    <%= link_to "Switch to #{child.email}'s Tracker", "/switch_user?scope_identifier=user_#{child.id}", class: "btn btn-primary" %>
  </div>
  <% end %>
<% end %>

So the return link should be:

<% if provider.original_user %>
  <div data-turbolinks="false">
    <%= link_to "Swtich back to #{provider.original_user.email}", "/switch_user?scope_identifier=user_#{provider.original_user.id}&remember=false", class: "btn btn-primary" %>
  </div>
<% end %>

EDIT:

The issue WAS NOT turbolinks in our case. The issue was that in one of our stimulus controllers we were calling:

s1.setAttribute('data-turbolinks-track', 'reload');

which causes a full page reload and all of the requests got processed twice.