web-dev-qa-db-fra.com

Transformez la connexion Facebook d'Omniauth en une fenêtre contextuelle

J'utilise la gemme omniauth avec Rails et cela fonctionne très bien avec la connexion des utilisateurs, mais chaque fois qu'il vous amène à la page de connexion fb puis vous redirige. Je me demandais s'il y avait un moyen pour faire ce que font la plupart des pages et afficher la connexion fb dans une fenêtre contextuelle, puis recharger la div parent une fois que c'est terminé.

Merci!

58
Danny

Bien sûr, vous pouvez facilement.

À votre avis:

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

Dans votre 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;
});

Et puis dans votre vue de rappel:

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

Cela fera apparaître votre authentification Facebook dans une fenêtre contextuelle 600x400 centrée, puis lorsque l'utilisateur reviendra de l'authentification, la vue fermera la fenêtre contextuelle et actualisera la page parent. Il se dégrade gracieusement si un utilisateur clique avec le bouton ctrl sur le lien, ou si Javascript n'est pas activé également.

125
Chris Heald

Ok, donc il y a un problème avec la solution de Chris Heald si vous utilisez OmniAuth en conjonction avec Devise. Le problème est que lorsque vous rechargez la fenêtre (c'est-à-dire sur la page de connexion), Devise vous amènera au root_path, ignorant complètement l'URL à laquelle vous tentiez d'accéder et générera le message d'erreur "Vous êtes déjà connecté". Cela est logique car Devise protège un utilisateur connecté contre l'accès à la page de connexion en redirigeant vers la page d'accueil. En rechargeant la page de connexion immédiatement après la connexion, vous vous retrouverez avec ce problème.

Donc, ma solution pour quelqu'un qui utilise Devise est la suivante:

# 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

Donc normalement, après avoir trouvé ou créé un utilisateur en utilisant le hachage retourné par un certain fournisseur (Facebook, Twitter, etc.), nous appellerions la fonction Devise sign_in_and_redirect. Mais nous ne pouvons pas rediriger tout de suite (rappelez-vous, en ce moment, l'utilisateur est actuellement dans une fenêtre popup) donc nous avons simplement sign_in l'utilisateur.

Ensuite, nous devons transmettre l'URL que l'utilisateur tentait d'accéder à la vue, et nous pouvons obtenir cette URL en utilisant la méthode de Devise after_sign_in_path_for.

Enfin, nous devons rendre la vue. Comme nous n'utiliserons la vue que pour appeler du JavaScript, il n'est pas nécessaire de rendre la mise en page, nous la désactivons donc pour ne pas nous ralentir. Voici cette vue:

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

De cette façon, l'utilisateur est redirigé vers l'URL appropriée après la connexion et le message flash correct s'affiche.

Avec JavaScript désactivé

Après quelques tests, j'ai réalisé que cette solution ne permettait pas l'authentification sans JavaScript, je voudrais donc faire un 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);
}

Le changement ici consiste à ajouter un paramètre simple appelé popup à l'url à l'aide de JavaScript. OmniAuth sera assez aimable pour stocker tous les paramètres de requête ajoutés à l'URL de la demande. Donc, finalement, nous vérifions ce paramètre dans le contrôleur. S'il existe, c'est parce que JavaScript est activé:

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

N'oubliez pas de faire de même pour votre action failure, qui est appelée lorsque l'utilisateur n'accepte pas la connexion.

Je n'aurais pas pu faire ça sans la solution de Chris Heald, alors .. Merci beaucoup!

29
Ashitaka

Publier au cas où cela aiderait les autres. J'utilisais la réponse de Chris Heald mais j'ai rencontré des problèmes avec le dernier bit de javascript fermant les liens de la nouvelle fenêtre. Par exemple, si j'ai publié un lien vers mon site sur Facebook, lorsque les utilisateurs ont cliqué sur le lien, la nouvelle fenêtre se fermait automatiquement Chrome car la condition vérifie uniquement "if (window.opener)"

J'ai fini par résoudre ce problème en utilisant une variable globale (popupValue). Il existe peut-être des solutions plus élégantes, mais j'ai pensé partager au cas où d'autres rencontreraient le même problème:

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

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


if(window.opener && window.opener.popupValue === 'on') {
 delete window.opener.popupValue;
 window.opener.location.reload(true);
 window.close()
}
});
4
pejmanjohn

J'ai fini par utiliser le SDK JS de Facebook car c'est plus facile.

# In facebook.js.coffee
jQuery ->
  $('body').prepend('<div id="fb-root"></div>')

  $.ajax
    url: "#{window.location.protocol}//connect.facebook.net/en_US/all.js"
    dataType: 'script'
    cache: true

window.fbAsyncInit = ->
  FB.init(appId: 'YOUR-APP-ID', cookie: true)

  $('#sign_in').click (e) ->
    e.preventDefault()
    FB.login (response) ->
      window.location = '/auth/facebook/callback' if response.authResponse

  $('#sign_out').click (e) ->
    FB.getLoginStatus (response) ->
      FB.logout() if response.authResponse
    true

Ensuite, selon vous:

<%= link_to "Sign in with Facebook", "/auth/facebook", id: "sign_in" %>
<%= link_to "Sign out", signout_path, id: "sign_out" %>

C'est directement de l'astuce de Sergio Gutierrez ici - https://coderwall.com/p/bsfitw

3
Han