Simpler Password Reset in Drupal 7 - Revisited and Improved

This is a follow-up to my earlier post in which I described a simple trick changing Drupal's password reset behavior. That trick emails the user a link that logs them directly into the website, instead of the default one-time login page. That one-time login page forces user to click a Log in button before they are able to change their password. In the thread following that original post, Heine referred me to the Drupal issue in which the one-time login form was created. Now I understand better the reasons behind the one-time login form. To be perfectly frank, the solution from that thread was lazy. It solved the problem, but not elegantly, and Drupal deserves better. The user asked to reset their password so that's the form they should see, as soon as possible without the unnecessary step. Here's what the user sees first, in core Drupal: And here's what (I think) they should see: This post describes how to accomplish this with a custom module. [UPDATE: Save yourself the trouble of writing a custom module by downloading Simple Password Reset module from] Our strategy is to replace the unwanted behavior, which comes from a menu item defined in user.module. We use hook_menu_alter() to replace that behavior. An access callback ensures that only users who received the password reset email can reset their password. The URL parameters have to be just right. This is the same logic used in the core Drupal function that renders the one-time login form. Now that our access callback ensures only the right users can access our reset page, we can show the profile edit form. Remember, this is where Drupal shows the unwanted one-time login. Instead we let the user make the password change right away. That seems quite simple, but we're not done. At this point the user would be able to change their password, but then they'd have to log in manually. We want to save them the trouble, and log them in right away. The profile edit form becomes the one-time login form. To do this, we need a hook_form_alter(). Two things to note in the above code. First, we add a submit handler to the profile edit form (but only when used to reset a password). Our submit handler logs the user in so they don't have to do it manually. Second thing to notice, we honor an optional parameter, "brief", which abbreviates the form. This is because Drupal uses the reset page not just when a password is reset but also when new user accounts are created or email addresses are confirmed. In those cases it may be reasonable to show the entire profile edit form. When just resetting password, it's nice to show them only that portion of the form. To use this feature, under Admin >> Configuration >> People >> Settings >> Password recovery, replace the token [user:one-time-login-url] with this: [user:one-time-login-url]/brief This technique allows an administrator to choose between the longer or shorter version of the form fairly easily. So there you have it! This approach should have none of the drawbacks of my earlier trick, and all the benefits.



Thanks for your work. But I am not getting how it works. Your function tests for drupal_anonymous_user(). The issue issue linked by Heine is about a mail program calling the one-time login link twice, and invalidating it by the first call. In addition some spam filters will call links in emails, and therefore invalidate a one-time link before it reaches the end user. The logic of your module seems to be that a mail program (or maybe a spam filter) which calls the link will fail the drupal_anonymous_user() test, but a human user will pass that test. But is that true? How are these 'dummy' visits to the link detected and refused access by custom_pass_reset_access()?

Dave Cohen's picture

In my earlier post, I suggested it is possible to skip the one-time login form by logging the user in as soon as they follow the link sent to their email. Heine pointed out that Drupal used to work that way by default, but it caused a problem with some mail clients. The URL emailed to a user can be used only once, and some clients were trying to use it twice but it failed the second time. In this post, I'm describing a strategy which gets around that problem. With the Simple Password Reset module, the user never sees the one-time login form. Instead they are shown the user profile edit form. That's the same form they'd normally see after the one-time login. When they submit the user profile edit form they are also logged in. So instead of submitting two forms to change their password, they have to submit only one. And importantly, nothing happens until the form is submitted, so the problem Heine pointed out is not a problem here.
Dave Cohen's picture

Just to be clear, I'm calling drupal_anonymous_user() as a sanity check. This code should only be reached when a user is not logged into drupal. They must reset their password before logging in. So both the mail client and the browser will be anonymous to Drupal. Only if they are anonymous (and the other URL parameters are just right) will the user see the form. And only after the form is submitted will they no longer be anonymous. The user submits the form via the browser, the mail client does not submit the form.