Skip directly to content

Welcome to my personal website. I am Sivaji a web developer from Chennai with 2+ years experience in Drupal.

How to bypass one-time login form in password reset steps

sivaji's picture
on January 8th, 2011 at 6:44:15 PM

In this blog post, I am sharing one of my favorite trick that i had to do for bypassing one-time login form in password reset steps.

The default password recovery steps in Drupal is as below,

  1. Fill up the form in "user/password" page. This form will prompt the user to enter the e-mail address where the instructions will be sent.
  2. User clicks the password reset link
  3. User is taken to Drupal site where s/he has to confirm the password reset action by submitting a one-time login form.
  4. Submitting the form in step 3 will redirect the user to account edit page "user/%user/edit" to enter new password.

This flow looks fine but i had to remove the one-time login form i.e. to jump directly to account edit page when the password reset link in clicked.

My first thought to implement this was to rewrite the callback of "user/reset/%/%/%", user_pass_reset() with hook_menu_alter() implementation.

In drupal6, user_pass_reset() definition is as below,

/**
 * Menu callback; process one time login link and redirects to the user page on success.
 */
function user_pass_reset(&$form_state, $uid, $timestamp, $hashed_pass, $action = NULL) {
  global $user;
 
  // Check if the user is already logged in. The back button is often the culprit here.
  if ($user->uid) {
    drupal_set_message(t('You have already used this one-time login link. It is not necessary to use this link to login anymore. You are already logged in.'));
    drupal_goto();
  }
  else {
    // Time out, in seconds, until login URL expires. 24 hours = 86400 seconds.
    $timeout = 86400;
    $current = time();
    // Some redundant checks for extra security ?
    if ($timestamp < $current && $account = user_load(array('uid' => $uid, 'status' => 1)) ) {
      // Deny one-time login to blocked accounts.
      if (drupal_is_denied('user', $account->name) || drupal_is_denied('mail', $account->mail)) {
        drupal_set_message(t('You have tried to use a one-time login for an account which has been blocked.'), 'error');
        drupal_goto();
      }
 
      // No time out for first time login.
      if ($account->login && $current - $timestamp > $timeout) {
        drupal_set_message(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
        drupal_goto('user/password');
      }
      else if ($account->uid && $timestamp > $account->login && $timestamp < $current && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) {
        // First stage is a confirmation form, then login
        if ($action == 'login') {
          watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->name, '%timestamp' => $timestamp));
          // Set the new user.
          $user = $account;
          // user_authenticate_finalize() also updates the login timestamp of the
          // user, which invalidates further use of the one-time login link.
          user_authenticate_finalize($form_state['values']);
          drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to login. Please change your password.'));
          drupal_goto('user/'. $user->uid .'/edit');
        }
        else {
          $form['message'] = array('#value' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to login to the site and change your password.</p>', array('%user_name' => $account->name, '%expiration_date' => format_date($timestamp + $timeout))));
          $form['help'] = array('#value' => '<p>'. t('This login can be used only once.') .'</p>');
          $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
          $form['#action'] = url("user/reset/$uid/$timestamp/$hashed_pass/login");
          return $form;
        }
      }
      else {
        drupal_set_message(t('You have tried to use a one-time login link which has either been used or is no longer valid. Please request a new one using the form below.'));
        drupal_goto('user/password');
      }
    }
    else {
      // Deny access, no more clues.
      // Everything will be in the watchdog's URL for the administrator to check.
      drupal_access_denied();
    }
  }
} 

When i looked at the code i noticed the callback and submit handler for one time login form are same. All I had to do is to call the user_pass_reset() with fifth argument, $action set to 'login'. I could achieve this by altering the e-mail text sent for password recovery from "admin/user/settings".

The default password recovery email body is as below

!username,

A request to reset the password for your account has been made at !site.

You may now log in to !uri_brief by clicking on this link or copying and pasting it in your browser:

!login_url

This is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.

After logging in, you will be redirected to !edit_uri so you can change your password.

Replaced "!login_url" with "!login_url/login". This helped to get the required flow without rewriting callback.

In short, we can skip the one-time login form in password recovery step by appending /login to token !login_url in password recovery e-mail.

Comments

Thanks so much for posting this. :) I had this exact need and simply appending /login to the !login_url token works great! :D

This feature is aimed at Microsoft Windows 8 need to securely access corporate network Windows 8 resources while away from the Office 2010 . Essentially a simple Microsoft Office 2010 replacement for VPN connections, Download Office 2010 natively optical discs Office 2010 Download and enables you to write to Office 2010 Professional recordable media. Microsoft Office 2010 Download has consolidated the most common Microsoft Office 2011 sharing tasks into a single simple interface Windows 7 .

Computers in a Microsoft Windows 7 can easily share documents, digital media files, and printers over a home network.The Win 7 contained all the features of Download Windows 7 think if you want most full functional type, Buy Windows 7 this version will be your best Office 2007 Key choice. In addition, you can save more money Office 2010 Key when you buy it.

This is definitely incredible tip! I always followed that one time login form untill I saw your post.

Thanks a lot buddy!

Thanks for this worthy tip.. it really saves time.. :) Thumbs Up..!

this let's the user login but it won't change the user role using loggintoboggan unless the user changes their password. That module has a non-authenticated role option in which is can assign a user to a pending role unless they change their password through the automatic email that drupal sends. This proves to be annoying if you allow the user to assign a password upon registration. 

Is there a way to bypass this?

Post new comment