SessionExpirable

All good things come to an end: At least that’s how it’s supposed to work.

SessionExpirable is an enhanced version of Devise[https://github.com/plataformatec/devise]‘s timeoutable module that ensures that no session is allowed to last forever.

Like Devise timeoutable, SessionExpirable adds a timestamp to sessions indicating the last time that the session was used, and checks these timestamps when a session is used to sign a user in. A configurable timeout_in option controls how long inactive sessions are considered valid.

Differences with Devise Timeoutable

When timeoutable reads a session that does not contain a timestamp, timestamp validation is bypassed: A session without a timestamp will never be considered as having timed out.

In order to prevent the abuse of non-expiring sessions, SessionExpirable treats sessions without timestamps as having already expired.

Another difference is that in the case of a timeout, timeoutable prevents other authentication strategies from being tried, although it has custom logic to allow Devise rememberable to work in spite of a timed-out session.

SessionExpirable does not allow a timed-out session to interfere with any authentication strategies that are configured. One consequence is that SessionExpirable does not support invalidation of authentication tokens from the Devise token_authenticatable module when a request with a valid authentication token is accompanied by an expired session.

One final difference is that SessionExpirable does not invoke the Devise FailureApp for requests that do not require authentication.

Configuration

Add devise_session_expirable to your Gemfile:

gem 'devise_session_expirable'

Include :session_expirable in your devise user model declaration:

class User < ActiveRecord::Base
  devise :database_authenticatable, :session_expirable # ...
end

Then update the Devise initializer:

Devise.setup do |config|
  # ...
  config.timeout_in = 15.minutes
  config.default_last_request_at = Time.parse('2013-02-16T00:00:00Z')
  # ...
end

Migrating from non-expiring sessions

The default_last_request_at option is intended to enable a less disruptive migration if sessions without timestamps have already been issued. The configured value will be used in place of the timestamp for sessions which don’t have one.

If default_last_request_at is configured, it should be set to a fixed date/time, ideally matching the time of deployment. If set to a dynamic time (e.g. Time.now), the lifetime of sessions without timestamps will be extended every time Rails is initialized.

After the timeout_in interval passes, any legacy sessions will have expired and default_last_request_at can be unset.

Alternatives

As long as you reset your secret_token when configuring Devise timeoutable to your rails application and avoid authenticating for actions which set devise.skip_trackable in the rack environment, you will have eliminated at least two possible sources of non-expiring sessions.

Acknowledgements

This module was adapted from Devise’s timeoutable module.

Thanks to the creators and maintainers of Devise and Warden for a truly extensible authentication library.

Contributing to devise_session_expirable

  • Fork the project.

  • Start a feature/bugfix branch.

  • Commit and push.

  • Make sure to add tests.

Copyright © 2013 Riley Lynch, Teleological Software, LLC. See LICENSE.txt for further details.