ChatGPT解决这个技术问题 Extra ChatGPT

How does browser know when to prompt user to save password?

This is related to the question I asked here: How can I get browser to prompt to save password?

This is the problem: I CAN'T get my browser to prompt me to save the password for the site I'm developing. (I'm talking about the bar that appears sometimes when you submit a form on Firefox, that says "Remember the password for yoursite.com? Yes / Not now / Never")

This is super frustrating because this feature of Firefox (and most other modern browsers, which I hope work in a similar fashion) seems to be a mystery. It's like a magic trick the browser does, where it looks at your code, or what you submit, or something, and if it "looks" like a login form with a username (or email address) field and a password field, it offers to save.

Except in this case, where it's not offering my users that option after they use my login form, and it's making me nuts. :-)

(I checked my Firefox settings-- I have NOT told the browser "never" for this site. It should be prompting.)

My question

What are the heuristics that Firefox uses to know when it should prompt the user to save? This shouldn't be too difficult to answer, since it's right there in the Mozilla source (I don't know where to look or else I'd try to dig it out myself). I've also had no luck finding a blog post or some other similar developer note from the Mozilla developers about this.

(I would be fine with this question being answered for Safari or IE; I would imagine that all the browsers user very similar rules, so if I can get it working in one of them, it will work in the others.)

