web-dev-qa-db-fra.com

Obtenir Chrome pour inviter à enregistrer le mot de passe lorsque vous utilisez AJAX pour vous connecter)

REMARQUE: Cette question a été fortement modifiée à partir de sa version d'origine. Le problème a été grandement simplifié.

Des questions similaires ont été posées plusieurs fois auparavant, sous différentes formes - par exemple,

Comment puis-je amener le navigateur à demander d'enregistrer le mot de passe?

Comment le navigateur sait-il quand inviter l'utilisateur à enregistrer le mot de passe?

Cependant, cette question aborde un aspect très spécifique des fonctionnalités de Chrome, il est donc très différent à cet égard.

À en juger par les réponses passées, il semble que la meilleure approche pour obtenir Chrome pour demander l'enregistrement du mot de passe consiste à soumettre le formulaire à un iframe factice, tout en se connectant via AJAX: exemple . Cela a du sens pour moi et donc je bidouille avec quelques exemples de code depuis quelques jours maintenant. Cependant, le comportement de Chrome à cet égard n'a pas de sens pour moi. Du tout. D'où la question.

Pour une raison quelconque, Chrome ne vous invitera pas à enregistrer votre mot de passe si le formulaire qui se soumet à un iframe factice est présent pendant/juste après onDomReady.

Un jsfiddle peut être trouvé ici , mais il est de peu d'utilité car vous devez créer dummy.html localement pour voir le comportement décrit. Donc, la meilleure façon de voir cela est de copier le code HTML complet dans votre propre index.html puis créez un dummy.html fichier aussi.

Voici le code complet pour index.html. Les trois approches sont mises en évidence comme (1), (2), (3). Seulement (2) garantit que l'utilisateur est invité à enregistrer son mot de passe et que (3) ne fonctionne pas est particulièrement déroutant pour moi.

<html>
<head>
<title>Chrome: Remember Password</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>

<script type="text/javascript">  

    $(function(){

        function create_login_form()
        {
            $('#login_form').html(
            "<input type='text' name='email'>" +
            "<input type='password' name='password'>" +
            "<input id='login-button' type='submit' value='Login'/>");
        }

        // (1) this does not work. chrome seems to require time after "onDomReady" to process
        // the forms that are present on the page. if the form fields exist while that processing
        // is going on, they will not generate the "Save Password?" Prompt.
        //create_login_form();

        // (2) This works. chrome has obviously finished its "work" on the form fields by now so
        // we can add our content to the form and, upon submit, it will Prompt "Save Password?"

        setTimeout(create_login_form,500);

    });

</script>
</head>
<body>

<!-- Our dummy iframe where the form submits to -->
<iframe src="dummy.html" name="dummy" style="display: none"></iframe>

<form action="" method="post" target="dummy" id="login_form">

    <!-- (3) fails. form content cannot be present on pageload -->
    <!--
    <input type='text' name='email'>
    <input type='password' name='password'>
    <input id='login-button' type='submit' value='Login'/>      
    -->

</form>

</body> 
</html>

Si quelqu'un pouvait expliquer ce qui se passe ici, je vous en serais très reconnaissant.

EDIT: Notez que ce "problème de sauvegarde du mot de passe" avec Chrome a été étendu aux personnes travaillant avec AngularJS, également développé par Google: Github =

33
EleventyOne

Après avoir étudié ce problème en profondeur, voici mon rapport final:

