Byron PeeblesBlog

Help password managers work with your two-step login system

There are different reasons to do a two-page login:

  • Realm discovery

  • SSO (in general)

  • "Security" (like your bank showing you "your" picture after you enter your username)

I recently had to convert a login page from the classic email/password/submit page to a two-step process because we started having users for whom we didn't store a password and they needed to be authenticated/redirected to their identity provider. My initial version worked. But only in incognito/privacy mode ignoring the real-world usage of password managers. Chrome, Firefox, and LastPass all had different issues in auto-populating the password field on the second page (or sometimes the username on the first field).

Note
For the this article, please read "username" as "username and/or email", depending on your application.

I tried some of the suggestions I found on the Internet:

  • Making a hidden username form input on the password page (there already was a static element showing the provided username), similar to:

<input type="hidden" size="50" value="{{ username }}"
       name="username" id="username" autocomplete="username">
  • Add the correct autocomplete value for password, as well:

<input type="password" size="50" value=""
       name="password" id="password" autocomplete="current-password"/>

But it just didn't work reliably. The above was enough, sometimes, for some of three mentioned above.

I read Password Form Styles that Chromium Understands, which kind of helped but it failed to work everywhere. Sign-in form best practices says "oh just do it in Javascript" and then links back to that Chromium page.

Another page current post in the mists of my past, suggested having a hidden password field on the first page, where you just ask for username. So I tried this:

<style> .hidden { display: none; } </style>
<input class="hidden" name="hiddenPwd" type="password" />

and various combinations of type, class with CSS that hides it, type="hidden", and so on. But still not everything worked.

So, I kept looking and found, oddly enough, the Auth0 login page gave me what I needed, where the final version is this password input on their initial page:

<input class="cfd3a9ac2" name="hiddenPwd" type="password" autoComplete="off"
       tabindex="-1" aria-hidden="true" />

the "magic", as it were, ended up being:

  1. Hiding via CSS.

  2. Its own HTML name.

  3. type="password"

  4. And, amazingly, autocomplete="off"! That off is what what lets Firefox, Chrome, and LastPass to act like they should.

Example minimal HTML forms one can use as a good starting point.

first-template.html
<form action="/login/" method="post">
  <input type="text" inputmode="email" size="50" required autofocus
         name="username" id="username" autocomplete="username">
  <input class="hidden" name="hiddenPwd" type="password" autoComplete="off"
         tabindex="-1" aria-hidden="true" />
  <input type="submit" value="Next" />
</form>
second-second-template.html
<form action="/login/" method="post">
  {{ username }}
  <input type="hidden" size="50" value="{{ username }}" name="username"
         id="username" autocomplete="username">
  <input type="password" size="50" value="" name="password" id="password"
         autocomplete="current-password"/>
  <input type="submit" value="Log In" />
</form>