In general, be only as secure as you need to be. I am ok with my bank logging me out after a short period of inactivity. However, I am willing to risk the occasional prank post on Facebook to not have to keep logging back in. As long as the social sites require a re-entering of a password before allowing a password change or any other personal information changes, they are secure enough from my point of view.
Session Expiration Tasks
If you want to expire sessions, you need to do a couple of things:
- Keep track of the time of the user's last action
- Enforce the timeout on the server side by having the server redirect to a login page if a different page is requested after a timeout
- Make sure the redirect also happens on Ajax updates
For purposes of this code, I am assuming that you have a login system like shown in Michael Hartl's "Ruby on Rails Tutorial".
Following the example I found at http://snippets.dzone.com/posts/show/7400 I made my ApplicationController look like:
class ApplicationController < ActionController::Base protect_from_forgery before_filter :session_expiry before_filter :update_activity_time include SessionsHelper def session_expiry get_session_time_left unless @session_time_left > 0 sign_out deny_access 'Your session has timed out. Please log back in.' end end def update_activity_time session[:expires_at] = 30.minutes.from_now end private def get_session_time_left expire_time = session[:expires_at] || Time.now @session_time_left = (expire_time - Time.now).to_i end endIn this code we store an expiration time in the session scope, and we make sure the session hasn't expired before performing any actions. update_activity_time accomplishes the goal of tracking the user's last action and session_expiry enforces the timeout.
Support Ajax Calls
A complication comes up if your web application has Ajax calls. If an Ajax request comes in after the session times out you want to do a full page redirect to the login page, not just a partial update. To accomplish this I modified the deny_access method in the SessionsHelper module to look like:
Client Side Timeout
To call these functions we add the following to our views/layout/application.html.haml
def check_session_alive get_session_time_left if @session_time_left > 0 render 'sessions/check_session_alive', :layout=>false else sign_out deny_access 'Your session has timed out. Please log back in.' end endIf the session has timed out, we just call deny_access which we showed above and already handles the redirect. If the session hasn't timed out, we render sessions/check_session_alive.js.haml which just calls setSessionTimeout again, with the new timeout value:
We don't want to enforce that the session is valid on the session actions like login, logout, and check_session_alive. To fix this we add the following to the top of our SessionsController:
class SessionsController < ApplicationController skip_before_filter :session_expiry skip_before_filter :update_activity_time
With that you have a bare bones session expiration system on your Rails app. I will leave it as an exercise to the reader to add extra features, like giving a user a 5 minute warning that they are about to be logged out.