Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turn omniauth facebook login into a popup

I'm using the omniauth gem with rails and it works great with loging in users, but everytime it takes you to the fb login page then redirects you back. I was wondering if there is a way to do what most pages do and show the fb login in a popup and then reload the parent div once that is complete. Any ideas?

Thanks!

like image 354
Danny Avatar asked Dec 20 '10 16:12

Danny


2 Answers

Sure, you can easily.

In your view:

=link_to "Log in with Facebook", omniauth_authorize_path(:user, :facebook), :class => "popup", :"data-width" => 600, :"data-height" => 400

In your application.js:

function popupCenter(url, width, height, name) {
  var left = (screen.width/2)-(width/2);
  var top = (screen.height/2)-(height/2);
  return window.open(url, name, "menubar=no,toolbar=no,status=no,width="+width+",height="+height+",toolbar=no,left="+left+",top="+top);
}

$("a.popup").click(function(e) {
  popupCenter($(this).attr("href"), $(this).attr("data-width"), $(this).attr("data-height"), "authPopup");
  e.stopPropagation(); return false;
});

And then in your callback view:

:javascript
  if(window.opener) {
    window.opener.location.reload(true);
    window.close()
  }

This'll pop up your Facebook auth in a centered 600x400 popup, then when the user returns from authentication, the view will close the popup and refresh the parent page. It degrades gracefully if a user ctrl-clicks the link, or doesn't have Javascript enabled, too.

like image 197
Chris Heald Avatar answered Oct 21 '22 23:10

Chris Heald


Ok, so there's a problem with Chris Heald's solution if you are using OmniAuth in conjunction with Devise. The problem is that when you reload the window (that is on the login page), Devise will take you to the root_path, completely ignoring the url you were trying to access and will yield the error message "You are already signed in". This makes sense because Devise protects a logged in user from accessing the login page by redirecting to the homepage. By reloading the login page immediately after logging in, you'll end up with this problem.

So my solution for someone using Devise is as follows:

# add this wherever needed in your_authentications_or_callbacks_controller.rb
sign_in user
@after_sign_in_url = after_sign_in_path_for(user)
render 'callback', :layout => false

So normally, after finding or creating a user using the hash returned by a certain provider (Facebook, Twitter, etc..), we'd call the Devise function sign_in_and_redirect. But we can't redirect just yet (remember, right now, the user is currently in a popup window) so we simply sign_in the user.

Next up, we need to pass the url the user was trying to access to the view, and we can get that url using Devise's method after_sign_in_path_for.

Finally, we need to render the view. Since we'll only use the view to call some JavaScript, there is no need to render the layout, so we turn it off to not slow us down. Here's that view:

# views/your_authentications_or_callbacks/callback.html.erb
<script type="text/javascript">
  window.opener.location = '<%= @after_sign_in_url %>';
  window.close();
</script>

This way the user is redirected to the proper url after signing in and the correct flash message is displayed.

With JavaScript disabled

After some testing I realised that this solution wasn't allowing authentication without JavaScript so I'd like to make an addendum.

function popupCenter(linkUrl, width, height, name) {
    var separator = (linkUrl.indexOf('?') !== -1) ? '&' : '?',
        url = linkUrl + separator + 'popup=true',
        left = (screen.width - width) / 2,
        top = (screen.height - height) / 2,
        windowFeatures = 'menubar=no,toolbar=no,status=no,width=' + width +
            ',height=' + height + ',left=' + left + ',top=' + top;
    return window.open(url, name, windowFeatures);
}

The change here is adding a simple parameter called popup to the url using JavaScript. OmniAuth will be kind enough to store any query params added to the request url. So finally we check for that param in the controller. If it exists, it's because JavaScript is enabled:

if request.env['omniauth.params']['popup']
  render 'callback', :layout => false
else
  redirect_to @after_sign_in_url
end

Also, don't forget to do the same for your failure action, which is called when the user does not accept logging in.

I could not have done this without Chris Heald's solution, so.. Thank you so much!

like image 29
Ashitaka Avatar answered Oct 21 '22 22:10

Ashitaka