Sending One-Time Login Links for Password Resets Programmatically in Drupal 9

In Drupal 9, user-friendly and secure password reset mechanisms are essential for user account management. One common approach is to send users one-time login links via email, allowing them to reset their passwords easily. In this blog post, we'll guide you through the process of implementing this feature programmatically within your custom module's .module file in Drupal 9.

Prerequisites

Before proceeding with this guide, make sure you have the following:

  1. A Drupal 9 website installed and configured.
  2. The ability to create and manage custom modules in Drupal.
  3. Basic knowledge of Drupal module development.

Step 1: Create a Custom Module

If you haven't already created a custom module, follow these steps to create one:

  1. In your Drupal installation, navigate to the /modules/custom directory.
  2. Create a new directory for your custom module, giving it a unique name. For example, "password_reset_module."
  3. Inside your module's directory, create a .info.yml file to define your module. Here's a sample .info.yml file:
name: 'Password Reset Module'
type: module
description: 'Custom module to send one-time login links for password resets.'
core_version_requirement: ^8 || ^9
package: Custom
dependencies:
  - user:user

Step 2: Implement Password Reset Logic in the .module File

Inside your custom module's directory, create a .module file (e.g., password_reset_module.module). Implement the password reset logic in this file. Below is a simplified example of this logic:

use Drupal\user\Entity\User;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Implements hook_menu().
 */
function password_reset_module_menu() {
  $items = [];

  $items['reset-password/%'] = [
    'title' => 'Reset Password',
    'page callback' => 'drupal_get_form',
    'page arguments' => ['password_reset_module_reset_password_form', 2],
    'access arguments' => ['access content'],
    'type' => MENU_CALLBACK,
  ];

  return $items;
}

/**
 * Form constructor for the password reset form.
 */
function password_reset_module_reset_password_form($form, &$form_state, $account) {
  $form['#account'] = $account;

  $form['pass'] = [
    '#type' => 'password_confirm',
    '#size' => 25,
    '#description' => t('Enter a new password for the user account.'),
  ];

  $form['submit'] = [
    '#type' => 'submit',
    '#value' => t('Reset Password'),
  ];

  return $form;
}

/**
 * Form submission handler for the password reset form.
 */
function password_reset_module_reset_password_form_submit($form, &$form_state) {
  $account = $form['#account'];

  // Set the new password.
  user_save($account, ['pass' => $form_state['values']['pass']);

  // Log the user in.
  user_login_finalize($account);

  // Redirect to the user's profile or any other destination.
  $form_state['redirect'] = 'user/' . $account->id();
}

/**
 * Send a one-time login link to a user's email.
 */
function password_reset_module_send_password_reset_link($username_or_email) {
  $account = user_load_by_name($username_or_email) ?: user_load_by_mail($username_or_email);

  if ($account && $account->id()) {
    // Generate a one-time login link.
    $token = user_pass_rehash($account, user_password());
    $link = url('reset-password/' . $account->id(), ['query' => ['u' => $account->id(), 'timestamp' => $account->get('login')->value, 'token' => $token], 'absolute' => TRUE]);

    // Send an email with the link.
    $mailManager = \Drupal::service('plugin.manager.mail');
    $module = 'password_reset_module';
    $key = 'password_reset_link';
    $to = $account->getEmail();
    $params = ['link' => $link];
    $langcode = \Drupal::currentUser()->getPreferredLangcode();
    $send = TRUE;

    $mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send);

    return TRUE;
  }

  return FALSE;
}

/**
 * Implements hook_mail().
 */
function password_reset_module_mail($key, &$message, $params) {
  switch ($key) {
    case 'password_reset_link':
      $message['subject'] = t('Password Reset Link');
      $message['body'][] = t('Click the following link to reset your password: @link', ['@link' => $params['link']]);
      break;
  }
}

In this code, we have created a custom module with a menu callback (reset-password/%) that allows users to reset their passwords by clicking on a one-time login link sent via email. When a user requests a password reset, an email is dispatched to their registered email address, containing a link that, when clicked, allows them to reset their password.

Step 3: Enable Your Custom Module

To start using your custom module, enable it through the Drupal admin interface by navigating to Extend and enabling your module.

Step 4: Usage

To send a one-time login link to a user, you can use the password_reset_module_send_password_reset_link($username_or_email) function and pass the username or email as an argument. For example:

password_reset_module_send_password_reset_link('username_or_email');

This function sends a reset link to the user's email address.

Step 5: Security and Best Practices

Make sure your custom module includes proper permission checks and validation to prevent misuse. Additionally:

  • Encourage users to set secure passwords by implementing a strong password policy.
  • Keep your Drupal installation and contributed modules up to date to patch any security vulnerabilities.

Conclusion

Implementing one-time login links for password resets in Drupal 9 programmatically within your custom module's .module file provides a secure and convenient way for users to reset their passwords. By creating a custom module and implementing the necessary logic, you can ensure your users have a reliable method for resetting their passwords when necessary. Following best practices is crucial to maintaining the security of user accounts and your Drupal website as a whole.

Share on social media

Add new comment