LoginForm deprecated - how to auto-fill passwords?

Now that

LoginForm

is deprecated in Vaadin 7, what is the recommended way to present a login form that browsers will recognize and auto-fill with saved username and password?

Specifically I’m using the LastPass FireFox plug-in. It doessn’t recognize a
TextField
and
PasswordField
in a
FormLayout
.

Nowadays, all browsers except Firefox have unfortunately closed the loophole that LoginForm depended on to make the browser store and autofill the password. We are not aware of any workarounds that could work together with Vaadin’s AJAX nature.

The most reliable way is to use a Servlet or RequestHandler that serves static HTML containing a that is POSTed and processed before passing requests through to Vaadin’s normal bootstrap handler. We have planned to write a tutorial about how to do this, but it has not yet been done.

Hi Leif,

is this tutorial alread available? I switched to VAADIN 7 and removed LoginForm.

Now it is really annoying to repeate Login credentials again and again.

Can you please increase priority?

Best regards
Steffen

Hi Steffen,

you could use Cookies to remember the login name and password until the tutorial is available.

Julian

Hi Julian,

remember user / pwd in cookie is not really comparable to auto-fill passwords like the browser does.

I think this is a must feature for every modern web-application…

@Leif: Can you please tell us when tutorial will be finished?

Best regards
Steffen

this is a static html example

http://demo.vaadin.com/book-examples/book/#component.loginform.basic

You can still continue using LoginForm with Vaadin 7 if you want to even though it is deprecated. It works in the same way as in Vaadin 6.x which means that Firefox saves username and password, most other browsers save the username and some save nothing at all.

I have no news regarding the tutorial. There are still lots of bugs that have higher priority than writing a tutorial for something that is essentially just a small usability inconvenience.

I don’t understand why LoginForm is deprecated. The “loophole” you are talking about is still there, it’s just that most browsers are a bit more strict nowadays. The most obvious restriction being that the form containing the username/password cannot be submitted using javascript - something that can easily be fixed in LoginForm.

I did a little research and quite a bit of trial-and-error, and eventually came up with something that runs on most browsers (if not all):

public class App extends Application {
    @Override
    public void init() {
		LoginForm loginForm = new LoginForm() {
			@Override
			public byte[] getLoginHTML() {
				String html = new String(super.getLoginHTML());

				// most browsers: do not use javascript to submit the form
				html = html.replace(
						"<form id='loginf' target='logintarget' onkeypress=\"submitOnEnter(event)\"",
						"<form id='loginf' target='logintarget'");

				// chrome: the form.action attribute cannot be set using javascript - make it static
				String winPath = getApplication().getURL().toString() + getWindow().getName();
				html = html.replace(
						"<form id='loginf'",
						"<form id='loginf' action='" + winPath + "/loginHandler'");

				// chrome: the iframe.src attribute must not be blank
				html = html.replace(
						"<iframe name='logintarget'",
						"<iframe name='logintarget' src='#'");

				// most browsers: use a "real" <input type=submit> element
				int buttonStartIdx = html.indexOf("<div><div onclick=");
				int buttonEndIdx   = html.lastIndexOf("</form>") - 1;
				html = html.replace(
						html.substring(buttonStartIdx, buttonEndIdx),
						"<input type='submit' value='" + getLoginButtonCaption() + "'");

				return html.getBytes();
			}
		};

        Window mainWindow = new Window();
        mainWindow.setContent(loginForm);
		setMainWindow(mainWindow);
    }
}

I tested this against the most recent versions of the browsers that I know:

[list]

[]
ie 9.0
[
]
ff 17.0
[]
chrome 23.0
[
]
opera 12.11
[*]
safari 5.1.7

[/list]They all prompt to save the password - and store it. So with a few adjustments the LoginForm component works.
Why deprecate it?

sohan

Vaadin 6.7.6

As a security professional, I would recommend not remembering passwords on the form itself. It is generally a bad security practice and any static analysis review of your source code should return a finding. The reason for this is that the passwords are generally stored somewhere on disk in a non-secure way making them susceptible to being found by an attacker. This exposes unnecessary risk to your end user.

