Session cookie expiration time in Rails

Update — this plugin won’t work as of Rails 2.3. See comments.

Solution for 2.3 is here: http://squarewheel.pl/posts/3

By default Rails do not support setting expiration of session cookies to ” after last visit”. The code at Rails wiki shows how to set up cookie expiration for a whole application, but it does not support setting this per-controller (or per-action) like other session options, due to the use of class variables.

This became a problem for us recently, as I needed to set different session cookie expiration times for different controllers.

Here is the solution, rewritten from scratch:

# -- put this into vendor/plugins/sliding_sessions/init.rb --
module SlidingSessions
  # Allows using :session_expires_after option for session cookies
  # NOTE it does not perform any checks on server-side, it just
  # sets the session cookie expiration time.
  # see http://wiki.rubyonrails.org/rails/pages/HowtoChangeSessionOptions
  # for a recipe to perform server-side expiration checks

  # (C) Paweł Stradomski , released under MIT licence
  def self.included(base)
    base.class_eval do
      alias_method_chain :set_session_options, :sliding
    end
  end

  def set_session_options_with_sliding(request) #:nodoc:
    set_session_options_without_sliding(request)
    if request.session_options && request.session_options[:session_expires_after] then
      request.session_options = request.session_options.clone
      request.session_options[:session_expires] = Time.now + request.session_options[:session_expires_after]

      # cookie store only sends cookie if data changed. But we need  to send it with every request
      # to update the expiration time
      request.session[:sliding_session_expiration] = request.session_options[:session_expires].to_s
    end

    request.session_options
  end
end

ActionController::Base.send(:include, SlidingSessions)
# -- end of vendor/plugins/sliding_sessions/init.rb --

The above code (module definition + ActionController::Base.extend(SlidingSessions)) should be put into vendor/plugins/sliding_sessions/init.rb

Now you can use

session :session_expires_after => 8.hour # in any controller

in any of your controllers, even application.rb

Of course it only deals with session cookies, you still need to perform server-side checks for expired sessions and write a cron job to remove stale sessions.

About these ads

21 Responses to “Session cookie expiration time in Rails”

  1. Alex Golubev Says:

    Hi,
    this solution seem to bee much better than the CGI::Session plugin. Yet, I can’t figure out where should I put that code. I tried to put it application.rb but it seem that I get an endless loop => calling from session_options_for_with_sliding to session_options_for_without_sliding aliased to session_options_for which is aliased to session_options_for_with_sliding.

    I would appreciate if you could help me with that.

    Thanks,
    Alex Golubev.

  2. squarewheel Says:

    Updated the text a bit to show where to put the code (vendor/plugins/sliding_sessions/init.rb – you’ll need to create the ‘sliding_sessions’ directory).

    It’s also available now at http://squarewheel.pl/files/sliding_sessions.zip

  3. palash Says:

    I have done all the things you have said, and wrote the following code in application.rb, but the code is not doing anything

    session :session_expires_after => 1.minute, :after_expiration => :some_method

    Please help

  4. squarewheel Says:

    I don’t know why you expect after_expiration => :some_method to work. I’ve just grepped through rails source code – and found no file containing after_expiration.

    Anyway, I just created an empty rails project (with rails 1.2.6), unpacked the zip above into vendor/plugins (so I had vendor/plugins/sliding_sessions/init.rb file) and added session :session_expires_after => 1.minute to application.rb. Checking the cookie with firefox clearly shows it expires after 1 minute, so my patch works as advertised.

    I’ve also checked with rails 2.0.1 and it still works without any modification.

  5. Alex Golubev Says:

    squarewheel many thanks, this one is working great :-)

  6. Ralph Says:

    It seems like the plugin doesn’t work on PRODUCTION environment, any suggestion?

  7. squarewheel Says:

    Seems that in current version of rails session options are not run for every request, but only on opening of new session. Will investigate and prepare new patch later.

  8. Mo J. Says:

    Hi, any ideas on when the patch would be ready for rails 2.1.0. I just updated from edge to the stable release and found it stopped working.

    Thanks,
    Mo

  9. squarewheel Says:

    Perhaps today; if I don’t manage to find time today then until then end of week.

  10. squarewheel Says:

    Uploaded the new version. See if it works, feel free to post comments if anything goes wrong. Once again – keep in mind it only changes cookie expiration time, it does not perform any server-side checks!

    To anybody interested in why it stopped working: the new cookie store only sends updated cookie if the session data changed – so if session data was kept identical in subsequent requests, then no new cookie was issued – so the browser did not update session expiration time.

  11. Mo J. Says:

    Thanks for that. Initial testing, works like a charm. Thanks for the info on why as well. Appreciate your promptness on the fix. If your ever in Melbourne, AU i’ll buy you a beer

  12. Allan L. Says:

    Your stuff works as advertised Rails 2.1.0, production environment.

    Thank you for sharing this!

  13. Alberto Says:

    Hi! I’ve used your module with rails 2.1 and it works really well. But today I update rails to v2.3 and it stops working.
    It gives me an error when starting my application:

    rails/activesupport/lib/active_support/core_ext/module/aliasing.rb:33:in `alias_method’: undefined method `set_session_options’ for class `ActionController::Base’ (NameError)
    from …/rails/activesupport/lib/active_support/core_ext/module/aliasing.rb:33:in `alias_method_chain’
    from …/plugins/sliding_sessions/init.rb:11:in `included’
    from …/plugins/sliding_sessions/init.rb:10:in `class_eval’
    … and so on …

  14. Alberto Says:

    Quicker, I’ve looked better on the Net and I’ve found that alias_method_chain is deprecated from > 2.1 (http://apidock.com/rails/Module/alias_method_chain).
    Google found some recent posts about that but my knowledge about ruby and rails is not so advanced so I don’t know how to solve the problem.

  15. squarewheel Says:

    Fortunately I haven’t been working with RoR for 7 months now, so I’m a bit out-of-date on latest changes. It seems session handling had changed a lot in 2.3, the “session“ method itself is deprecated now.

    I’ll look into the matter later this week, should be a simple matter of changing the plugin to use “around_filter“ and modify the session cookie… though it might not.

  16. squarewheel Says:

    Quick update—if you can set expiration period globally, for whole application, and do not need to set it up on per-controller basis, then just modify your “config/intializers/session_store.rb” and add :expire_after key with desired period. When it comes to per-controller changes, then it becomes more complicated. It seems it’s too late to change session cookie options at that time (it shouldn’t be, as the cookie is not yet sent then, but it is). A quick glance at Rack::Session middleware shows it should allow late changes to options, but I haven’t yet figured out how rails exactly use that middleware.

  17. squarewheel Says:

    The solution is here: http://squarewheel.pl/posts/3

  18. Alberto Gasparin Says:

    Thank you! Great job.

  19. Robb Shecter Says:

    Hi,

    Thanks for the work on this. It seems to work great for me, but I just realized that the statement must be in the body of a controller, not in a controller method. Would you happen to know of a way to invoke the plugin from within an action method? (I want to change the cookie expiration date depending on some factors.)

    As a work-around, I can redirect to another controller that simply makes the call to the plug-in in its body.

  20. squarewheel Says:

    This plugin is obsolete, update to rails 2.3.x.

    Other than that, you can try to alter request.session_options[:expires_after] in your controller method, just make sure to clone session_options first (it’s a frozen hash). I think it might not work at that stage though.

  21. Misha Rabinovich Says:

    Hello,
    This definitely didn’t work for me in 2.1.1
    Application controller body had the call, however, it didn’t seem to do anything (and I added the module).
    Ended up just using JS to extend the exp time.
    m

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: