Feature: Log in as Another User— Ruby on Rails and Auth0

Rodrigo Walter Ehresmann
7 min readAug 25, 2019

Introduction

On a daily basis of a web developer is not unusual to face the follow situation:

A costumer is complaining that something is not working how it should work. The next logical step is to check this particularly functionality, because it should be a general bug and is simple to reproduce, right? No. Everything works as intended for you, for everyone, but not for this customer that is starting to get annoyed and about to give up from the purchase at your eCommerce… I don’t need to go further, you probably got the idea.

We need to act fast, and there are some features that could help us to go faster sometimes, like logging in directly in the customer account to do a quick check of what is happening inside his account.

What we are going to build

This article will describe a simple approach to log in as another user inside your application, whereas only users with admin privilege will have the power to do so.

Although the approach is simple, I want to keep it more close as possible from a “real world” implementation, so we’ll use the administrate gem to create our admin dashboard and Auth0 as our login service.

Auth0 itself already had this feature available, but it was deprecated and further removed. I’m not sure how it worked and with what purpose it was used (I did not used before), but this article the uses purpose is to fulfill the brief case described before; this is: to see what the user is seeing at his screen.

What do we Need

In this example I will be using rails 5.2.3 and ruby 2.6.2, but it should work just fine in other versions too. Also, it’s necessary to create a free Auth0 account.

Let’s start!

Development

Configuring Auth0 application

Inside our Auth0 account we need to create a new application. I’ll handle a regular web application, so this is the application type I’ve chosen.

Auth0 application type

At the settings tab of your application, search for those url options and set them:

Of course, in a production environment the production urls need to be added too.

Creating the User model

As simple as possible, the User model will only have the email and admin fields. Create it with rails g model User and complement the migration file:

User migration file

Don’t forget to run rake db:migrate.

Configuring omniauth-auth0

Include on your gemgile the following gems:

gem 'omniauth-auth0', '~> 2.2'
gem 'administrate'
gem 'dotenv-rails', groups: [:development, :test]

administrate configurations are pretty straightforward, a quick check at the documentation and you’ll be ready to go. dotenv-rails is what I’ve chosen to handle environment variables, feel free to use whatever you want. omniauth-auth0 brings to us a strategy for authenticating with Auth0. Let’s create our initializer to make use of this strategy:

app/config/initializers/auth0.rb

Points of interest:

Now let’s add the necessary auth routes:

get 'auth/auth0', to: 'auth0#authentication', as: :authentication
get 'auth/auth0/callback', to: 'auth0#callback'
get 'auth/failure', to: 'auth0#failure'
post '/auth/logout', to: 'auth0#logout'

The GET for authentication path will trigger Auth0 Universal Login. Try to open http://localhost:3000/auth/auth0 in your browser: you should be redirect to login page where a form appears.

Example of Auth0 Universal Login form

The other actions will be further implemented in our Auth0Controller.

Logging In as Another User

Let’s go step-by-step introducing the desired feature for this example. The final routes should be the following:

app/config/routes.rb

I added the admin routes for the User model, and included the custom route to log in as the selected user. Also, the root route for the website and admin section were declared. Please note that the non-admin root points to home#index. The HomeController have only an empty index action, created just to show the desired feature of this example working. You can create it too, we’ll use it latter.

Moving to Auth0Controller, this is the authentication action where we redirect to Auth0 authorize endpoint:

app/controllers/auth0_controller.rb — authentication

This is the callback action for where Auth0 will redirect after a successful login:

app/controllers/auth0_controller.rb — callback

As simple as it looks like, find_or_create_user will retrieve the user email from the response, returning a User object from the method, either found or created; based on the returned User we redirect to the proper page. The key detail: we store the user id in the rails session with the purpose of always identify the current user (:user_id) and if he is the logged in admin (:admin_user_id).

The failure action will be called if the login wasn’t successful. This action isn’t relevant for the purposes of this article so I kept it empty, but is worth to know that in a real situation you would like to implement it. Moving on, our logout action:

app/controllers/auth0_controller.rb — logout

There are two logout situation:

  • If the user is logged as another user that means we are using the desired feature of this example. This block of code already delivers our approach to implement the “logging in as another user”: the accessed_user plays the current user logged in, and its id keep stored in session[:user_id]. Remember that only an admin user can make use of this feature, so we keep its id stored in session[:admin_user_id]. Now the logout logic is simple: session[:user_id] = session[:admin_user_id] just says that our admin user will be the current user again;
  • The usual case, where we’ll erase the user sessions id, what will say to our application that none user is logged in and also call Auth0’s logout endpoint to effectively logout from Auth0 too.

Now this is the login_as_user action, created in the Admin:UsersController:

app/controllers/admin/users_controller.rb

Complementing what was explained on the logout action, withsession[:user_id] = @user.id we’re just saying that the user selected by the admin will be interpreted as the current user logged in the application.

Visualizing the Result

Everything set up so let’s implement a view to see the result. First it’s necessary to implement the current user helper method:

app/controllers/application_controller.rb

The admin section uses its own ApplicationController, so we need to do the same there:

app/controllers/admin/application_controller.rb

The authenticate_admin method do the most basic and logical validation for an admin section: only allow the access for admin users. Ultimately, the session[:user_id] always will be present if a user is logged in. Once only admin users can access the admin section, the current user will be the one in session[:user_id].

Personal note: My viewpoint is that when we implement the “login as another user” feature, we should not create special rules that would change what we can see logged in as another user comparing with what the user itself can see when logged in. For instance: a non-admin user must not be able to access the admin section, so when we log in as this kind of user using this feature, that should not happen.

Now we add a button to call the feature, directly on the admin user page (you can check how to generate this view here):

app/views/admin/users/show.html.erb

Finally, a simple index view with links to all actions that we want to user:

app/views/home/index.html.erb

This view goes against my personal note above, but have in mind that this is just to provide a strong visual confirmation that the target feature is working.

For this test I’ve already created two users:

Admin users dashboard

Logged in with the admin user, I access the non-user register and click on “Log in as user”:

“Log in as user” button

The result:

Home index with an admin logged as another user

If I logout and go to the root path, the admin user is still logged in:

Home index after the admin user log out as another user

Conclusion

In this example we covered a simple approach using rails sessions to implement a feature that allows an admin user to login as another user of the application. The approach doesn’t involves any communication with Auth0, although it’s not a bad idea consider that: when calling the authorization endpoint we keep track of the action on Auth0 logs.

This feature was proposed with the intention of “see what the user see when he is logged in” (although you can find other uses too). So we can quickly check reports of strange occurrences: an layout bug that or a feature that isn’t working just for one particular user. Yes, there are plenty of situations that this feature doesn’t cover: layout bugs that end relying on the browser version used, for instance.

A lot screen-sharing and co-browsing tools are a more complete solution. However, this feature doesn’t involves much of complexity to be implemented and is a quick resource that doesn’t relies on the costumer collaboration.

The full application developed in this article is available here.

Make sure you give this post some claps and follow me if you enjoyed the content and want to see more!

--

--