Ever wondered how to login as another user within Devise?
Recently we had a feature request that would provide Admins with the ability to sign is as another user. You could imagine live demonstrations or even production support calls where you would like to be signed in as another user, yet not have to ask for or decrypt their current password. After stewing for a bit, we found a pretty nice solution with Devise.
Here's the Cucumber feature...
Feature: Sign in as another user
As an admin
I want to sign in as another user
Scenario: Admin can sign in as another user
Given I am logged in as an admin user
And a user: "bob" exists with email: "bob@example.com", password: "password", ...
When I go to the admin users page
And I follow the "Sign in as" link for user: "bob"
Then I should see "Welcome Bob"
The trick was to store the admin info within the rack session.
request.env['rack.session']['devise.remember_admin_user_id']
We decided on using a Rails 3 concern to keep our actual strategy clean.
require 'devise/strategies/base'
module SignInAs
module Concerns
module RememberAdmin
extend ActiveSupport::Concern
private
def remember_admin_id
request.env['rack.session']['devise.remember_admin_user_id']
end
def remember_admin_id=(id)
request.env['rack.session']['devise.remember_admin_user_id'] = id
end
def remember_admin_id?
request.env['rack.session'] && request.env['rack.session']['devise.remember_admin_user_id'].present?
end
def clear_remembered_admin_id
request.env['rack.session']['devise.remember_admin_user_id'] = nil
end
end
end
end
The above really makes writing the Devise strategy fairly easy.
require 'devise/strategies/base'
module SignInAs
module Devise
module Strategies
class FromAdmin < ::Devise::Strategies::Base
include SignInAs::Concerns::RememberAdmin
def valid?
remember_admin_id?
end
def authenticate!
resource = User.find(remember_admin_id)
if resource
clear_remembered_admin_id
success!(resource)
else
pass
end
end
end
end
end
end
Warden::Strategies.add(:sign_in_as, SignInAs::Devise::Strategies::FromAdmin)
Then we just wire in our new strategy. Last line above.
Lastly, here's our sign-in-as controller which sets the Devise session variable.
class SignInAsController < ApplicationController
include SignInAs::Concerns::RememberAdmin
layout 'admin/application'
def create
if can?(:manage, :users)
self.remember_admin_id = current_user.id
sign_in :user, User.find(params[:user_id])
end
redirect_to '/admin'
end
end
That's it, pretty neat and might make for a nice Gem or Devise addition.

Please note that blog posts which are 100% gist show up as blank entries in the RSS feed.
Nice post Mike. Based on the title, I thought you might have stumbled upon a middleware I threw together recently: http://github.com/jackdempsey/spy
There's an example app up there at spy_example as well. It looks like your code is more structurally laid out--perhaps spy might spur some other creative thoughts along the lines of gemifying it up and including the middleware switcher.
Anyway, good stuff! Thx for the post.
very cool! also, I'm no longer using Gists and have updated the existing posts...