Authentication in Web applications has been highjacked, HTTP defines a standard way of providing authentication but most apps use the evil spawn of Netscape, otherwise known as cookies. Why is this? Cookies aren't designed for authentication, they're a pain to use for it, insecure unless you know what you're doing, non-standard, and unRESTful.
Warning: The solution outlined in this article is experimental and might be a complete lie, be warned that your mileage may/will vary.
The main reason people walk away from using HTTP authentication is that they want control over the look of the login form and most browsers display an awful looking dialog box. So what we need is a way for HTML forms to pass HTTP auth data when it's submitted. The HTML spec provides HTML forms as a way to create queries and to POST urlencoded data to a URL, but can we subvert it?
We could add an
onsubmit event to our login form that pushes the username
and password values within our form into the URL in the forms
That way our login request would supply the users credentials in the URL and avoid the
server returning a 401 response and causing our browser from showing the HTTP auth box.
So is there an alternative way that doesn't require us to mung the username and password into the URL? Yes, our new friend, XMLHTTPRequest, it can submit the correct HTTP auth headers for us. Rather than adjusting the URL the form submits to, we can use XMLHTTPRequest to do a request before the form submits supplying the entered username and password. This will set up the browser with the HTTP auth credentials so it'll also send them with our actual form submission login request.
Enough talking, here's some code. This is our login function that we bind to our form submission:
It sends our XHR request with the given username and password, and then redirects the
that I introduced here, and then some code to create our login form and set everything
This looks for all anchors with the class of
httpauth and replaces it with
a nice HTML form with the
login() function bound to it's
private.php looks like this, then we'll see that everything
works as we'd like:
It's not all good just yet, this technique works in IE6 and Firefox but is known not to work in both Opera and Safari, so if you care about those browsers you may want to think again about using this (or to spend some time investigating why it fails in those browsers).
HTTP Digest is a way of authenticating a client while never sending the clients password over the wire. To use Digest rather than Basic HTTP Auth, we only need to adjust our PHP script to implement digest:
So there we have it, there is no need for HTTP authentication to be shunned, even for aesthetic reasons. HTTP auth offers lots of advantages over using cookies:
So we should be using it, there's no excuses anymore.
I have put up an example of this code as a demonstration.
Note: I was first introduced to this idea by Christian Jensen and Jan Algermissen on the REST Discuss mailing list, so credit should be sent their way.
Update: Dimitri Glazkov made me revisit using HTTP Digest with this technique and realise that it does actually work, thanks Dimitri.
Update: Travis Estill and David Kleinschmidt reminded me that 401 responses shouldn't be returned without an Auth header and so a 403 is a better response code. This also helps to make Safari behave too.