diff --git a/core/modules/user/lib/Drupal/user/Controller/UserController.php b/core/modules/user/lib/Drupal/user/Controller/UserController.php
index b7196bf..495a5aa 100644
--- a/core/modules/user/lib/Drupal/user/Controller/UserController.php
+++ b/core/modules/user/lib/Drupal/user/Controller/UserController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\user\UserInterface;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
  * Controller routines for user routes.
@@ -59,6 +60,75 @@ public function userTitle(UserInterface $user = NULL) {
   }
 
   /**
+   * Returns the user password reset page.
+   *
+   * @param int $uid
+   *   UID of user requesting reset.
+   * @param int $timestamp
+   *   The current timestamp.
+   * @param string $hash
+   *   Login link hash.
+   *
+   * @return array
+   *   The form structure.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   */
+  public function resetPass($uid, $timestamp, $hash) {
+    $account = $this->currentUser();
+    $config = $this->config('user.settings');
+    $user_storage_controller = $this->entityManager()->getStorageController('user');
+    // When processing the one-time login link, we have to make sure that a user
+    // isn't already logged in.
+    if ($account->isAuthenticated()) {
+      // The current user is already logged in.
+      if ($account->id() == $uid) {
+        drupal_set_message($this->t('You are logged in as %user. <a href="!user_edit">Change your password.</a>', array('%user' => $account->getUsername(), '!user_edit' => $this->url('user.edit', array('user' => $account->id())))));
+      }
+      // A different user is already logged in on the computer.
+      else {
+        $reset_link_user = $user_storage_controller->load($uid);
+        if (!empty($reset_link_user)) {
+          drupal_set_message($this->t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="!logout">logout</a> and try using the link again.',
+            array('%other_user' => $account->getUsername(), '%resetting_user' => $reset_link_user->getUsername(), '!logout' => $this->url('user.logout'))));
+        }
+        else {
+          // Invalid one-time link specifies an unknown user.
+          drupal_set_message($this->t('The one-time login link you clicked is invalid.'));
+        }
+      }
+      return $this->redirect('<front>');
+    }
+    else {
+      // The current user is not logged in, so check the parameters.
+      // Time out, in seconds, until login URL expires.
+      $timeout = $config->get('password_reset_timeout');
+      $current = REQUEST_TIME;
+      /* @var \Drupal\user\UserInterface $user */
+      $user = $user_storage_controller->load($uid);
+      // Verify that the user exists and is active.
+      if ($user->isActive()) {
+        // No time out for first time login.
+        if ($user->getLastLoginTime() && $current - $timestamp > $timeout) {
+          drupal_set_message($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 && $hash === user_pass_rehash($user->getPassword(), $timestamp, $user->getLastLoginTime())) {
+          $expiration_date = $user->getLastLoginTime() ? $this->container()->get('date')->format($timestamp + $timeout) : NULL;
+          return \Drupal::formBuilder()->getForm('Drupal\user\Form\UserPasswordResetForm', $user, $expiration_date, $timestamp, $hash);
+        }
+        else {
+          drupal_set_message($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');
+        }
+      }
+    }
+    // Deny access, no more clues.
+    // Everything will be in the watchdog's URL for the administrator to check.
+    throw new AccessDeniedHttpException();
+  }
+
+  /**
    * Logs the current user out.
    *
    * @return \Symfony\Component\HttpFoundation\RedirectResponse
diff --git a/core/modules/user/lib/Drupal/user/Form/UserForm.php b/core/modules/user/lib/Drupal/user/Form/UserForm.php
deleted file mode 100644
index 49c8c76..0000000
--- a/core/modules/user/lib/Drupal/user/Form/UserForm.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-/**
- * @file
- * Contains \Drupal\user\Form\UserForm.
- */
-
-namespace Drupal\user\Form;
-
-/**
- * Temporary form controller for user module.
- */
-class UserForm {
-
-  /**
-   * Wraps user_pass_reset().
-   *
-   * @todo Remove user_pass_reset().
-   */
-  public function resetPass($uid, $timestamp, $hash, $operation) {
-    module_load_include('pages.inc', 'user');
-    return drupal_get_form('user_pass_reset', $uid, $timestamp, $hash, $operation);
-  }
-
-}
diff --git a/core/modules/user/lib/Drupal/user/Form/UserPasswordResetForm.php b/core/modules/user/lib/Drupal/user/Form/UserPasswordResetForm.php
new file mode 100644
index 0000000..add599e
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Form/UserPasswordResetForm.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Form\UserPasswordResetForm.
+ */
+
+namespace Drupal\user\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Component\Utility\Crypt;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Form controller for the user password reset form.
+ */
+class UserPasswordResetForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'user_pass_reset';
+  }
+
+  /**
+   * Form constructor.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   * @param \Drupal\Core\Session\AccountInterface $user
+   *   User requesting reset.
+   * @param string $expiration_date
+   *   Formatted expiration date for the login link, or NULL if the link does
+   *   not expire.
+   * @param int $timestamp
+   *   The current timestamp.
+   * @param string $hash
+   *   Login link hash.
+   *
+   * @return array
+   *   The form structure.
+   *
+   */
+  public function buildForm(array $form, array &$form_state, AccountInterface $user = NULL, $expiration_date = NULL, $timestamp = NULL, $hash = NULL) {
+    if ($expiration_date) {
+      $form['message'] = array('#markup' => $this->t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $user->getUsername(), '%expiration_date' => $expiration_date)));
+    }
+    else {
+      // No expiration for first time login.
+      $form['message'] = array('#markup' => $this->t('<p>This is a one-time login for %user_name.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $user->getUsername())));
+    }
+
+    $form['#title'] = 'Reset Password';
+    $form['user'] = array(
+      '#type' => 'value',
+      '#value' => $user,
+    );
+    $form['timestamp'] = array(
+      '#type' => 'value',
+      '#value' => $timestamp,
+    );
+    $form['help'] = array('#markup' => '<p>' . $this->t('This login can be used only once.') . '</p>');
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Log in'));
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    $user = $form_state['values']['user'];
+    user_login_finalize($user);
+    watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $user->getUsername(), '%timestamp' => $form_state['values']['timestamp']));
+    drupal_set_message($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::randomStringHashed(55);
+    $_SESSION['pass_reset_' . $user->id()] = $token;
+    $form_state['redirect_route']['route_name'] = 'user.edit';
+    $form_state['redirect_route']['route_parameters'] = array('user' => $user->id());
+    $form_state['redirect_route']['options'] = array(
+      'query' => array('pass-reset-token' => $token),
+      'absolute' => TRUE,
+    );
+  }
+}
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index 2977253..f1df715 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -5,96 +5,8 @@
  * User page callback file for the user module.
  */
 
