While it may be attractive from the usability perspective to let users click on a link in an email to confirm a registration, it violates one of the cardinal rules of the web: don't change state on HTTP GET requests. This weblog post explains the problem and suggests solutions.
In REST Mistake #1: Confirming GETs (See Resources), Elliotte Rusty Harold described a conversation he and I had last week after the Software Developer conference about confirmation emails that use GET to change state. Over dinner he described a scheme he had devised to enable users to post comments to his blog without registering, while still requiring the user to authenticate. In his scheme you would need to supply your email with your comment. After submitting your comment, you would receive a confirmation email containing a link. Clicking on the link in the email would approve your comment. You hadn't registered, but you authenticated yourself as the person who has that email. He said this approach did a good job of eliminating comment spam and noisy comments.
I pointed out to Rusty that the scheme violates the rule not to change state as a result of a GET. When you click on the link in your email, it opens up the browser, and the browser does an HTTP GET to retrieve the page. If that action actually approves the comment, then you've changed the state of the web application as a result of the GET. To make such changes, however, you should use POST. When Rusty went back to look at his implementation, it turned out that only his description of his scheme had the flaw, not the actual implementation. He had forgotten how he had implemented it.
The reason this flaw was apparent to me is that at Artima we actually did send out emails that used a similar one-click mechanism, which had the same flaw. One day, however, it dawned on me we were changing state on GET requests, and we altered our design to POSTs.
Side effects and Usability
We initially chose the one-click design to maximize ease of use. I wanted it to be easy for a user to unsubscribe from a newsletter, for example. Previously to unsubscribe you had to log in to your Artima account and unclick a checkbox in your settings. It was a several step process, which would grow to even more steps if you forgot your password and had to go through the process to get a new one. In addition, we wanted it to be easy for you to confirm an email address by clicking on a link in an email we send after you register.
The HTTP specification says that you mustn't hold users accountable for any state changes that result from a GET. It doesn't imply you can't change state at all on a GET. For example, it seems quite reasonable to log a user's GET requests (which changes state on the server), even if sometime later that user can look and see a report of that GET request (which means the state change can ultimately be visible to the user). But you shouldn't do something on a GET that from the user's perspective indicates they have agreed to something. This is an important aspect of HTTP, which enables various automated activities including caching, prefetching, and crawling.
Prefetching is a good example of why you should avoid using one-click confirmation emails. Google has a tool called Web Accelerator that you can install in your browser to speed up your experience of surfing of the web. Among other techniques, the Web Accelerator prefetches URLs mentioned on the page you are looking at, so that in case you click on a link, the page may already have been fetched and cached at your browser, in which case you won't have to wait for it to download.
Imagine you have installed Google Web Accelerator in your browser, and you are reading your email via Yahoo Mail. You open an email that says, "Click the following link to verify this email address and enable your newly created Artima account." Well, you've never heard of Artima, and you didn't attempt to register there, so you don't click. Unfortunately, however, because you are looking at that email in a web page, Google Web Accelerator on your browser may decide to prefetch the confrimation page to accelerate your access of it in case you click on the link. So even though you were careful to not click on the link, Web Accelerator performed the GET anyway. Thus, Google Web Accelerator verified the email and enable the account, even though you didn't actually attempt to register in the first place and never clicked on the confirmation link. The problem is not with Web Accelerator, however, but with one-click email confirmation itself.
A Two-Click Solution
The solution we deployed on Artima was simply that when you click on a link in an email, we take you to a page that contains a button to press to perform the action. To verify an email on a new account, for example, you click on the link in the email, which does a GET. The GET returns a page that says, "Please click on this button to verify your email," and provides a form button to press that sends a POST. This solves the HTTP problem, but now a smooth one-click process has become a more cumbersome click-read-then-click-again process. If you can send HTML email, then you can put the button right in the email, but if you send text email, you are left with a GET-then-POST solution if you want to use HTTP. (You could also have people reply to an obscure email address with something obscure in the subject, but I think that for most users using the web for email confirmation will be easier.)
Over the weekend I drew some diagrams of the patterns I used building the signup / activate / login / forgot password pages of a site I'm building.
Besides being a diagrammatic example of what you and Rusty are talking about, I also make a point at the end about another pattern in handling forgotten passwords that I just discovered Artima does not use :-)
> Over the weekend I drew some diagrams of the patterns I > used building the signup / activate / login / forgot > password pages of a site I'm building. > > Besides being a diagrammatic example of what you and Rusty > are talking about, I also make a point at the end about > another pattern in handling forgotten passwords that I > just discovered Artima does not use :-) > Those are nice diagrams. You're right that we have a password reset problem, though you don't mention on your page what the problem is.
We don't know anyone's password, because we keep a hash of the password in the database, not the actual password. When someone logs in, we hash the password they type in and compare it to the one in the database. That means that we can't actually send someone a "reminder" of their password as advertised. So one minor problem is I should rephrase that description of the activity as reseting a password, not getting a reminder.
But the main problem is that what we do is compute a random new password and send that to the user in an email. They are then instructed to use the new password to log in, and then change the password back to something they can more easily remember. The problem here is that we are sending the password in plain text. By sending an email with a link to a password reset page, we can keep passwords hidden from spying, so long as the reset page is always accessed over HTTPS, not HTTP. We don't do that now in part because when you log in you're not sending your password over HTTPS, so it can already be sniffed. In our new architecture, however, our plan is that passwords will always be transmitted across the internet over HTTPS, and never transmitted via email.
We do one thing not shown in your diagrams. Often users who register can't get the email, either they mistyped it or the email got stuck in their spam filter. If they log in with their username and password, we don't log them in, but we give them an opportunity to send another confirmation email to the same email address or enter another one. We also give them this opportunity as soon as they register.
One good practice is to send a one-time, limited life token with the email, and not reset the user's actual password until they've followed the link. That way you don't get people being annoying and resetting the passwords of people they don't like.
I've recently decided that if you're going to be doing email-validated signup, you may as well move the bulk of the registration process to after the validation has been completed. For the initial signup just request full name, email address and reserve a username for them, then have the link in the email direct back to a "now give us the rest of your details" page.
This also gives you a good excuse to use POST instead of GET, because then the registration isn't complete until that form has been filled in successfully.
> One good practice is to send a one-time, limited life > token with the email, and not reset the user's actual > password until they've followed the link. That way you > don't get people being annoying and resetting the > passwords of people they don't like. > Well, I figure that so long as you are sending them a limited-life token in the email, why not take them to a page where they can change their password, instead of taking them to a page that says (after they press a submit button to get a POST) that their password has been set to some randomly generated string, and now they can log in and edit their settings to change it to whatever they want.
> I've recently decided that if you're going to be doing > email-validated signup, you may as well move the bulk of > the registration process to after the validation > has been completed. For the initial signup just request > full name, email address and reserve a username for them, > then have the link in the email direct back to a "now give > us the rest of your details" page. > > This also gives you a good excuse to use POST instead of > GET, because then the registration isn't complete until > that form has been filled in successfully.
That's a great suggestion. I had noticed that when I registered with TheServerSide.com a long time ago, Floyd Marinescue had it set up so that the first step was getting a valid email address. After that I was presented with a form asking for way too much personal information. But I have considered changing our system to something more like this, and now that we have a post after clicking on the link in the email, you rightly point out that there may as well be something to fill in as "step 2."
The other thing I was considering was dropping the requirement to get a nickname until you actually do something for which we would require it, such as posting to a forum. In the meantime, you'd be able to log in with your email address as your username, because we now enforce that each email address in our database appears only once (so it can also be used to identify an account). This would simplify the sign up process, and reduce the demand on the limited namespace of usernames. A lot of people register to accomplish specific tasks, and have no intention of ever posting to a forum. (I like having a username showing in each forum message so that you can differentiate one John Smith from the next.)
> Good point about GET. This may be off topic, but if you > send an email to confirm, then it may be more convenient > for the user to just reply to that email. Mailing lists > often do it this way. > In my experience people find these kinds of confirmation procedures confusing. I myself have been frustrated when I have to read a long explanation of what I'm supposed to reply to and what I should or should not have as the Subject or in the Body of the email. In addition, I use a challenge/response system to block spam, and all it requires is that people simply reply. But some people don't figure it out. I suspect that putting a link in the email and asking people to click, then continuing the process on a web page will be easier for most, especially unsophisticated, users.
> Those are nice diagrams. You're right that we have a > password reset problem, though you don't mention on your > page what the problem is.
You might be thinking of a different problem but I do state what I think the problem is: "I realised someone could maliciously enter the email address of another user to reset their password. Not a security issue so much (the new password goes to the right person) but it's a nuisance for the person if they didn't request the reset."
This problem is very constructed, Gogle web accelerator does not pre-fetch links that contain query-parameters, IDs etc. Another reason why I won't let that argument count, is that people shouldn't design their websites/applications around certain pieces of software.
That leaves you with the recommendation of the W3 w.r.t to GET/POST. Frnakly I think the recommendation is good in general, but it is not religious dogma. I think this pecific case (i.e. activation) would grant an exception.
My suggestion: Activate and show a confirmation message. Below that put some explanatory text and the ability to undo the last action via a POST ( a cancel but, if you will). This makes the easy stuff easy, without making the hard stuff hard. People are by now very trained to click blindly on links in emails, especially from unkown people, talking about confirmations etc.
A simple concept like "click here to get the job done" is turned into another programming nightmare that requires tons of documentation, articles, analysis and discussion...that says something for "standards", simplicity/complexity, and why ultimately we spend more time discussing things instead of creating things...
> This problem is very constructed, Gogle web accelerator > does not pre-fetch links that contain query-parameters, > IDs etc. Another reason why I won't let that argument > count, is that people shouldn't design their > websites/applications around certain pieces of software. > > That leaves you with the recommendation of the W3 w.r.t to > GET/POST. Frnakly I think the recommendation is good in > general, but it is not religious dogma. I think this > pecific case (i.e. activation) would grant an exception. > It isn't an issue of designing application around a certain piece of software, but around a standard. The standard enables more innovation around that standard in the future. I.e., it isn't about Google Web Accelerator, but software agents in general that may use GET, even those that haven't been imagined yet. You could invent something tomorrow that uses GETs in a way no one has yet imagined, because the standard says that doing GETs should always be "safe," and for the most part people follow that. (The spec elaborates quite a bit on what "safe" means.) If a GET isn't safe, it isn't the tool's fault, it's the fault of the developers who wrote the application. For example, if you have a "Delete This" link on every page of your application that deletes the page with a GET, and a search engine comes in and spiders every one of those links thereby deleting your entire website, it isn't the fault of the search engine.
> My suggestion: Activate and show a confirmation message. > Below that put some explanatory text and the ability to > undo the last action via a POST ( a cancel but, if you > will). This makes the easy stuff easy, without making the > hard stuff hard. People are by now very trained to click > blindly on links in emails, especially from unkown people, > talking about confirmations etc.
What I recommend is if possible send an HTML confirmation email that contains a one-click button to press right in the email. If sending an email in plain text, take them to a page that clearly and simply says push this button to unsubscribe/register, etc. It isn't as user-friendly as one click, but it isn't that onerous for the user, and it is compliant with the specification.
> A simple concept like "click here to get the job done" is > turned into another programming nightmare that requires > tons of documentation, articles, analysis and > discussion...that says something for "standards", > simplicity/complexity, and why ultimately we spend more > time discussing things instead of creating things...
The same argument can be said for transaction processing, algorithms, distributed communication - or, for that matter, any human endeavor. For instance, if a patient comes in with a headache, a doctor could say, well, just take some pain killer and you'll be done with that visit. Who is to blame if the patient dies the next week of a brain tumor?
There is a difference between correctness and unnecessary complexity. If a specification clearly states that something works a certain way, and we are too busy or not caring enough to follow that instruction, we almost certainly set ourselves up for failure.
We can sometimes recover from that failure with makeshift solutions that, in turn, lead to undue complexity down the road. For intance, the first time a spider deletes a page because of an incorrect use of GET, you could write some work-around code, e.g., if (SPIDER) then don't delete. I've seen code peppered with such work-arounds (Swing event threading, anyone?). These things definitely create complexity, and make a system untestable and fragile over time.
Keynes said once that maybe we should not worry too much about the economic consequences of our present decisions because "in the long run, we'll all be dead." The trouble is, the long run often turns out to be shorter than at first imagined.
> It isn't an issue of designing application around a > certain piece of software, but around a standard.
That's what I said. My point was that is all there is to the argument of the article, GWA is irrellevant.
> For example, if you have a "Delete This" link > on every page of your application that deletes the page > with a GET, and a search engine comes in and spiders every > one of those links thereby deleting your entire website, > it isn't the fault of the search engine.
Exactly, that's why I said, it is a good idea in general. And if you carefully read my above reply I didn't advocate using such links, and neither would I ever.
Rules are a good thing, and followin them in general is also a good thing. But they are NOT religious dogma, the better you get to know the technology and the limitations, and the more you work with it, the better you can estimate the "why" of the rules and come to a true understanding. What I was saying was, that there exist certain situations where it is more beneficial to break a rule, than to blindly follow it "just 'cause".
It's like arguing people never should have used tables and blind-gifs for design, instead they should have waited a few years until CSS was ready. In the meantime everybody should have used visually unappealing sites, "just 'cause".
> You could invent > something tomorrow that uses GETs in a way no one has yet > imagined, because the standard says that doing GETs should > always be "safe," and for the most part people follow > that.
Like I said very constructed. How does that affect the very specific problem at hand: A link in an email, which activates an account and leads to a page, where you can undo the activation? Tell you what: Deal with the problem when it arrises, because as it is -> the "cost of failure" is so incredibly low. It's not like I am advocating sending money via clicking on links for a banking site. ;)
Think about this: Do you also advocate not using cookies at all? DO you also argue that access-logs in webservers break the rules and should be abolished?
> What I recommend is if possible send an HTML confirmation
HTML emails are the devil. Congratulations, you have just created a lot more problems than you have solved. "just 'cause"
Flat View: This topic has 21 replies
on 2 pages