(* Note that if your answer to me has anything to do with cookies, encryption or anything else that is about how I'm storing passwords in my local database, odds are strong that you have misunderstood my question. :-)

I dunno. Is your form a POST form with a password-type field?
Yep, wrapped in
tags, and the fields are named 'username' and 'password'. I load it as a separate layer with AJAX, but so does disqus.com (just to throw an example out there) and it works great for them. That's why, rather than (continue to) randomly tweak things to see if it somehow helps, I want to find out exactly how the browser is thinking.

A
Ariel

Based off what I have read, I think Firefox detects passwords by form.elements[n].type == "password" (iterating through all form elements) and then detects the username field by searching backwards through form elements for the text field immediately before the password field (more info here). You might try something similar in Javascript and see if you can detect your password field.

From what I can tell, your login form needs to be part of a <form> or Firefox won't detect it. Setting id="password" on your password field probably couldn't hurt either.

If this is still giving you a lot of problems, I would recommend asking on one of the Mozilla project's developer mailing lists (you might even get a response from the developer who designed the feature).


That's marvelous. Thanks. That's what I'm looking for.
This helped me get it to work in Firefox but I'm not having any luck with Chrome. This is the form I'm using:
@1.21gigawatts- There's not a "standard" way to do this (to my knowledge), so different browsers may do it slightly differently. I don't know how Chrome's process differs from Firefox's, but you may be able to figure it out if you look through the Chrome source code. The only way I knew how Firefox did it was by reading their code. This is the sort of thing that someone needs to document in a big chart, listing how each browser does it...
An author of the password manager code walks you through things in a 2013 blog post -- it's a worthwhile read: blog.mozilla.org/dolske/2013/08/20/on-firefoxs-password-manager
Updated link to the password manager blog posts: dolske.wordpress.com/2013/08
u
user324356

I had the same problem and found a solution:

to make the browser ask to store the password, user name and password boxes must be in a form and that form must be actually submitted. The submit button could return false from the onclick handler (so the submit does not actually happen). to make the browser restore the previously stored password, the input boxes have to exist in the main HTML form and not be created through javascript dynamically. The form can be created with display:none.

It's necessary to note, that the password is filled immediately upon the page is loaded and is present there during the whole session, so it can be read by injected javascript: it makes such attacks much worse. To avoid this, forwarding to a separate page just to log in is reasonable, and it solves all problems for which you started to read this topic :). As a partial solution I clear the fields upon submitting the form - if the user logs out and wants to log in again, the password is not filled by the browser, but that's minor to me.

Viliam


Works in Firefox 3.5 and IE8, does not work in Chrome (point 1 not working). Maybe the submit has to be real...
user324356 - I set the form to "#" then called myForm.submit() and that worked in Firefox but not Chrome is not prompting.
fyi: When you use return false or event.preventDefault() this will not work in Chrome because of this bug: code.google.com/p/chromium/issues/detail?id=282488
This is definitely important with the current algorithm used by firefox -- it's not the only thing the password manager "looks for" but the firefox password manager will not be triggered w/o an actual submit.
Update: Chrome 46 fixed its wrong behaviour - everything should work fine now. See stackoverflow.com/a/33113374/810109
j
jpaugh

You should look at the Mozilla Password Manager Debugging page and the nsILoginManager docs for extension writers (just for the nitty gritty technical details of how Firefox deals with password management). You can dig into the answers there and other pages linked there to find out more than you probably ever wanted to know how the password manager interacts with sites and extensions.

(Specifically as pointed out in the password manager debugging doc, make sure you don't have autocomplete set to off in your html, as that will suppress the prompt to save the username and password)


1
1.21 gigawatts

This seems to work for Firefox, Chrome and Safari on Mac. Not tested on Windows.

<form id="bridgeForm" action="#" target="loginframe" autocomplete="on">
    <input type="text" name="username" id="username" />
    <input type="password" name="password" id="password"/>
</form>

<iframe id="loginframe" name="loginframe" src="anyblankpage.html"></iframe>

This needs to be added to the page. It can't be added dynamically. The form and iframe can be set to display:none. If you don't set the src of the iframe the prompt won't show until you've submitted the form at least once.

Then call form submit():

bridgeForm.submit();

The action may be optional and the autocomplete may be optional. Haven't tested.

Note: On some browsers, the form has to be running on a server (not localhost and not the file system) before the browser will respond.

So this:

http://www.mysite.com/myPage.html

not this:

http://126.0.0.1/myPage.html
http://localhost/myPage.html
file://directory/myPage.html


@ErikAronesty have you come up with a solution? I noticed that in some browsers (safari) the page has to be on a server and not local host or the file system.
Chrome 46 fixed its wrong behaviour - no iframe workarounds needed anymore. See stackoverflow.com/a/33113374/810109
j
jpaugh

Works for me with angular, chrome, firefox: (I've searched and tested for hours - for chrome the form action parameter (#) was the answer. @1.21 gigawatts, thank you!!! Your answer was invaluable.)

form

firefox 30.0 - doesn't need a hidden iframe and submit button (as shown below), but needs the "login-form-autofill-fix" directive for recognizing the autofilled credendials, as follows:

<form name="loginForm" login-form-autofill-fix action="#" target="emptyPageForLogin" method="post" ng-submit="login({loginName:grpEmail,password:grpPassword})">
<input type="text" name=username" id="username" ng-model="grpEmail"/>
<input type="password" name="password" id="password" ng-model="grpPassword"/>
<button type="submit">Login</button>
</form>

hidden iframe

chrome 35.0 - doesn't need the above directive, but needs a hidden iframe and a submit button on the real form. The hidden iframe looks like

<iframe src="emptyPageForLogin.html" id="emptyPageForLogin" name="emptyPageForLogin" style="display:none"></iframe>

angular directive (using jqLite)

This works with angular 1.2.18

module.directive('loginFormAutofillFix', function() { 
            return function(scope, elem, attrs) {
        if(!attrs.ngSubmit) {
            return;
        }
        setTimeout(function() {
            elem.unbind("submit").bind("submit", function(e) {
                //DO NOT PREVENT!  e.preventDefault(); 
                elem.find("input").triggerHandler("input");
                scope.$apply(attrs.ngSubmit);
            });
        }, 0);
});

amendment

after some testing, I realised, that chrome needs a little timeout by the angular login method (200ms) - it seems, the redirect is sometimes just too fast for the password manager.

better clear browsercache... with every change


since newest chrome the pw-manager fills in the form only sometimes. also the pw manager sometimes pops up, sometimes not... seems to be a randomized function now.
the new firefox ignores the auto-filled password field. the triggerHandler("input") isn't working. this can be solved with a native event (document.createEvent) - dispatch it an fire.
Chrome 46 fixed its wrong behaviour - no iframe workarounds needed anymore. See stackoverflow.com/a/33113374/810109
@110maor It took me a long time to understand your post. I hope my edits are true to your intention.
s
spender

Well, on our site, a form field with name "username" type "text" immediately followed by a field with name "password" and type "password" seems to do the trick.


Too quick for me! My thoughts exactly... (I'm deleting my answer)
Yes. Tried it. No luck. :-( My theory is that I have something else on the page that the browser doesn't like/makes it think the page doesn't have a login form...
A
Adrien Joly

If you're using AJAX login, take a look at that source code: https://gist.github.com/968927

It consists of submitting a login form to a hidden iframe, so that IE and Chrome can detect the actual login, without having to reload the page.


Chrome 46 fixed its wrong behaviour - no iframe workarounds needed anymore. See stackoverflow.com/a/33113374/810109
j
jonsca

The heuristics are pretty simple here: detect fields with certain names in certain order. Which ones, I can't tell, but this worked fine for me in Chrome and IE:

Username field: name "login", type "text";
Password field: name "password", type "password"
Submit button: element "input", type "submit". 
Element "button" type "submit" did not work.

I had to replace <button type="submit"> by <input type="submit"> to get Dashlane to work. Can't believe this is even a thing though...
Z
ZeWaren

I also noticed that Chrome will not offer to remember a password if the login form is still present after the login request, even if it's hidden on the page.

I guess it thinks the login action failed and thus refuse to store invalid credentials.

Seen on Chrome 34.0.


Chrome 46 fixed its wrong behaviour, hiding the form after an ajax request should be enough. See stackoverflow.com/a/33113374/810109
j
jpaugh

I'd recommend looking at the Firefox source code. It's actually pretty straightforward code.

The methods you want to look at are _onFormSubmit, _getFormFields and _getPasswordFields.

You might even find the problem you're having is infact an undiscovered bug in Firefox ;) https://bugzilla.mozilla.org/show_bug.cgi?id=1211780


h
hendrikbeck

In addition to a lot that has been said already I realized that the input fields must not be "disabled". We had a multi-step login that first asks for the username and then, on the next screen, for the password. On that second screen we repeated the email but disabled it and that prevented Chrome et. al. from recognizing as a valid field for the username.

Since we really wanted to keep that disabled input field we ended up with this hacky workaround:

<input type="text" name="display-username" id="display-username" value="ENTERED_USERNAME" placeholder="Username" disabled="disabled">
<input type="text" name="username" id="username" value="ENTERED_USERNAME" placeholder="Username" style="display: none;">
<input type="password" name="password" id="password" value="" autofocus="autofocus" autocomplete="on" placeholder="Password" required="required">

I wouldn't go so far to recommend this but maybe it points somebody to something in their own code:

The first element displays the username in a disabled input field. Disabled doesn't submit as part of the form and disabled isn't recognized by the browser. The second element is a proper input field, with type = "text" and name/id = "username". This is being recognized by the browser. In order to prevent the user from editing it we hide it with CSS (display:none).


For situations where you want an input whose value is submitted but which the user cannot change, you might want to try "readonly" instead of "disabled".
k
keivan kashani

If you have for example two type=text in your form before input type=password, Browser detect the nearest input type=text for your username.

This is not matter another input has is=username and name=username

for solving this problem you must put your username input that you want to save exactly before your input password

Good Luck :-)


J
Jeba Moses

The major keyword is here,

   <input type="password">

Try to explain your answer with some more details as it will be helpful for others.