5

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?

Nathan Long
  • 122,748
  • 97
  • 336
  • 451
  • Possible duplicate of http://stackoverflow.com/questions/5504130/whos-online-using-devise-in-rails, but the answers to both questions have good points. – Mark Berry Apr 15 '14 at 19:47
  • 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 Apr 16 '14 at 13:51
  • 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](https://groups.google.com/forum/#!starred/plataformatec-devise/R2dou7yp27s), basically updating a User.last_request_at field on each request (except I throttle updates to once per minute). – Mark Berry Apr 16 '14 at 21:11

4 Answers4

6

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.

yuяi
  • 2,617
  • 1
  • 23
  • 46
4

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
Zachary Anker
  • 4,500
  • 21
  • 19
  • Do you know whether the `before_logout` block will run if the user is automatically logged out, like with Devise's `timeoutable`? – Nathan Long Jan 12 '12 at 17:18
  • It's run whenever the Warden `logout` method is used which handles cleaning up the session. Specifically for `timedoutable` it'll run for sure. – Zachary Anker Jan 12 '12 at 17:20
1

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.

Ram kiran Pachigolla
  • 20,897
  • 15
  • 57
  • 78
0

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

Steve Y
  • 343
  • 3
  • 11
  • 2
    I think you misunderstood my question. `current_user` is scoped to a session, so to speak; if 5 people are logged into my site right now, all looking at the same page which displays the value of `current_user`, they will each see themselves. What I want is a way, as a site admin, to list all 5 of those logged-in users, or to ask "is someuser@example.com signed in right now or not?" – Nathan Long Jan 17 '12 at 12:47