Daniel Fone

Ruby/Rails Engineer

Devise causing a CookieOverflow error

November 2014

tl;dr If you're going to submit forms via GET, keep your parameter names short!

Today I came across an exception that was caused by a collision of two uncommon circumstances:

  • A large and complex form, submitted via GET (it was a search form with a lot of parameters)
  • A Devise session time out

How It Happens

  1. A user who signed in some time ago has the form open. They submit the form which GETs a very long url from the server.
  2. Devise attempts to authenticate the user and realises that their session has expired.
  3. Devise attempts to redirect the user to the sign in form, but first it stores the url in the session as user_return_to.
  4. The url is too long to fit in the session and a ActionDispatch::Cookies::CookieOverflow exception is raised.

Reproducible Test Case

Fortunately, it’s very easy to replicate the problem in tests. Anytime an unauthenticated user requests a url approaching ActionDispatch::Cookies::MAX_COOKIE_SIZE, Devise will attempt to store the the url and cause the exception to be raised.

  # /spec/requests/large_request_spec.rb
  require 'spec_helper'

  RSpec.describe 'A very large request', type: :request do
    it 'should not overflow cookies' do
      get '/', foo: 'x' * ActionDispatch::Cookies::MAX_COOKIE_SIZE
      expect(response).to redirect_to '/users/sign_in'
    end
  end

This reliably produces

1) A very large request should not overflow cookies
   Failure/Error: get '/', foo: 'bar' * 1000
   ActionDispatch::Cookies::CookieOverflow:
     ActionDispatch::Cookies::CookieOverflow

Solutions

There were a couple of approaches that I didn’t want to pursue.

  • Changing the form method to POST. This substantially alters the behaviour of the form, “breaking” the back and refresh functionality, and preventing users from easily sharing search results.
  • Abbreviating the parameter name. Because the forms are all generated by rails helpers, this would require overriding a lot of simple behaviour. The form is also dynamic and the number of GET parameters is not fixed. This means we can never ensure that the GET url will be small enough.

Ultimately, I wanted to solve this at the Devise level. The problematic behaviour is defined in the store_location module, which is included into Devise’s failure app.

I wrote a simple initializer to monkey patch the method and prevent it storing excessively long urls in the session.

The downside to this approach is that the user loses their place in the form, but it is far better than an unhandled exception when submitting. I’ve submitted a pull request to the Devise project, but to be honest this is such an edge case that it may not warrant it. We’ll see what happens.

comments powered by Disqus