If you read a few of my other blog posts like Naomi85 – password generator, it might surprise you to know I don’t like passwords, And that I find passwords to be horrible both from a usability and security point of view.
Managing passwords is hard. If you try to do it right, you must keep unique and complex passwords for each service you use. More than once, I forgot a decryption password and spent hours even days trying to recall it. Password managers will help, but using them securely is not that simple.
I might be paranoid but I have three standards for passwords –
- Trivial – for forums and other websites I don’t care about much so I use a password vault I can access from my phone
- Important – for servers, bank accounts and other services I care about so I use a password vault which I only access from my laptop
- Critical – for my e-mail the actual password vaults private keys and other encrypted backups which I remember and don’t store digitally at all. Except for e-mails, I only use them on offline devices
So though I got this system kinda working for me (and I haven’t lost a password in a while) it is still hard to manage.
Besides being hard to manage, passwords are easy to compromise. One login from a compromised device or network is enough for an attacker to get permanent access to your account. That’s why many services allow you to use a 2nd-factor auth to compliment the password.
I won’t go into all the major problems and solutions related to passwords. There are enough articles online about these issues. Instead, I will talk about how password inputs are hard to use and how we can fix them. A much smaller issue, but it can become quite irritating.
Typing long and complex enough passwords into password fields can be a daunting task. You can’t see the password, and when you get it wrong, it is hard to know the reason. Did I just have a typo? Did I forget the password? Is CapsLock on? Maybe I copied the URL instead of the password from my vault? Maybe I’m trying to login to some else account? Who knows?
In an ideal world, you should be able to just copy past from a password manager, but in reality, it doesn’t always work. Your password manager requires a password. Some banks, For some counterproductive security policy, won’t allow you to past the password in. As I mentioned earlier, some passwords are too important to keep in a password manager.
I always hated not being able to see my password as I type. recently I used a screen reader to check our UI at work for accessibility issues. Web accessibility deserves a full academic course to get a grasp on so I won’t be talking much about it (you can check this list of free and paid accessibility courses as a place to get started). One thing I noticed is that screen readers won’t read out the characters as you type in password fields. From a security perspective, it makes as much sense as hiding the characters behind * signs. If you are working in a cafe where someone can look behind your shoulder and see your password that probably makes sense, but if you’re alone at your office, it is mostly an inconvenience. The same rule applies to screen readers if you have privacy or even if you just use headphones not hearing your password as you type it is just an inconvenience.
So the first thing I did was searching online hoping there is just a keyboard shortcut that will read the password. Not only that I couldn’t find any, but I found at least one feature request for chrome vox was rejected as it is too big of security risk… I also found an interesting article by James Edwards trying to implement a hear password feature and failing. In this article, I’ll show how to implement an accessible password input.
The core features of this input will be –
- All native input features
- Option to see the password.
- The password will only be visible when it is being entered/interacted.
- Screen readers will read the password if it is visible
- Option to hear the password.
- The password will only be audible when being entered/interacted.
- The password will not be visible on the screen
- Not blocking copy paste feature to allow using password managers (that should be obvious)
As always, if you only care about the code you can check the password-input GitHub repository.
The first rule of adding custom inputs and accessibility is to always use native HTML elements. We will start with a basic password input –
1 2 3 |
<form> <label>Password <input type="password"></label> </form> |
And we can see the result –
This is just a native password input so we have all the native features of password inputs.
We still have all the problems discussed, mainly we have no option to see or hear the password.
The thing is we have a native password element that will show us the password or read it as it is typed –
1 2 3 |
<form> <label>Password <input type="text"></label> </form> |
And we can see the result –
Much better! Now you might notice that this is not a password field but a regular text field but from a usability perspective that is exactly what we want. We can see the password, and screen readers will read characters as they are being typed. But we lost all the security features of using the password field.
It is clear we need to get the input field to behave like a password field by default but allow to convert it to a text field as it is being interacted.
If you think the solution is simple, you are right. You can take a short break and think how you would implement it yourself before reading on (If you get a different solution than mine share it in the comments or the GitHub repo)
Ok let’s implement a password input with a small twist. We’ll add a button next to it, that will allow us to see the password. The button will cause the input to change to a regular text input but only as long as it has focus –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<form> <span id="desc" class="sr-only">usability controllers available after input</span> <label>Password <input type="password" aria-describedby="desc"></label> <Button title="password will be visual on screen" id="seeButton">See Password</button> </form> <script> const $input = document.querySelector('input[type="password"]'); const $seeButton = document.getElementById('seeButton'); $seeButton.addEventListener('click', () => { $input.setAttribute('type', 'text'); $input.focus(); }); $input.addEventListener('blur', () => { $input.setAttribute('type', 'password'); }); </script> |
And the result –
I added an aria description to make sure that screen reader users will get info about the controllers.
For a seeing user, this works great. I can click the input and see the password. When I leave the input field it changes back and hides the password. A blind user will hear the password but the fact that it is also visible is going to be a security concern, so we’ll add another button where we hide the text entered in the input field –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<form> <span id="desc" class="sr-only">usability controllers available after input</span> <label>Password <input type="password" aria-describedby="desc"></label> <Button class="sr-only-focusable" title="Hear Password as you type, password will not be visible on the screen" id="hearButton">Hear Password</button> <Button title="password will be visual on screen" id="seeButton">See Password</button> </form> <script> const $input = document.querySelector('input[type="password"]'); const $seeButton = document.getElementById('seeButton'); const $hearButton = document.getElementById('hearButton'); $seeButton.addEventListener('click', () => { $input.setAttribute('type', 'text'); $input.focus(); }); $hearButton.addEventListener('click', () => { $input.style.textIndent = '-100000px'; $input.setAttribute('type', 'text'); $input.focus(); }); $input.addEventListener('blur', () => { $input.setAttribute('type', 'password'); $input.style.textIndent = null; }); </script> |
And the result –
The first thing to notice is that the “Hear Password” is not visible. Hearing functionality is only available for users using screen readers. We don’t want to confuse other users by showing it. The “sr-only-focuseable” class means the button will be visible when it’s focused so any user tabbing through the interface will see it once focused even when not using a screen reader. To test the button, click on the input and click tab once to focus the button.
Pushing the button will take you back to the password input, but now the password will be invisible and screen readers will read the password in the field.
This is mostly it. On the password-input GitHub repository, you will find a slightly more complex implementation using icons and web components.