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!