Quick tip: store_location with subdomains

Posted by Jon
on Thursday, May 01

Both restful_authentication and the older acts_as_authenticated have a handy method called store_location. This method stores a URL in a session variable for future reference. The obvious use case involves login. For example, if you’re browsing a product anonymously and want to write a review, you’ll need to sign in first. So if you click a link on that product page that requires you to be logged in, and this sends you through the login process, you’ll ideally want to be returned right back to where you were before you logged in. store_location enables this, along with the redirect_back_or_default(), also provided by Rick Olson’s authentication plugins.

You store a location like this:

1
2
3
4
5
6
7

  def private_action
    unless logged_in?
      store_location
      redirect_to login_path
    end
  end

After authenticating the user, you send them back to the stored location with this:

1
2
3
4
5
6

  def login
    if login_successful? # pseudocode, obviously
      redirect_back_or_default(home_path)
    end
  end

If a location is stored in session, redirect_back_or_default will send the user that location. Otherwise, it redirects to the default path.

This is pretty handy. But unfortunately, it doesn’t jump across domains, including subdomains. Tumblon lets parents set up blogs for their families, and these blogs are either identified by a subdomain (e.g. myfamily.tumblon.com) or by a top-level domain (coming soon). Tumblon also has privacy controls, so I can set a story to be viewable only by my family and friends. So if an anonymous user hits the URL of a private photo/story/video, they should be redirected to the login screen and then right back to the item they were trying to view. But out of the box, store_location can’t handle this.

Let’s look at the store_location method to see why. This method is in lib/authenticated_system.rb.

1
2
3
4

    def store_location
      session[:return_to] = request.request_uri
    end

store_location uses the request.request_uri method, which only provides the relative path (e.g. /photos/932783). So if you login at tumblon.com, store_location won’t return you to myfamily.tumblon.com/photos/932783 – it will send you to tumblon.com/photos/932783. Your app could have logic to redirect from this page to the subdomain, but an easier solution is just to create a new store_location method, like store_location_with_domain. Or you could always override the store_location method to always use request.url instead of request.request_uri if you don’t want a separate method.

1
2
3
4

    def store_location_with_domain
      session[:return_to] = request.url
    end

Put this method in application.rb, and you can now use redirect_back_or_default to hit an exact URL – complete with subdomain, top-level domain, and port.

Comments

Leave a response

  1. Cameron BoothMay 05, 2008 @ 09:28 PM

    thanks for the post, useful thing to know for sure! Am I missing something though, should there be an alias_method_chain call in there, or something like that so that the original store_location gets overridden by store_location_with_domain? Or I guess you would just call store_location_with_domain when you need it?

    Cheers, Cameron

  2. JonMay 08, 2008 @ 03:29 PM

    Hi Cameron – alias_method_chain would make this more transparent. At the moment, I’m just calling the store_location_with_subdomain method directly in order to leave store_location alone (for cases when a subdomain isn’t needed), but there’s no reason I can think of why you couldn’t just always store the full URL.

    It would be great to replace the stock store_location with a better store_location method that takes an argument (def store_location(subdomain = false); ...; end). The downside of this is that store_location is often called in a before_filter, and I don’t know of a clean way to pass arguments to a method called by before_filter.

    But again, you might just want to always store the full URL rather than just the relative path, so alias_method could do this easily.