Premièrement, le problème mis en évidence dans la question était en fait un hareng rouge. Je soumettais incorrectement le formulaire à un iframe (qui arty surligné - mais je n'ai pas connecté les points). Mon approche de ce problème était basée sur cet exemple , que certaines des autres réponses connexes ont également référencé. L'approche correcte peut être vue ici . Fondamentalement, le action du form doit être défini sur le src du iframe (ce qui est exactement ce que @arty a suggéré).

Lorsque vous faites cela, le problème particulier mis en évidence dans la question disparaît car la page n'est pas rechargée du tout (ce qui est logique - pourquoi la page devrait-elle se recharger lorsque vous soumettez à un iframe? Elle ne devrait pas , ce qui aurait dû me prévenir). Quoi qu'il en soit, parce que la page ne se recharge pas, Chrome ne vous demande jamais d'enregistrer votre mot de passe, peu importe le temps que vous attendez pour afficher le formulaire après onDomReady.

Par conséquent, la soumission à un iframe (correctement) n'entraînera JAMAIS Chrome vous demandant d'enregistrer votre mot de passe. Chrome ne le fera que si la page qui contient le formulaire se recharge (note: contrairement aux anciens articles ici, Chrome VOUS DEMANDERA de sauvegarder votre mot de passe si le formulaire a été créé dynamiquement) .

Et donc, la seule solution est de forcer un rechargement de page lorsque le formulaire est soumis. Mais comment faire cela ET garder intacte notre structure AJAX/SPA? Voici ma solution:

(1) Divisez le SPA en deux parties: (1) Pour les utilisateurs non connectés, (2) Pour les utilisateurs connectés. Cela pourrait ne pas être possible pour ceux qui ont de plus gros sites. Et je ne considère pas cela comme une solution permanente - s'il vous plaît, Chrome, s'il vous plaît ... corrigez ce bogue.

(2) Je capture deux événements sur mes formulaires: onClickSaveButton et onFormSubmit. Pour le formulaire de connexion, en particulier, j'attrape les détails de l'utilisateur sur onClickSaveButton et j'effectue un appel AJAX pour vérifier leurs informations. Si ces informations passent, j'appelle manuellement formName.submit() Dans onFormSubmit, je m'assure que le formulaire n'est pas soumis avant l'appel de onClickSaveButton.

(3) Le formulaire se soumet à un fichier PHP qui redirige simplement vers le index.php fichier

Cette approche présente deux avantages:

(1) Cela fonctionne sur Chrome.

(2) Sur Firefox, l'utilisateur n'est désormais invité à enregistrer son mot de passe que s'il s'est correctement connecté (personnellement, j'ai toujours trouvé ennuyeux d'être invité à enregistrer mon pwd lorsqu'il était incorrect).

Voici le code pertinent (simplifié):

INDEX.PHP

<html>
<head>
<title>Chrome: Remember Password</title>

<!-- dependencies -->
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="http://underscorejs.org/underscore.js"></script>
<script type="text/javascript" src="http://backbonejs.org/backbone.js"></script>

<!-- app code -->
<script type="text/javascript" src="mycode.js"></script>
<script>

    $(function(){

        createForm();

    });

</script>

</head>
<body>

    <div id='header'>
        <h1>Welcome to my page!</h1>
    </div>

    <div id='content'>
        <div id='form'>
        </div>
    </div>

</body> 
</html>

MYCODE.JS

function createForm() {

    VLoginForm = Backbone.View.extend({

        allowDefaultSubmit : true,

        // UI events from the HTML created by this view
        events : {
            "click button[name=login]" : "onClickLogin",
            "submit form" : "onFormSubmit"
        },

        initialize : function() {
            this.verified = false;
            // this would be in a template
            this.html = "<form method='post' action='dummy.php'><p><label for='email'>Email</label><input type='text' name='email'></p><p><label for='password'>Password<input type='password' name='password'></p><button name='login'>Login</button></form>";        
        },
        render : function() {
            this.$el.html(this.html);
            return this;
        },
        onClickLogin : function(event) {

            // verify the data with an AJAX call (not included)

            if ( verifiedWithAJAX ) {
                this.verified = true;
                this.$("form").submit();
            }           
            else {
                // not verified, output a message
            }

            // we may have been called manually, so double check
            // that we have an event to stop.
            if ( event ) {
                event.preventDefault();
                event.stopPropagation();
            }

        },
        onFormSubmit : function(event) {
            if ( !this.verified ) {             
                event.preventDefault();
                event.stopPropagation();
                this.onClickLogin();
            }
            else if ( this.allowDefaultSubmit ) {
                // submits the form as per default
            }
            else {
                // do your own thing...
                event.preventDefault();
                event.stopPropagation();
            }
        }
    });

    var form = new VLoginForm();
    $("#form").html(form.render().$el);

}

DUMMY.PHP

<?php
    header("Location: index.php");
?>

EDIT: La réponse de mkurz semble prometteuse. Peut-être que les bugs sont corrigés.

8
EleventyOne

À partir de Chrome 46 vous n'avez plus besoin de iframe hacks!

Tous les problèmes Chrome ont été corrigés: 12

Assurez-vous simplement que le formulaire de connexion d'origine "n'existe plus" plus après un état Push ou une demande ajax en supprimant (ou en masquant) le formulaire ou en modifiant son URL d'action (n'a pas été testé mais devrait également fonctionner). Assurez-vous également que tous les autres formulaires de la même page pointent vers une URL d'action différente, sinon ils sont également considérés comme un formulaire de connexion.

Découvrez cet exemple:

<!doctype html>
<title>dynamic</title>
<button onclick="addGeneratedForms()">addGeneratedForms</button>
<script>
function addGeneratedForms(){
  var div = document.createElement('div');
  div.innerHTML = '<form class="login" method="post" action="login">\
    <input type="text" name="username">\
    <input type="password" name="password">\
    <button type="submit">login</button> stay on this page but update the url with pushState\
  </form>';
  document.body.appendChild(div);
}
document.body.addEventListener('submit', function ajax(e){
  e.preventDefault();
  setTimeout(function(){
      e.target.parentNode.removeChild(e.target); // Or alternatively just hide the form: e.target.style.display = 'none';

      history.replaceState({success:true}, 'title', "/success.html");

      // This is working too!!! (uncomment the history.xxxState(..) line above) (it works when the http response is a redirect or a 200 status)
      //var request = new XMLHttpRequest();
      //request.open('POST', '/success.html', true); // use a real url you have instead of '/success.html'
      //request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
      //request.send();
  }, 1);
}, false);
</script>

Si vous êtes intéressé, vous trouverez d'autres exemples dans ce dépôt . Vous pouvez l'exécuter avec le noeud: node server.js. Voir également les commentaires de ce commit: https://github.com/mkurz/ajax-login/commit/c0d9503c1d2a6a3a052f337b8cad2259033b1a58

Si vous avez besoin d'aide, faites le moi savoir.

23
mkurz

Vous pouvez obtenir Chrome (v39) pour afficher l'invite de sauvegarde du mot de passe sans rechargement. Envoyez simplement le formulaire à un iframe dont le src est une page vierge qui ne pas possède un Content-Type: text/html en-tête (en omettant l'en-tête ou en utilisant text/plain les deux semblent fonctionner).

Je ne sais pas s'il s'agit d'un bogue ou d'un comportement souhaité. Si ancien, veuillez garder le silence :-)

1
ejain

Nous pouvons maintenant utiliser API expérimentale pour cela: API de gestion des informations d'identification .

exemple officiel

Ma autre réponse avec extrait de code

0
shameleo

J'utilise Chrome 32.0. Les deux méthodes fonctionnent bien ici, je ne comprends pas pourquoi c'est différent de votre côté ...

Cela dit, j'ai souvent dû utiliser un setTimeout () pendant quelques centaines de millisecondes pour faire par exemple focus () fonctionne comme prévu. Cela ferait probablement l'affaire pour vous aussi. Dans ton cas:

setTimeout(display_login_form(),500)
0
aanders77