redirecting devise

Been playing around with Devise for a project that I’m helping a friend with. I ran into something that isn’t too intuitive, so I figured this would a good candidate for a post.

I have the following desired workflow:

  • Anonymous user submits form
  • Controller redirects to login/signup form
  • User logs in
  • Controller posts original form

With Devise, I found that I was being redirected back to the root_url no matter what I tried to throw in the way in the controllers. After I poked around a bit on the documentation, the google group and various blogs I came to the Warden documentation.

Note: Warden is awesome.

I realized that one way of solving the problem would be to add a Warden before_failure callback to my Devise configuration. This would intercept the request and save it to the session, so that I could come back to it in my application_controller. Devise provides an easy way to redirect out of a login, just no method to capture the original request (unless I’m completely missing something).

My attempts to put this into code suffered abject failure.

Fortunately I found this: http://rubyglasses.blogspot.com/2008/04/redirectto-post.html

Turns out you can’t redirect to a POST.

Philosophy of HTTP aside, this makes my desired workflow impossible. The only way to mimic it would be to add model creation logic to my ApplicationController#after_sign_in_path_for… and that would make me cry. Instead I’ve changed my workflow so it works with a GET:

  • Anonymous user submits form
  • Controller redirects to login/signup form
  • User logs in
  • User redirected back to form, which is filled out

Here’s my devise initializer:

Devise.setup do |config|
  # ... normal setup options
  Warden::Manager.before_failure do |env,opts|
    request = Rack::Request.new(env)
    request.session[:return_to] = request.env['REQUEST_METHOD']=="GET" ? request.env['action_dispatch.request.path_parameters'] : request.env['HTTP_REFERER']
    request.session[:return_params] = request.env['action_dispatch.request.request_parameters']
  end

And here’s what I added to my application_controller.rb:

def after_sign_in_path_for(resource)
  if request.session[:return_to].is_a? String
    [request.session[:return_to], request.session[:return_params].to_query].join("?")
  elsif request.session[:return_to].is_a? Hash
    request.session[:return_to].merge!(request.session[:return_params])
  else
    super
  end
end

Now in my form action, I need to make a small update:

## before
  @thing = Thing.new
## after
  @thing = Thing.new(params[:thing])

So after logging in, the user is redirected back to the form. All the params are there in the GET, though, so it fills itself out.

I could see cases where you would NOT want to do this… confidential information in a GET request may show up in a browser’s history, even if the page content passes over SSL. But for anonymous information, this works out pretty nicely.

Comments are closed.