I have a Drupal 8 site and I need to dynamically redirect the user after they set their password for the first time after using their one-time login link.

I cannot figure out any way to hook into the workflow for this condition. Is it possible?


2 Answers 2


If you wanna redirect users, the below solution won't work, because '/user/reset/{uid}/{timestamp}/{hash}/login' controlled by UserController::resetPassLogin action if you see the return of resetPassLogin :

  • @return \Symfony\Component\HttpFoundation\RedirectResponse
    • Returns a redirect to the user edit form if the information is correct.
    • If the information is incorrect redirects to 'user.pass' route with a
    • message for the user.

I figure out a solution but i'm not sure if its the best way to do it:
for example if i want to redirect the user after first login to admin/content, i will override the resetPassLogin action like the following:


  path: '/user/reset/{uid}/{timestamp}/{hash}/login'
    _controller: '\Drupal\myModule\Controller\CustomUserController::customResetPassLogin'
    _title: 'Reset password'
    _user_is_logged_in: 'FALSE'
    _maintenance_access: TRUE
    no_cache: TRUE


namespace Drupal\myModule\Controller;

use Drupal\user\Controller\UserController;
use Drupal\Component\Utility\Crypt;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class CustomUserController extends UserController {

  public function customResetPassLogin($uid, $timestamp, $hash) {
    // The current user is not logged in, so check the parameters.
    $current = REQUEST_TIME;
    /** @var \Drupal\user\UserInterface $user */
    $user = $this->userStorage->load($uid);

    // Verify that the user exists and is active.
    if ($user === NULL || !$user->isActive()) {
      // Blocked or invalid user ID, so deny access. The parameters will be in
      // the watchdog's URL for the administrator to check.
      throw new AccessDeniedHttpException();

    // Time out, in seconds, until login URL expires.
    $timeout = $this->config('user.settings')->get('password_reset_timeout');
    // No time out for first time login.
    if ($user->getLastLoginTime() && $current - $timestamp > $timeout) {
        ->addError($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
      return $this->redirect('user.pass');
    elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && Crypt::hashEquals($hash, user_pass_rehash($user, $timestamp))) {
      $this->logger->notice('User %name used one-time login link at time %timestamp.', [
        '%name'      => $user->getDisplayName(),
        '%timestamp' => $timestamp,
        ->addStatus($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
      // Let the user's password be changed without the current password
      // check.
      $token = Crypt::randomBytesBase64(55);
      $_SESSION['pass_reset_' . $user->id()] = $token;
       * @TODO: change the route name to yours
      return $this->redirect(
          'absolute' => TRUE,

      ->addError($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'));
    return $this->redirect('user.pass');


NOTE : change system.admin_content by your route name.


You could try using hook_user_login() in a custom module or theme.

function my_module_user_login($account) {

  // Get the end part of the URL.
  $request_uri = \Drupal::request()->getRequestUri();

  // Check if the URL is a reset / one time login link.
  if (strpos($request_uri, 'user/reset') !== false) {

function _my_module_custom_function() {
  // Do stuff.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.