Your Teacher
Chris Oliver
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