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.