redis-store / redis-actionpack

Redis stores for ActionPack
http://redis-store.org/redis-actionpack
MIT License
76 stars 44 forks source link

Ability to add the user_id to redis key when available #32

Closed DavidMealha closed 4 years ago

DavidMealha commented 4 years ago

Hey 👋

Recently I have migrated my rails sessions in one of my projects from MySQL to Redis, while using this gem.

However, I want to have the ability to delete all sessions of a specific user (e.g: when a user changes password, logging out from all devices, disabling a user).

After some investigation I haven't managed to find a way to do it.

I thought of managing a separate set in redis per user with all the session_private_ids (because the key is based on the private_id) in order to know each ones to remove. The keys would be populated on login, but this doesn't work because the Rack::Session::Id I have access to during the request is renewed after login.

The list management would be more or less like this (ignoring the invalidation of old keys for now):

# Class that manages list of sessions for each user
module Auth
  class SessionList
    LIST_NAMESPACE = "sessionlist"
    SESSION_NAMESPACE = "session"

    def initialize(user:)
      @user = user
    end

    def client
      @client ||= Redis.new(url: Rails.application.config.sessions[:redis_url])
    end

    # Append user session to list of active sessions
    def append_session_key(session_id)
      session_key = session_key(session_id)
      client.sadd(list_key, session_key)
    end

    private

    def list_key
      @list_key ||= "#{LIST_NAMESPACE}:#{@user.id}"
    end

    # Representation of each session key inside redis, which is composed by
    # the namespace defined in config/initializers/session_store.rb
    # and the session private id
    def session_key(session_id)
      "#{SESSION_NAMESPACE}:#{session_id}"
    end
  end
end
# Action that handles login
def login
  # delegate auth to warden
  if authenticate
    Auth::SessionList.new(user: current_user).append_session_key(session.id.private_id)
  end
end

So, if there was the ability to allow the session_key in redis to have a user identified, that would be fixed.

Do you have any idea on this?

Thanks, David

tubbo commented 4 years ago

It's somewhat buried in the documentation, but Rack offers support for dropping the session within rack.session.options. I think this might be what you want to do if you want to delete the session:

request.env['rack.session.options'][:drop] = true

This is supported in redis-rack, the library that this one depends on to handle rack-based session management.

DavidMealha commented 4 years ago

It's similar but I need to delete sessions across multiple devices.

In the meantime, I found out that I can access the correct session_id on Warden hook (Warden::Manager.after_set_user) this way I can manage the active sessions of each user, in order to delete them all at once when necessary.