Created
August 27, 2010 14:46
-
-
Save barinek/553493 to your computer and use it in GitHub Desktop.
Fast user switching with Devise
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: "[email protected]", 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 | |
Then we just wire in our new strategy. | |
Warden::Strategies.add(:sign_in_as, SignInAs::Devise::Strategies::FromAdmin) | |
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. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
May want to mention that the "sign in as" link sets the devise session variable in a SignInAsController.