-use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-use Drupal\Component\Utility\Crypt;
-
-/**
- * Menu callback; process one time login link and redirects to the user page on success.
- *
- * @deprecated Use \Drupal\user\Form\UserForm::resetPass()
- */
-function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $action = NULL) {
-  global $user;
-
-  // When processing the one-time login link, we have to make sure that a user
-  // isn't already logged in.
-  if ($user->isAuthenticated()) {
-    // The existing user is already logged in.
-    if ($user->id() == $uid) {
-      drupal_set_message(t('You are logged in as %user. <a href="!user_edit">Change your password.</a>', array('%user' => $user->getUsername(), '!user_edit' => url("user/" . $user->id() . "/edit"))));
-    }
-    // A different user is already logged in on the computer.
-    else {
-      $reset_link_account = user_load($uid);
-      if (!empty($reset_link_account)) {
-        drupal_set_message(t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="!logout">logout</a> and try using the link again.',
-          array('%other_user' => $user->getUsername(), '%resetting_user' => $reset_link_account->getUsername(), '!logout' => url('user/logout'))));
-      } else {
-        // Invalid one-time link specifies an unknown user.
-        drupal_set_message(t('The one-time login link you clicked is invalid.'));
-      }
-    }
-    return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
-  }
-  else {
-    // Time out, in seconds, until login URL expires.
-    $timeout = \Drupal::config('user.settings')->get('password_reset_timeout');
-    $current = REQUEST_TIME;
-    $account = user_load($uid);
-    // Verify that the user exists and is active.
-    if ($timestamp <= $current && $account && $account->isActive()) {
-      // No time out for first time login.
-      if ($account->getLastLoginTime() && $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.'));
-        return new RedirectResponse(url('user/password', array('absolute' => TRUE)));
-      }
-      elseif ($account->isAuthenticated() && $timestamp >= $account->getLastLoginTime() && $timestamp <= $current && $hashed_pass == user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime())) {
-        // First stage is a confirmation form, then login
-        if ($action == 'login') {
-          // Set the new user.
-          // user_login_finalize() also updates the login timestamp of the
-          // user, which invalidates further use of the one-time login link.
-          user_login_finalize($account);
-          watchdog('user', 'User %name used one-time login link at time %timestamp.', array('%name' => $account->getUsername(), '%timestamp' => $timestamp));
-          drupal_set_message(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::randomStringHashed(55);
-          $_SESSION['pass_reset_' . $user->id()] = $token;
-          return new RedirectResponse(url('user/' . $user->id() . '/edit', array(
-            'query' => array('pass-reset-token' => $token),
-            'absolute' => TRUE,
-          )));
-        }
-        else {
-          if (!$account->getLastLoginTime()) {
-            // No expiration for first time login.
-            $form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getUsername())));
-          }
-          else {
-            $form['message'] = array('#markup' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $account->getUsername(), '%expiration_date' => format_date($timestamp + $timeout))));
-          }
-          $form['help'] = array('#markup' => '<p>' . t('This login can be used only once.') . '</p>');
-          $form['actions'] = array('#type' => 'actions');
-          $form['actions']['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 that has either been used or is no longer valid. Please request a new one using the form below.'));
-        return new RedirectResponse(url('user/password', array('absolute' => TRUE)));
-      }
-    }
-    else {
-      // Deny access, no more clues.
-      // Everything will be in the watchdog's URL for the administrator to check.
-      throw new AccessDeniedHttpException();
-    }
-  }
-}
 
 /**
  * Prepares variables for user templates.
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index bbc2053..4395455 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -173,10 +173,9 @@ user.cancel_confirm:
     _entity_access: 'user.delete'
 
 user.reset:
-  path: '/user/reset/{uid}/{timestamp}/{hash}/{operation}'
+  path: '/user/reset/{uid}/{timestamp}/{hash}'
   defaults:
-    _content: '\Drupal\user\Form\UserForm::resetPass'
+    _content: '\Drupal\user\Controller\UserController::resetPass'
     _title: 'Reset password'
-    operation: NULL
   requirements:
     _access: 'TRUE'
