REST + XMLHTTPRequest + 401 != joy
27/05/08 15:38
For the last few months I’ve been working on a web app using PHP that I’m trying to be as RESTful I can. It’s been a learning curve, and has been both satisfying and infuriating. Over the weekend I’ve been working on the authentication. As it is a RESTful app HTTP digest authentication is the obvious (only?) choice. This being 2008 I also want the app to be pretty, like all the other web 2.0 apps (I hate the term “2.0″. If the internet was made of technologies that could be broken in to discrete versions then life would be so much easier. While IE6 is still around the web will be stuck in beta testing).
Unfortunately, after much keyboard thumping and bouts of pseudo-tourette, I’ve reach the conclusion that RESTful apps haven’t got the style to get into Club Web 2.0.
This conclusion is based on the assumption that the standard browser authentication dialog box should be banished to 1997 and has no place in a 2008 web app. I must mention that I have only tested this on Firefox 2, Safari 3 and Opera 9 on OS X 10.4. FF and Safari both suffer from the problem outlined below. (Opera will always display the ugly dialog box even if a username and password were supplied to the xmlhttprequest object).
There are a few tutorials that say that you can use the xmlhttprequest object to suppress the browsers dialog box. It is true that the xmlhttprequest object can do this, but it only works in very select conditions. This is because of two features (bugs?) of the xmlhttprequest:
1. Firstly the XHR only answers the first 401 response per resource. Any subsequent 401 responses will cause the ugly dialog box to pop up. There a quite a few scenarios where the server might respond with a 2nd 401 response. The most common is when the username and password supplied to the XHR object are incorrect. Another common occurrence is that the nonce used in the digest expires causing the server to send a new nonce with the 401.
2. Above I mentioned that “the XHR only answers the first 401 response per resource“. This means that we could re-authenticate using a different resource. The problem with this is that browser send a mix and match of digest information. Here’s an example:
Lets say that www.example.com/restricted/ requires authentication. Once the server has authenticated the client, the client will continues to send the the authentication details to all resources within www.example.com/restricted/. This is the correct behaviour.
Next the client tries to access www.example.com/restricted/secrets.html. When the client sends the request it also sends the authentication headers as before but this time the server response with a 401. The credentials that were valid for the rest of the site are not valid for this one resource. If we this request is made with the XHR object then it counts as the first 401 response from that resource. Therefore the XHR sends the request again using the username and password. So where’s the problem? The problem lies with the fact that the client (Firefox 2 and Safari 3 at least), continue to send the nonce from the very first 401 response. The server may not accept the old nonce for this request and will send another 401 response. This second 401 response will cause the client to show the ugly dialog boxes.
I have developed methods to work around both of these problems so that the XHR could be used. I’m not going to use either of them because they are ugly and complicated. I always strive for simplicity as simple code is easier to understand and there’s less chance for things to go wrong. Introducing unnecessary complication into an authentication system is asking for trouble.
Unfortunately, after much keyboard thumping and bouts of pseudo-tourette, I’ve reach the conclusion that RESTful apps haven’t got the style to get into Club Web 2.0.
This conclusion is based on the assumption that the standard browser authentication dialog box should be banished to 1997 and has no place in a 2008 web app. I must mention that I have only tested this on Firefox 2, Safari 3 and Opera 9 on OS X 10.4. FF and Safari both suffer from the problem outlined below. (Opera will always display the ugly dialog box even if a username and password were supplied to the xmlhttprequest object).
There are a few tutorials that say that you can use the xmlhttprequest object to suppress the browsers dialog box. It is true that the xmlhttprequest object can do this, but it only works in very select conditions. This is because of two features (bugs?) of the xmlhttprequest:
1. Firstly the XHR only answers the first 401 response per resource. Any subsequent 401 responses will cause the ugly dialog box to pop up. There a quite a few scenarios where the server might respond with a 2nd 401 response. The most common is when the username and password supplied to the XHR object are incorrect. Another common occurrence is that the nonce used in the digest expires causing the server to send a new nonce with the 401.
2. Above I mentioned that “the XHR only answers the first 401 response per resource“. This means that we could re-authenticate using a different resource. The problem with this is that browser send a mix and match of digest information. Here’s an example:
Lets say that www.example.com/restricted/ requires authentication. Once the server has authenticated the client, the client will continues to send the the authentication details to all resources within www.example.com/restricted/. This is the correct behaviour.
Next the client tries to access www.example.com/restricted/secrets.html. When the client sends the request it also sends the authentication headers as before but this time the server response with a 401. The credentials that were valid for the rest of the site are not valid for this one resource. If we this request is made with the XHR object then it counts as the first 401 response from that resource. Therefore the XHR sends the request again using the username and password. So where’s the problem? The problem lies with the fact that the client (Firefox 2 and Safari 3 at least), continue to send the nonce from the very first 401 response. The server may not accept the old nonce for this request and will send another 401 response. This second 401 response will cause the client to show the ugly dialog boxes.
I have developed methods to work around both of these problems so that the XHR could be used. I’m not going to use either of them because they are ugly and complicated. I always strive for simplicity as simple code is easier to understand and there’s less chance for things to go wrong. Introducing unnecessary complication into an authentication system is asking for trouble.