Hi, welcome to Word of Mike, my little corner of the internet. I am a Software/Web Developer working in North Yorkshire. I mainly write about programming but my other passion is politics so beware. (click to hide)

2015-11-12 16:45:08 UTC

Testing Session Expiry in Rails

We had a recent issue with a Rails app whereby the sessions weren't expiring as we expected.

To set an expiry on session cookies you use the 'expire_after' option in your session store configuration (typically config/initializers/session_store.rb) like this:

MyApp::Application.config.session_store(:cookie_store, {
  key: '_my_app_session',
  expire_after: 4.hours

It turned out I'd made a silly error and used "expires_after" instead of the correct "expire_after", so it was being silently ignored and sessions weren't expiring.

I wanted to write a test to ensure that sessions were expiring after the correct period of time and that problems like this didn't silently occur in the future. I took inspiration from the Rails framework test for expire_after to come up with this:

require 'minitest/mock'

class SessionTest < ActionDispatch::IntegrationTest
  test "session expiration works" do
    user = users(:user_one)

    get "/login"
    assert_response :success

    # stub Time.now to a fixed time so we know what to check for
    time = Time.now
    Time.stub :now, time do
      post_via_redirect "/login", {
        email: user.email,
        password: "Password123"
      assert_equal '/dash', path, "Login failed."

      # get the expected expiry time string
      expected_expiry = (time + 4.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000")

      # check the cookie matches this regexp expression containing the
      # expiry time
      assert_match /_my_app_session=[^;]+; path=\/; expires=#{Regexp.quote(expected_expiry)}; HttpOnly/,
        "Cookie not with correct expiry time"

    # go forward 3 hours (less than our expiry) and check our session
    # is still valid
    second_request_time = time + 3.hours
    Time.stub :now, second_request_time do
      get '/dash'

      assert_equal '/dash', path

    # go forward 5 hours from our last request (more than expiry time)
    # and check that it's redirecting us to the login page, thus
    # meaning our session has expired.
    third_request_time = second_request_time + 5.hours
    Time.stub :now, third_request_time do
      get '/dash'

      assert_redirected_to login_path