What do you mean by “remembering passwords on the form itself”? The form’s HTML does not contain any passwords. Instead, the HTML is structured in such a way that browser-side tools that auto-fill passwords are capable of recognizing it as a login form.

In any case, while your comment may be good advice, etc., you are talking about a separate issue that’s not relevant to this discussion.

  1. Not all password auto-fill solutions are stupidly insecure.
  2. If someone wants to use a tool to auto-fill passwords, who are you or me to tell them that they can’t?
  3. This discussion is simply about making it possible for people to do something, not forcing them to.

I believe it is quite relevant to the topic. As a security professional, I would not be doing what I am ethically responsible for if I did not point out the risks involved with a potential solution. The discussion is around implementing something which, as you correctly pointed out, could be implemented in a very non-secure method. Implementing a secure solution to this problem is not an easy thing to solve and a lot of smart people do not do it securely because there are things that can be missed. I am simply bringing awareness to that risk and how the security industry views auto-complete and auto-fill solutions with regards to passwords. You can choose to do with that information as you wish.

Thank you for your insights!

We did test some alternatives similar to what you did before we decided to deprecate the component, but we did not find the solution that you have found. We were aware that the loophole was still present, but our assumption was that it didn’t work any more if the form was submitted in an iframe.

I have updated
ticket #8171
accordingly and it might well lead to us removing the deprecation before Vaadin 7.0.0 is released.

As someone with insights into both security and usability, I would recommend the application developer and the end user to choose whether to save passwords or not based on various tradeoffs.

Most applications that involve sensitive data, e.g. internet banking sites, have chosen to tell the browser to not save the password, whereas most other applications lean towards the usability (or ignorance) of allowing the passwords to be saved.

An end user that is aware of the security tradeoffs can choose whether to save the password based on how highly he values the security of that particular password combined with e.g. reliance on the used browser’s capability of storing the password in a secure way (e.g. the master password in Firefox or browsers storing the passwords in the secure OS X keychain that is protected by the user’s login password). End users that are not aware of security implications might still choose to store the password in a text file somewhere if the application doesn’t offer to save the password…

Ok, just wanted to post here my idea for this issue. Could I create my custom client/server component place there (in client widget constructor all fields that I needed in form)? Or browser will not understand that this is the form that it need to full fill, cause approach with LoginForm.class looking as a hack.

P.S. would be great if we continue discussion about this issue…

Just wanted to bump this again, has anyone tried the method Leif hinted at?

If I get a chance I will have a go.

Regarding the ethics of the topic, I think how people store the credentials on their own PC is out of the developers control, we provide a platform that conforms as much as possible to modern web standards, which involves accepting passwords if they are provided by the browser. My concern is however that it is the assumption of the user that this information is only submitted when they click login? It’s all semantics I suppose as if it can be done then it will be done but is it an issue with Vaadin if it can be done? Wouldn’t this allow you to harvest the users address and even card details and upload them to the server without their knowledge in theory? If this is the case people should there be a tutorial at all? I think not.

While we all continue to wait for a tutorial on how to not use LoginForm, I’ve taken Sohan’s code and made a version that runs in Vaadin 7.1.

    LoginForm form = new LoginForm() {
      @Override protected String getLoginHTML()
      {
        URI uri = getUI().getPage().getLocation();
        String path = uri.getPath() + '/'
                + ApplicationConstants.APP_PATH + '/'
                + ConnectorResource.CONNECTOR_PATH + '/'
                + getUI().getUIId() + '/' + getConnectorId() + "/login";
        try
        {
// Recreate URI with new path and no query or fragment.
          uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), path, null, null);
        }
        catch (URISyntaxException ex)
        {}
        return "<!DOCTYPE html>\n"
                + "<html>"
                + "<head><script type='text/javascript'>"
                + "var setTarget = function() {"
                + "document.forms[0]
.username.focus();};"
                + ""
                + "var styles = window.parent.document.styleSheets;"
                + "for(var j = 0; j < styles.length; j++) {\n"
                + "if(styles[j]
