Web Development & Programming Thoughts

By Timothy Elliott

How Google Toolbar Screws Your Rails Authentication Forwarding

4/1/2009

A common approach to Rails authentication is with a before_filter in your controller to authenticate your users:

before_filter :authorize

The authorize method stashes the originally requested URL in your visitor's session if they are not logged in yet:

def authorize
  unless session['user']
    session['return_to'] = request.request_uri
    redirect_to some_login_path
  end
end

When the user logs in they are redirected to the 'return_to' session parameter:

redirect_to session['return_to'] ? session['return_to'] : some_default_path

This is all pretty standard. However, I have found that the above approach can break thanks to some odd favicon requests. Here is an excerpt from my web server log:

xxx.xxx.xxx.xxx - - [01/Apr/2009:12:42:02 -0700] "GET /users/favicon.gif HTTP/1.1" 302 106 "-" "Mozilla/4.0 (compatible; GoogleToolbar 5.0.2124.6042; Windows XP 5.1; MSIE 7.0.5730.13)"

Looking through the log, there appears to be a pattern where these requests include "GoogleToolbar" in the agent header. A Google search for "google toolbar favicon" returns some mystified webmasters but not much of an explaination to why the toolbar is doing this.

Anyhow, here is a series of events that will trigger the 404 for your user:

  1. Browser requests /some/path/requiring/authentication.
    Rails puts '/some/path/requiring/authentication' into the user's 'return_to' session parameter.
  2. Google Toolbar requests /some/path/requiring/authentication/favicon.gif
    Rails replaces 'return_to' session parameter with '/some/path/requiring/authentication/favicon.gif'
  3. The user logs in.
    Rails forwards the user to '/some/path/requiring/authentication/favicon.gif' and gets a 404 error.

The Google toolbar request to favicon.gif overwrote the original 'return_to' session parameter with a bad url.

The fix for this problem is to reject 'return_to' session parameters that include 'favicon':

redirect_path = if session['return_to'] && !session['return_to'][/favicon/]
  session['return_to']
else
  some_default_path
end
redirect_to redirect_path