5
votes

In a Rails app that uses Devise, is there a way to tell if a specific user is signed in right now?

I know about the view helper, user_signed_in?. What I'm looking for is more like:

User.all.each do |user|
  puts user.signed_in?
end

I see that Devise has added a current_sign_in_at column to users. But that doesn't get set to NULL when the user signs out, so I can't test with that.

How can I check whether a user is currently signed in?

4
Possible duplicate of stackoverflow.com/questions/5504130/…, but the answers to both questions have good points.Mark Berry
Not sure what I was originally thinking. HTTP is stateless. Whether someone is signed in "now" is a meaningless question unless they're currently making a request. The closest we could get is "for what users do we have sessions in the db that are not older than X, such that if they made a request right now with the corresponding cookie, we would consider it valid?"Nathan Long
Right. Most of the answers have to do with whether a user has made a request in the last x minutes. I didn't know how to peek into sessions (which I store in cookies), so I went with a modified version of this, basically updating a User.last_request_at field on each request (except I throttle updates to once per minute).Mark Berry

4 Answers

6
votes

You can add a before_filter to your ApplicationController to set the current date/time in the last_request_at field in your users table, which you'd have to add first via a migration. In the method called from the before_filter, you wanna make sure that the user is authenticated first, of course. And you may want to except the filter for actions not requiring authentication.

Then, assuming you've included the Timeoutable module in your User model, you can see if the user's session is current or not by making a call like this:

user.timedout?(user.last_request_at)

The timedout?(last_access) method, defined in the Timeoutable module, will compare the Devise session timeout with the last request time/date.

As a side note, you might want to only update last_request_at if the last last_request_at value is greater than a minute ago (or any time interval you choose). Otherwise, if your page makes lots of controller calls, like mine does via AJAX, there are a lot of unnecessary db updates in one request.

4
votes

Are you trying to track if the user is signed in and has a session active, or if the user is on the site right now?

For tracking if the user is signed in, you would need to add a callback to Warden and update a timestamp every time a page loads and then if they haven't done anything for longer than X time period you assume they are logged out. You can do this with:

Warden::Manager.after_authentication do |user, auth, opts|
    if user.last_action <= 5.minutes.ago.utc
        user.last_action = Time.now
        user.save
    end
end

If you want to see if the have an active season and haven't explicitly logged out, it would be something such as:

Warden::Manager.before_logout do |user, auth, opts|
    user.current_sign_in_at = nil
    user.save
end
1
votes

Add an active flag to your user (devise) model. In the application controller under the after_sign_in_path_for(resource) hook set the active flag to true(happens when the user successfully creates a new session).

for loged out user, override the destroy method in the devise session_controller and set the active flag to false, then call super.

Now you can call user.active? on any user.

0
votes

Devise comes with a method current_user that will return the current user in controllers, models, and views.