Rails: Completing Requested Action After Authentication

by

Within Seekler, we have several actions that are only available to logged-in users. We wanted a general solution that would first redirect the user to login or create an account and then complete the action the user initially requested. The primary motivator for this was non-logged-in users clicking ‘create your own list’ on a Seekler list. If a user clicked this link, we wanted to allow them to login or register and then have the list creation action take place and return them to their newly created list. We had other cases of site actions, that also required being logged in, so I was hoping to find a simple way to do this across the site.
We use the acts_as_authenticated plugin (I know most people are now using restful_authentication, we haven’t switched yet), which relies on before_filters to verify users are logged in before using logged-in-only actions. I ended up just adding one line into that system that makes it simple to return the user to his or her previous action after logging in.
In a controller using acts_as_authenticated, there is normally a before_filter requiring authentication before any actions except the actions available to all visitors

before_filter :login_required, :except => [ :show, :list]

This means that on an action like :create the user will end up calling the login_required method in /lib/authenticated_system.rb. If you add the following line to that method, it will record the information you need to redirect the user back to the action they intended after they login.

def login_required
username, passwd = get_auth_data
self.current_user ||= User.authenticate(username, passwd) || :false if username && passwd
session[:last_action] = request.env['REQUEST_URI'] unless logged_in? && authorized? # add this line!
logged_in? && authorized? ? true : access_denied
end

At this point, the filter will direct all non-logged-in users to your associated login failure action, in our case /account/login
In the login action, (after doing any other login specific code) you just have to redirect the user if they have :last_action set

def login
# login stuff
if session[:last_action]
redirect_to session[:last_action]
else
redirect_to :controller => 'account', :action => 'index'
end
end

You can add a bunch of other things like ignoring certain actions you don’t want to forward the user to, or also return the user to wherever they were before clicking a login link, but this simple version gives the basic idea. Obviously this code is specifically for the acts_as_authenticated plugin, but it should be relatively easy to generalize this same sort of solution to work for any type of authentication that is in use on your Rails project.
If you want to see this in action, just visit a list like Best Charities on Seekler and click the ‘create your own list’ link in the top right.

Advertisements

5 Responses to “Rails: Completing Requested Action After Authentication”

  1. MS Says:

    What happens if REQUEST_URI is an offsite link? Wouldn’t it try to redirect back to that site after logging in?

  2. Dan Says:

    MS,
    You can filter around that to check and only forward if there are cases like that. Since all of these are relatively uncommon actions that are actions with in our site we haven’t noticed any problems like that yet.
    That is a good point that might be worth adding a quick filter for. We do some various checks and filtering before forwarding the users, so it would be simple enough to verify that as well.

  3. Eric Anderson Says:

    I have made similar code for resuming an action after login and it works well. Instead of getting the REQUEST_URI from the environment I just stored the params in the session. Then did the redirect to the stored params. Either way will probably work well but just wanted to post another option.
    Also, when would REQUEST_URI have an offsite link? Are you maybe thinking of HTTP_REFERRER? REQUEST_URI should always be the URI of the current request.

  4. Dan Says:

    Eric
    good point about the HTTP_REFERRER, I knew there was a reason I didn’t worry about it when I first made the code. Yeahs ince it is the current request even if another site links into you it will always be the location on your sever where they linked to. Storing all the params, would work as well, I just came up with this solution before coming up with that.
    Thanks for yout thoughts.

  5. Jim Says:

    Crikey, I gotta read all your old blog posts. Adding request completion after logging in was a tiny todo on my giant list. Thanks for prodding me to add it! Of course, I had to change it a smidge. I used the railsy request.request_uri instead of request.env[‘REQUEST_URI’].
    Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: