Devise: rememberable means that last_sign_in_at is not updated by trackable

6.2k views Asked by At

I have being using Devise, and relying on last_sign_in_at of the user model to work out if my customers have not returned within X days. However, I recently discovered that last_sign_in_at is only updated when an actual form log in event occurs, as opposed to when a user is logged in automatically due to the inclusion of rememberable.

If want to ensure that last_sign_in_at is updated each time a user logs in (a new browser session), regardless of whether they used a form to log in or were automatically logged in by the rememberable cookie, how would I go about doing this in a Devise-compatible way?

5

There are 5 answers

4
RobH On BEST ANSWER

The trackable hook is from Warden's after_set_user hook -- what you could do to easily remedy this is set a before_filter to call sign_in.

This could be optimized, but test to see if using

before_filter proc{ sign_in(current_user, :force => true) }

updates the last_signed_in_at timestamp.

0
Oskar Holmkratz On

Devise: rememberable means that last_sign_in_at is not updated by trackable

Expanding on previous solutions, the problem with them would be that if the user signs in normally, they will "sign in twice". Which will set last_sign_in_at to the same (or almost the same) value as current_sign_in_at. On my site, I use last_sign_in_at to let the user know what has happened since last time they visited the site, and as such I need it to be somewhat accurate. Also, it logs +1 login count.

Also, there are people (like myself) who leave a browser window open for days without closing it (and hence never clearing the session flag). For metric purposes etc, it can be useful if such user behavior sometimes refresh the current_sign_in_at time.

The below variants will remedy these things.

class ApplicationController < ActionController::Base
  before_filter :update_sign_in_at_periodically
  UPDATE_LOGIN_PERIOD = 10.hours

  protected

  def update_sign_in_at_periodically
    if !session[:last_login_update_at] or session[:last_login_update_at] < UPDATE_LOGIN_PERIOD.ago
      session[:last_login_update_at] = Time.now
      sign_in(current_user, :force => true) if user_signed_in?
    end
  end
end

However, when I try the above, using Devise 3.2.4, I do get a new login when it auto-logins by cookie (login-count +1 and current_sign_in_at being set). So, I'm left with only the issue of wanting the tracking to periodically update even for users which keep the session open.

class ApplicationController < ActionController::Base
  before_filter :update_sign_in_at_periodically
  UPDATE_LOGIN_PERIOD = 10.hours 

  protected 

  def update_sign_in_at_periodically
    # use session cookie to avoid hammering the database
    if !session[:last_login_update_at] or session[:last_login_update_at] < UPDATE_LOGIN_PERIOD.ago
      session[:last_login_update_at] = Time.now
      if user_signed_in? and current_user.current_sign_in_at < 1.minute.ago # prevents double logins
        sign_in(current_user, :force => true)
      end
    end
  end
end
2
Jake On

On the application_controller you can set a before_action that checks if the current_sign_in_at of the current user is longer then X ago. If it is, then use sign_in(current_user, force: true) that updates the current_sign_in_at.

before_action :update_last_sign_in_at

def update_last_sign_in_at
   return unless user_signed_in? && current_user.current_sign_in_at < 12.hours.ago
   sign_in(current_user, force: true)
end

I use it so to detect inactive users (not signed in for 6 months) and delete them. #GDPR

2
Christoph Lupprich On

Taking Matthew's solution, I think the code should be the following (note the not-operator before the session[:logged_signin]):

before_filter :update_last_sign_in_at

protected

def update_last_sign_in_at
  if user_signed_in? && !session[:logged_signin]
    sign_in(current_user, :force => true)
    session[:logged_signin] = true
  end
end
1
Alexander Zinchuk On

AFAIK you can also use update_tracked_fields! on that current_user model.