How To Add Impersonation To Rails Authentication Generator

October 7, 2024

Track your progress

Sign in to track your progress and access subscription-only lessons.

Log In

Your Teacher

Hi, I'm Chris. I'm the creator of GoRails, Hatchbox.io and Jumpstart. I spend my time creating tutorials and tools to help Ruby on Rails developers build apps better and faster.

About This Episode

The Rails authentication generator is an awesome new addition with Rails 8 so let's explore how we can customize it to support user impersonation.

Notes

# app/controllers/concerns/authentication.rb
module Authentication
  extend ActiveSupport::Concern

  included do
    before_action :require_authentication
    helper_method :authenticated?
    helper_method :impersonating?
  end

  class_methods do
    def allow_unauthenticated_access(**options)
      skip_before_action :require_authentication, **options
    end
  end

  private
    def authenticated?
      Current.session.present?
    end

    def require_authentication
      resume_session || request_authentication
    end


    def resume_session
      Current.impersonated_user = find_impersonated_user
      Current.session = find_session_by_cookie
    end

    def find_session_by_cookie
      Session.find_by(id: cookies.signed[:session_id])
    end


    def request_authentication
      session[:return_to_after_authenticating] = request.url
      redirect_to new_session_url
    end

    def after_authentication_url
      session.delete(:return_to_after_authenticating) || root_url
    end


    def start_new_session_for(user)
      user.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session|
        Current.session = session
        cookies.signed.permanent[:session_id] = { value: session.id, httponly: true, same_site: :lax }
      end
    end

    def terminate_session
      Current.session.destroy
      cookies.delete(:session_id)
      stop_impersonating
    end


    def impersonating?
      Current.impersonated_user.present?
    end

    def impersonate(user)
      Current.impersonated_user = user
      session[:impersonated_user_id] = user.id
    end

    def find_impersonated_user
      if (id = session[:impersonated_user_id])
        User.find_by(id: id)
      end
    end

    def stop_impersonating
      Current.impersonated_user = nil
      session.delete(:impersonated_user_id)
    end
end
# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
  attribute :session, :impersonated_user

  def user
    impersonated_user || true_user
  end

  def true_user
    session&.user
  end
end

Want to stay up-to-date with Ruby on Rails?

Join 85,972+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.