.href) {"
                + "var stylesheet = document.createElement('link');\n"
                + "stylesheet.setAttribute('rel', 'stylesheet');\n"
                + "stylesheet.setAttribute('type', 'text/css');\n"
                + "stylesheet.setAttribute('href', styles[j]
.href);\n"
                + "document.getElementsByTagName('head')[0]
.appendChild(stylesheet);\n"
                + "}"
                + "}\n"
                + "</script>"
                + "</head><body onload='setTarget();' style='margin:0;padding:0; background:transparent;' class=\""
                + ApplicationConstants.GENERATED_BODY_CLASSNAME
                + "\">"
                + "<div class='v-app v-app-loginpage "
                + getUI().getTheme()
                + "' style='background:transparent;'>"
                + "<iframe name='logintarget' src='#' style='width:0;height:0;"
                + "border:0;margin:0;padding:0;display:block'></iframe>"
                + "<form id='loginf' target='logintarget' method='post' action='" + uri.toASCIIString() + "'>"
                + "<div>Login Name:</div>"
                + "<div><input class='v-textfield v-widget' style='display:block;' type='text' name='username'></div>"
                + "<div>Password:</div>"
                + "<div><input class='v-textfield v-widget' style='display:block;' type='password' name='password'></div>"
                + "<div><input type='submit' value='Submit' class='v-button-wrap v-button-caption'></form></div>" + "</body></html>";
      }
    };

The uri was the tricky part. Note that with so much HTML changing, it’s easier just to replace it all. Also, there didn’t seem to be any point in calling the set*Caption() functions only to reference them here, so my captions are just in the HTML; change it as you with for your application.

I tested it in Firefox 24 and Chrome 29.

I have developed a login form add-on that works with all major browsers:


https://vaadin.com/directory#addon/loginform

Hi Leif,

It’s been 4 years now. Is the tutorial available finally? The one to which Thomas Hoenen is referring is for the deprecated LoginForm. It’s very annoying not being able to remember credentials / autofill forms, and I don’t want to use any hackish method as the iframe one.

Unconsistent info about how to supposedly accomplish this is unconsistent accross Vaadin sources. For instance:


https://vaadin.com/api/com/vaadin/ui/LoginForm.html

“Login form with auto-completion and auto-fill for all major browsers”… blah… “You can derive from this class and implement the createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button) method”… blah blah… “Note that the API of LoginForm changed significantly in Vaadin 7.7”

No such “createContent()” method exist in Vaadin 7.7.

Also, here:


https://vaadin.com/api/7.6.6/com/vaadin/ui/LoginForm.html

“This component no longer fulfills its duty reliably in the supported browsers and a VerticalLayout with two TextFields can be used instead.”

No details are given on how to accomplish this purpose with a VerticalLayout and two TextFields, but that’s what my login form was using for sure and in any case browser is not offering to remember passwords, no matter which one (Chrome, Firefox, IE…) I try.

Also that class has 4 .addListener() methods, two of them are deprecated, and and extra .addLoginListener()… which of them is the proper one to implement a regular login?? If I try to implement all the abstract classes of both methods there’s only a loginSuccessful(), which vastly differs from the code posted by Thomas Hoenen.

Vaadin 6 docs are of no help to, as what’s stated here doesn’t seem to apply anymore:

https://vaadin.com/book/vaadin6/-/page/components.loginform.html

That seems to go against what Leif Åstrand stated here:

“You can still continue using LoginForm with Vaadin 7 if you want to even though it is deprecated. It works in the same way as in Vaadin 6.x”

More unconsistences:

https://vaadin.com/directory#!addon/loginform

I just discovered that they un-deprecated LoginForm using this extension. However, when trying to find the docs, they refer to methods tht don’t exist:

“Release notes - Feb 12, 20160.6.2:
Calling
setInputPrompt
on the password field breaks the functionality of the login form, so disable it from having any effect. The input prompt is not readable, so it does not make sense to use this feature in any case.”

The setInputPrompt() method don’t seem to exist. Also, when trying to follow instructions from
the plugin GitHub page
, DefaultVerticalLoginForm and other classe don’t exist.