I’ve always found it strange that an HTTP 401 is used to indicate two very different server-side states:
• “this resource requires authentication, and you didn’t attempt authentication”
• “as an early step in the request flow, you tried to authenticate yourself—but your authentication failed (due to e.g. having an unrecognized username; or using the wrong authentication method; or, of course, having the wrong password)”
I mean, I get it; in the end, after trying and failing to authenticate, the request-processing continues (with the “auth” field in the server’s model of the request state being nil, just like it would be if you hadn’t attempted auth); and the request you’re making at that point is still an attempt to request an authentication-required resource. You’re “not authenticated” at that point, so 401 it is.
But it really seems like auth processing should have its own “off ramp” in the HTTP request-processing lifecycle—i.e. if you ask for auth, and auth fails, you get a code and the request isn’t processed any further, so it doesn’t matter that the request you made is auth-required.
(After all, we mentally model HTTP resources, fundamentally, as URLs; and URLs put the auth stuff in the origin part, not in the request sent to the origin part. I would naively expect that an auth failure would resolve at the same stage as an unrecognized Host name!)
When you’re coding an HTTP client, it’s very hard to debug a corrupt Authorization header, because all you know, when you have anything even slightly wrong, is that the server is pretending it didn’t see it!
-----
And yeah, I know, in practice, why this isn’t the way things are. Historically, one of the first uses of the Authorization header was through Apache’s mod_auth_basic, which refers to an .htpasswd file in a directory to determine the set of valid auth credentials for all resources descending from that directory. In this model, auth is resource-specific, rather than server-specific; so it makes sense that, if you’re sending auth, and auth is unrecognized, but the specific resource you’re authing against turns out to not require auth, then the server can proceed just fine without sending you a 401.
However, I don’t think there’s any modern use-case where auth is resource-specific like that. HTTP auth “realms” are almost always per-origin these days. It makes a lot more sense to think of auth as something happening at the level of an implicit HTTP gateway between the client and the server ultimately sending the resource, where the gateway can know what server (realm) the client wants to route to—and so can auth the client on that basis, and deny the request if that auth fails—but the gateway can’t know anything about what the auth requirement policies are for individual resources on the backend it’s sitting in front of.