user_signed_in? true after session timeout

1,193 views
Skip to first unread message

mcbsys

unread,
Mar 18, 2015, 2:18:20 PM3/18/15
to plataforma...@googlegroups.com
Hi,

I have a Rails 3.2.16 app with Devise 3.4.1.

I'm storing last_request_at in the Users table so processes outside the session can see if the user is currently active.

application_controller.rb

  before_filter :set_last_request_at

  protected
    def set_last_request_at
      if user_signed_in?
        # checked on every request but database updates limited to once per minute
        if current_user.last_request_at.nil? || current_user.last_request_at < 1.minute.ago
          current_user.update_attribute(:last_request_at, Time.now.utc)
        end
      end
    end

I have discovered that last_request_at is sometimes getting updated in the database after the session times out. I can duplicate this as follows:

1. In devise.rb, set config.timeout_in = 1.minutes.

2. Sign in.

3. Wait two minutes. Browser does not change.

4. In the open browser window, click on Sign out, which links to signout_path (devise/sessions#destroy). The following is logged:

Started GET "/signout" for 127.0.0.1 at 2015-03-18 10:52:58 -0700
Processing by Devise::SessionsController#destroy as HTML
  User Load (3.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 'a10b6752-cc32-11e4-9f2d-014c24e0ceb2' LIMIT 1
   (0.1ms)  BEGIN
   (1.0ms)  UPDATE "users" SET "last_request_at" = '2015-03-18 17:52:58.983498', "updated_at" = '2015-03-18 17:52:58.985108' WHERE "users"."id" = 'a10b6752-cc32-11e4-9f2d-014c24e0ceb2'
   (5.7ms)  COMMIT
Redirected to http://localhost:3000/
Completed 302 Found in 23.8ms (ActiveRecord: 9.9ms)

5. Check User.last_request_at in database. Time matches when user clicked Sign out.

So it appears that while processing Devise::SessionsController#destroy, my before_filter is hit but if user_signed_in? is still true even though the session has expired.

I tried to write a failing test for this but I can't figure out how to simulate session expiration. I see that Devise's tests use get expire_user_path(user) but that gives me an undefined method error.

Is user_signed_in? supposed to be true when the session has expired? Is there another test I should be using to confirm that the session is active?

Thanks,

Mark

mcbsys

unread,
Mar 20, 2015, 6:26:54 PM3/20/15
to plataforma...@googlegroups.com
I think I have a workaround for this issue. Even though user_signed_in? is true for hours or days after the session has expired, last_request_at in the warden session cookie still corresponds to the actual last request, so I can use that for updating the database. Example:  if a user works until 5:30pm and their session expires at 6pm, and the user clicks Sign out the next morning at 8am, the last_request_at cookie will still be 5:30pm from the previous day.

The code got quite a bit longer:

    def set_last_request_at
      if user_signed_in?
        if session["warden.user.user.session"]
          warden_last_request_at = session["warden.user.user.session"]["last_request_at"]
          if warden_last_request_at.is_a? Integer
            warden_last_request_at = Time.at(warden_last_request_at).utc
          elsif warden_last_request_at.is_a? String
            warden_last_request_at = Time.parse(warden_last_request_at).utc
          else
            warden_last_request_at = nil
          end
        else
          warden_last_request_at = nil
        end

        # checked on every request but database updates limited to once per minute
        if warden_last_request_at # only if filled in above
            logger.debug ">> last_request_at in database over a minute old compared to warden_last_request_at, so updating database"
            current_user.update_attribute(:last_request_at, warden_last_request_at)
          end
        end
      end
    end


Some of that was adapted from Devise's timeoutable.rb. BTW, I see there that in one case, time is converted to UTC and in another it is not:

https://github.com/plataformatec/devise/blob/master/lib/devise/hooks/timeoutable.rb#L16

Intentional or an oversight?

Any confirmations or corrections welcome!

Mark

mcbsys

unread,
Mar 20, 2015, 6:31:28 PM3/20/15
to plataforma...@googlegroups.com
P.S. Still would like to know how to test this. Is there a way to expose the "expire" function/URL?
Reply all
Reply to author
Forward
0 new messages