Please Enter Your Password
How many times have you entered a password into a computer, phone or tablet?
Hundreds of times? Thousands? Tens of thousands?
Anyone who’s spent any time using any kind of internet-connected device will have a basic working knowledge of what passwords are, what they do, and why they exist. But as the builders of these products, should we be giving passwords a little bit more attention?
Unsolicited Interruptions
If you use an iPhone, you’ve probably seen this dialog more than once:
And if you use iTunes on a Mac, you’re probably pretty familiar with this one:
Here’s a different dialog, this time from the InVision Sync client for OS X:
What do all of these dialogs have in common? They all interrupted what I was doing, at a time when it made no sense that I should need to enter my password. I had already logged the app or device into the relevant account, I hadn’t changed my password, and in the case of the iTunes Store dialogs, I hadn’t just made a purchase (which would be a legitimate reason to ask for my iTunes Store password).
Let’s explore the InVision prompt, since it is reproducible.
I can make the InVision “Invalid email or password” dialog shown above pop up quite reliably by performing the following steps:
- Turn off your Wi-Fi
- Wait until you see the InVision Sync icon indicate “offline mode”
- Turn on your Wi-Fi
- As soon as the Wi-Fi connects, immediately turn it off again
That’s weird… what does toggling my Wi-Fi on and off have to do with authentication on my InVision account? Well, the answer should be nothing. From the user’s point of view, these two things are completely unrelated. If you’ve ever written any kind of HTTP client code, however, you probably know what’s going on here.
When InVision Sync goes from offline to online mode, it starts a HTTP request to an InVision backend server. By turning the Wi-Fi off soon after, you interrupt the request, causing a network error.
Network Errors
There are few things that stay constant for long in the world of technology, but the unreliability of networks (especially, wireless networks) is certainly one. In the context of a client of some sort (e.g. an app, or a system service) communicating with a web API using HTTP, there are a large number of potential causes for network errors. Here’s a few common ones (although there are many more):
- The client’s internet connection is experiencing significant packet loss (causing a timeout)
- The client is unable to resolve the domain name of the URL that it’s trying to reach
- The client had no internet connection in the first place
- The server is unreachable (it may have crashed)
Note that these are all errors describe scenarios where the client doesn’t actually receive a HTTP response.
HTTP Status Errors vs Network Errors
There’s another type of error that can occur when clients interact with web APIs using HTTP, and this is an unexpected HTTP status code. When requesting a resource using a GET request, any status code other than 200 generally indicates an error of some kind (ignoring redirects).
How does this relate to password prompts? The answer lies in how these different errors are handled. Consider the following code in Swift, which describes a client making a request to a HTTP API for a hypothetical service named “Friend Face”:
func getFriendFaceTimeline(completion: (NSData?, NSError?) -> Void) {
self.session.dataTaskWithRequest(self.timelineGetRequest()) { (data, response, error) in
if (response as? NSHTTPURLResponse)?.statusCode == 200 {
// Success!
completion(data, nil)
} else {
// Error: Any Error!
completion(nil, error)
}
}.resume()
}
The getFriendFaceTimeline()
method is pretty straightforward, and calling code can simply check if the first argument in the completion
block is nil to determine whether or not the request “succeeded”. So far, so good.
What happens if the client has bad credentials, and the server returns a 403 Forbidden HTTP status? Well, if this happens, we’d like our client to prompt the user for their password. Either our getFriendFaceTimeline()
method will need a way to communicate three different “completion scenarios” (success, authentication error and “every other error”), or we will need to squash the three scenarios down into two (success and “any error”) and handle the “any error” scenario in our calling code by throwing a password prompt. If we take the second (much, much easier) approach, we will save ourselves a bunch of time and work, but we will end up with a client that throws a password prompt when any kind of network error of any kind occurs.
There are many valid approaches to dealing with this class of problem, and Swift has a number of nice, new approaches that weren’t possible with good old Objective-C. I’m not going to go into details about Swift enums, Swift error handling or Result types, because other people have have already written interesting discussions about those topics, and the exact approach you choose to adopt is an implementation detail anyway. The important point is this:
Every network success and failure scenario that would ideally result in different feedback to your users should be explicitly designed for and handled in your code.
Product Quality and User Perception
It is generally established in much of the tech industry that improving “quality” by improving the design and implementation of your product is a smart business strategy. Every password prompt you show without a legitimate reason will leave your users slightly less certain about either your product – “ugh, it should already know my password, this product is stupid!” – or less certain about themselves, their knowledge and their competence – “what am I doing wrong, why do I have to keep entering my password?”. No matter what their exact response is, it’s almost certainly going to be negative for either the user, your product and company, or both.
So when should we show password prompts? Some actually valid reasons for showing a password prompt (at least in the context of a network client) are:
- Logging in to an account for the first time
- Receiving an authentication error after the user has changed their password or revoked an authentication token
- Verifying credentials in order to perform a “secure” task (e.g. tasks involving financial transactions, purchases or changing account credentials)
The current culture of disregard for proper network error handling, and the resulting barrage of inappropriate password prompts results in a poor user experience, which in turn affects perception of your products. There’s a strong business argument to be made for taking this seriously.
Not only do these password problems reflect badly on both the product and the people who designed and implemented it – there’s another particularly insidious issue at hand…
The S Word
No, not Swift, structs or singletons. I’m referring to security.
Throwing password prompts left, right and center is really bad, because it trains our users to think that it’s ok to enter their important passwords even when prompted for no good reason.
I’ve had “legitimate” prompts for my iTunes Store and iCloud accounts pop up on my iPhone when I was using 3rd-party apps. Once an app or webpage knows your email address, there’s no way to prevent it from presenting UI that looks exactly the same as one of these prompts. It’s therefore impossible for users to distinguish between legitimate password prompts thrown up by iOS, OS X or iTunes, and phishing password prompts that are presented by malicious apps or webpages.
This is a security time bomb waiting to explode. Don’t be part of the problem by saving yourself a couple of hours of dev time.
We Can Do Better
If you’re a developer, a designer or a product manager, you owe it to your users and your companies to take network errors and password prompts seriously. For most apps and projects, this isn’t a particularly hard problem to solve, but it does require that you care.
Make password prompts and network failure handling an essential part of your product designs. Only show them to your users when you have a really good, valid reason. This is not a place to cut corners.
Don’t just consider it a vaguely noble goal to only ever show password prompts when absolutely required; set your sights higher, and start thinking of it as an absolute requirement.