Thursday, August 25, 2011

GWT and Username/Password autocomplete

Userbase Demanded Login-Autocomplete!

For one of my work projects, I had to pass-through login from my server code to another service.  No problem.  My initial design just used a GWT RPC post to send the username and login credentials to my server, which sent the login request to the other server.  If that succeeded, then I would assign a variable to the session and return success.

However,  the input boxes for username and password did not have the desired autocomplete feature.


After lots of messing about, and some Googling, I came up with a pretty easy strategy.

Part 1: Getting the browser to save the password

1. Convert the login to a form, and have the form submit a POST to a login servlet.  The inputs must be named 'username' and 'password', and the password input must be of type 'password' as well.

As an aside, I was disabling the inputs and the login button before submitting the post, so the user can't keep clicking it.  This caused the post to be empty!  In order for the post to actually send the username and password, I had to disable the inputs after submitting the post.


Essentially:
public class LoginForm extends VerticalPanel
{
    final TextBox username;
    final PasswordTextBox password;
    final private FormPanel formPanel = new FormPanel();
    final SubmitButton button = new SubmitButton("Log in");

    public LoginForm()
    {
        add(formPanel);
        formPanel.setAction("servletpath/login");
        formPanel.setMethod(FormPanel.METHOD_POST);

        VerticalPanel vp = new VerticalPanel();
        formPanel.setWidget(vp);

        username = new TextBox();
        username.setName("username");
        vp.add(username);

        password = new PasswordTextBox();
        password.setName("password");
        vp.add(password); 
        vp.add(button);

        formPanel.addSubmitCompleteHandler(
            new SubmitCompleteHandler(){...});
    }
}
Okay, that works for me: After clicking the Login button, Firefox asks if I want to save the password.
Also works for IE and Safari.  Chrome *sometimes* works.  I can't believe IE works better than Chrome for this.

Part 2: Getting the Presets Back into the Login Form
So, we've got the values saved.  But it doesn't work!
The browser does not apply the password database to forms that are added by JavaScript.

I read about one technique, which was to add a hidden to the root HTML page, and then grab the pre-filled values from those inputs.
However, this does not help in the case where you have more than one username/password!
In my case, this is a real concern, as some users need to log in as the super admin from time to time.

Here's my variation of the technique:
Add a hidden form to the root HTML page, and then wrap the inputs.
Works like a charm!

Here is my root HTML page:

<html>
  <head>
    ... usual GWT stuff ...
  </head>
  <body>
    <div id="title">
      etc...
    </div>

    <div style="display:none;">
      <form id="login_form" method="post" action="ccsadmin/login">
        <input id="creds_username" name="username"/>
        <input id="creds_password" name="password" type="password"/>
      </form>
    </div>
  </body>
</html>

Then, in my LoginForm class:

public LoginForm(String message)
{
    formPanel = new FormPanel();
    add(formPanel);
    formPanel.setAction("loginservlet/login");
    formPanel.setMethod(FormPanel.METHOD_POST);

    VerticalPanel vp = new VerticalPanel();
    formPanel.setWidget(vp);

    username = TextBox.wrap(DOM.getElementById("creds_username"));
    vp.add(username);

    password = PasswordTextBox.wrap(DOM.getElementById("creds_password"));
    vp.add(password);
    vp.add(button);
    formPanel.addSubmitCompleteHandler(new SubmitCompleteHandler(){...});
}

There you have it!
Clicking in the username box will show the list of saved credentials for my login page.

Note:
I just searched and found another example that just wraps the entire hidden form.  However, I like my version a bit better, as my app is internationalized, and so I am using the GWT i18n code to add labels and text programmatically.

Update 1:

Previously, I was using a normal Button to submit the form.
To try and get Chrome working, I converted it to a SubmitButton.
This does seem to work sometimes, not sure what's wrong.

Update 2:

I have zipped up a complete example of a GWT application.  You can download it here:
autologin.zip
To download it, click the link, then select File/Download.
To use it:

  • Start Eclipse
  • Then, select 'File/Import...'
  • Select 'Existing projects into Worksace' and click Next...
  • Choose 'Select archive file' and choose the downloaded file
  • Click 'Finish'
Note that you must have GEP installed, and with GWT 2.4+.



4 comments:

mars girl said...

hi, great post, just wondering where did you place the login button? could you upload the sample code? thanks.

harun said...

Hi man,

this solution doesn't work chroime and internet explorer, only firefox work.

what can we do? can you suggest me any suggestion?

Jamie S. said...

mars girl,
I have updated the code to show where the button is added.

harun,
Hmmm, it was working in Chrome; now it doesn't. I will have to look into that.

Jamie S. said...

Harun, I can only get it to work sometimes in Chrome. Sorry man!