DEV Community

Cover image for Manage active sessions in Rails 2024
Yaroslav Shmarov
Yaroslav Shmarov

Posted on • Originally published at blog.corsego.com on

Manage active sessions in Rails 2024

Ok, so sometimes to enchance security of your application you will want to allow users to see all the devices/browsers they are logged in with. You would also provided a button to sign out of a device/browser.

Here’s how you can do it:

1. Store current login (device/browser) info and ensure the current device/browser has not been logged out.

rails g resource Login user:references device_id ip_address user_agent


# app/models/user.rb
class User < ApplicationRecord
  has_many :logins, dependent: :destroy
end


# app/models/login.rb
class Login < ApplicationRecord
  belongs_to :user
end


# app/controllers/application_controller.rb
  include Users::LoginActivity

  # ensure this device has not been logged out
  before_action :validate_login, if: :current_user

  # this works with Devise
  # when logging a user in, register the current device
  def after_sign_in_path_for(resource_or_scope)
    set_login # this!
    root_path
  end


# app/controllers/concerns/users/login_activity.rb
module Users
  module LoginActivity
    extend ActiveSupport::Concern

    # included do
    # helper_method :set_login
    # end

    def set_login
      current_login = current_user.logins.where(device_id: device_id).first_or_create(ip_address: request.remote_ip, user_agent: request.user_agent, device_id: device_id)
      # current_login.touch
      # current_login.save
      session[:device_id] = device_id
      # cookies[:device_id] = device_id
    end

    def validate_login
      if Rails.env.test?
        # mock
        current_login = current_user.logins.create(device_id: "test_device_id")
      else
        current_login ||= current_user.logins.find_by(device_id: session[:device_id])
      end

      if current_login.nil?
        sign_out current_user

        flash[:notice] = 'Device not recognized'
        redirect_to new_user_session_path
      end
    end

    private

    def device_id
      Digest::SHA256.hexdigest("#{request.remote_ip}_#{request.user_agent}")
      # SecureRandom.hex
    end

  end
end

Enter fullscreen mode Exit fullscreen mode

2. Views to see all logins of a user, log out of a select device/browser

# config/routes.rb
  namespace :users do
    resources :logins, only: %i[index destroy]
  end


# app/controllers/users/logins_controller.rb
class Users::LoginsController < Accounts::BaseController
  def index
    @logins = current_user.logins
  end

  def destroy
    @login = current_user.logins.find(params[:id])
    @login.destroy

    flash[:notice] = "You have been logged out of this device."
    redirect_to edit_user_registration_path
  end
end


# app/views/logins/index.html.erb
<% @logins.order(updated_at: :desc).each do |login| %>
  <div>
    <i class="<%= device_type_icon(login.user_agent) %>"></i>

    <%= device_description(login.user_agent) %>

    Last login at:
    <%= login.updated_at %>

    IP address:
    <%= login.ip_address %>

    <% if login.device_id == session[:device_id] %>
      current session
    <% else %>
      <%= link_to 'Disconnect', users_login_path(login), method: :delete %>
    <% end %>

  </div>
<% end %>

Enter fullscreen mode Exit fullscreen mode

ℹ️ gem “device_detector” will help you decript user_agent info and make it user-friendly.

module LoginsHelper
  def device_description(user_agent)
    device = DeviceDetector.new(user_agent)
    "#{device.name} / #{device.os_name}"
  end

  def device_type_icon(user_agent)
    device = DeviceDetector.new(user_agent)

    case device.device_type
    when "tablet"
      "fa fa-tablet"
    when "smartphone"
      "fa fa-phone"
    when "desktop"
      "fa fa-desktop"
    end
  end
end

Enter fullscreen mode Exit fullscreen mode

That’s it! 🤗

Inspired by:

Top comments (0)