Daniel Doubrovkine bio photo

Daniel Doubrovkine

aka dB., @awscloud, former CTO @artsy, +@vestris, NYC

Email Twitter LinkedIn Github Strava
Creative Commons License

Devise has a :trackable _strategy which updates the user’s last sign-in time, remote IP and increments a counter in the _User model upon successful logon. This is implemented in Devise::Models::Trackable.update_tracked_fields! and invoked as a Warden callback in devise/hooks/trackable.rb.

Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
  if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope]) && !warden.request.env['devise.skip_trackable']
    record.update_tracked_fields!(warden.request)
  end
end

Warden will invoke the callback every time warden.set_user is called, which is done once per logon with :event => :authenticate in the options. But when used with the :rememberable strategy, a returning user is not logging on – he continues a previous session. The callback is invoked with an :event => :fetch in the options, which is explicitly excluded in the code above.

Of course, we might not see things in the same eye. A user returning 24 hours later might as well be logging in again. Let’s add a callback that will update tracked fields in this case (to config/initializers/devise_trackable.rb).

Warden::Manager.after_set_user do |record, warden, options|
  if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope]) && ! warden.request.env['devise.skip_trackable'] &&
    (record.last_sign_in_at.nil? || (Date.today - record.last_sign_in_at.to_date).to_i >= 1)
    record.update_tracked_fields!(warden.request)
  end
end

Is there a better way to do this? Maybe something worth contributing in one way or another to Devise?