Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails + Angular: Redirection After Sign-In/Up

I'm using Rails + Devise for my log in and sign-up, but the vast majority of my front-end uses angular routing. When someone attempts to go to a specific page without being logged in I want them to be redirected after a successful log-in. Usually this would be a simple matter of storing the path in the session and modifying the after_sign_in_path_for method. However, I cannot seem to find a Rails method that will effectively get the actual path. I'm confident I could do this through JS and then store it as a cookie (or Angular really), but I'd prefer to keep it within Rails. Is there a method I'm missing that will give me the actual path including the /#/whatever/whatever?

It seems that the below should work:

  request.original_url

based on this (http://api.rubyonrails.org/classes/ActionDispatch/Request.html)

But it isn't returning the hashtag or following parameters.

Perhaps due to what this person says (AngularJS routing without the hash '#')

The # is an old browser shortcircuit which doesn't fire the request, which allows many js frameworks to build their own clientside rerouting on top of that.

Is there a clean way to gather the full path within Rails or do I need to go into Angular/JS?

like image 608
Morgan Avatar asked May 06 '14 02:05

Morgan


2 Answers

I had the same problem but with Backbone. The issue is that browsers don't send the anchor part of the URL to the server, so Rails can't handle this issue, only the JS in the front-end.

I am posting the workaround that worked for me.

In the sign in page add this javascript snippet (most likely in app/views/sessions/new.html.haml)

:javascript
  // keeping the hash route into a cookie to retrieve it after sign in redirect
  cname = 'hash_route'
  cvalue = window.location.hash
  // checking if the hash value is blank, don't overwrite the cookie
  // this check covers the case of user giving wrong credentials in login
  // then successfully login within the expiration time of the cookie
  // other cases that aren't supported are relatively rare
  if(cvalue.length){ 
    var d = new Date();
    d.setTime(d.getTime() + (3*60*1000)); // enough time to log in
    var expires = "expires="+d.toGMTString();
    document.cookie = cname + "=" + cvalue + "; " + expires;
  }

In the SessionsController#create (or you can overwrite the Devise method "after_sign_in_path_for")

def create
  # ------- Devise code --------
  self.resource = warden.authenticate(auth_options)
  set_flash_message(:notice, :signed_in) if is_navigational_format?
  sign_in(resource_name, resource)

  # ------- This is the code --------
  # handling the case of redirecting to a link that had a hash route    
  location = after_sign_in_path_for(resource)  

  if cookies[:hash_route].present?
     location+= cookies[:hash_route]
     cookies.delete :hash_route
  end

  respond_with resource, :location => location
end

I hope this helps.

like image 151
Brary Avatar answered Oct 15 '22 09:10

Brary


Thanks a lot @bero for this beautiful answer. It works perfectly.

I'll add my 2 cents with the following variations:

<!-- app/views/sessions/new.html.erb: -->
<script type="text/javascript">
  cname = 'hash_route'
  // Added a .replace(/^#/, '') here to store '/hash_path' instead of '#/hash_path'
  cvalue = window.location.hash.replace(/^#/, '');
  if(cvalue.length){
    var d = new Date();
    d.setTime(d.getTime() + (3*60*1000)); // enough time to log in
    var expires = "expires="+d.toGMTString();
    document.cookie = cname + "=" + cvalue + "; " + expires;
  }
</script>

And in the ruby side:

# application_controller.rb
  def after_sign_in_path_for(resource)

    if cookies[:hash_route].present?
       anchor = cookies[:hash_route]
       cookies.delete :hash_route
    end

    webpages_app_path(anchor: anchor)
  end

I prefer to use the path_helper with anchor and to override after_sign_in_path_for (which you probably already had), instead of customizing Devise controllers.

But the whole thing is the same.

like image 4
Augustin Riedinger Avatar answered Oct 15 '22 09:10

Augustin Riedinger