29 April 2009

Misadventures with Restful Authentication

This may seem trivial to some people, but I got a bit confused finding a path through the Restful Authentication plugin for Rails.

To restrict access inside a controller, I originally put this line inside the controller class:

before_filter :authorize

This invokes an authorize method before any actions in the controller are called.

authorize is a private method that I put inside the class ApplicationController. It checks the logged_in? method, setting an :original_uri value in the Rails session object and then redirecting users to a login page, if required (it’s private because methods in ApplicationController are available as instance methods in all other controllers and so public methods would be exposed to end users as actions).

logged_in is a protected method inside the AuthenticatedSystem module (which needs to be included in ApplicationController, so that the logged_in? method is available to it).

logged_in? in turn calls the current_user method also in AuthenticatedSystem (and all the action described from here on in takes place inside this module), which attempts to identify the user from (in order) the Rails session object, standard HTTP authentication headers and then any cookies that might have been set. Otherwise it returns false and session[:original_uri] is set before the user is redirected to a login page.

It turns out that there is also a :return_to key in the Session object, which sounds like it might be used for the same thing as :orginal_uri, and like :original_uri gets its value from request.request_uri. But :return_to only gets set when the login_required? method is called, which in turn calls logged_in? as well as setting the value of :return_to and giving a hook to extend the default behaviour to something more complex than just checking whether a user is logged in.

The problem for me is that I have been using the method redirect_back_or_default to get users back to where they were going before logging in and this method uses :return_to and not :original_uri.

Reading the very helpful comments left in the AuthenticatedSystem class by its authors it has now become obvious that I have made a significant design mistake: I added an added layer of complexity, i.e. the :authorize method, where none was needed, and then compounded my error by accessing the logged_in method directly instead of going through login_required.

Upshot: unexpected behaviour in my application leading to much frustration.

Moral: When somebody leaves very helpful comments in their code, read the comments before using the code.

d. sofer