diff --git a/devel.module b/devel.module
index 7b810b1..708ff9a 100644
--- a/devel.module
+++ b/devel.module
@@ -350,17 +350,6 @@ function backtrace_error_handler($error_level, $message, $filename, $line, $cont
   }
 }
 
-
-
-/**
- * Comparison helper function for uasort() in devel_switch_user_list().
- *
- * Sorts the Switch User links by the user's last access timestamp.
- */
-function _devel_switch_user_list_cmp($a, $b) {
-  return $b['last_access'] - $a['last_access'];
-}
-
 /**
  * Returns the core version.
  */
diff --git a/devel.routing.yml b/devel.routing.yml
index c12faaa..0504ec5 100644
--- a/devel.routing.yml
+++ b/devel.routing.yml
@@ -130,7 +130,7 @@ devel.session:
 devel.switch:
   path: '/devel/switch/{name}'
   defaults:
-    _controller: '\Drupal\devel\Controller\DevelController::switchUser'
+    _controller: '\Drupal\devel\Controller\SwitchUserController::switchUser'
     _title: 'Switch user'
   options:
     _admin_route: TRUE
diff --git a/devel.services.yml b/devel.services.yml
index da1f51e..31a38fb 100644
--- a/devel.services.yml
+++ b/devel.services.yml
@@ -15,9 +15,3 @@ services:
     class: Drupal\devel\StackMiddleware\DevelMiddleware
     tags:
       - { name: http_middleware, priority: 350 }
-
-  access_check.switch_user:
-    class: Drupal\devel\Access\SwitchAccess
-    arguments: ['@csrf_token']
-    tags:
-      - { name: access_check }
diff --git a/src/Access/SwitchAccess.php b/src/Access/SwitchAccess.php
deleted file mode 100644
index c306ccd..0000000
--- a/src/Access/SwitchAccess.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\devel\Access\SwitchAccess.
- */
-
-namespace Drupal\devel\Access;
-
-use Drupal\Core\Access\CsrfTokenGenerator;
-use Drupal\Core\Routing\Access\AccessInterface as RoutingAccessInterface;
-use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-
-/**
- * Defines a special access checker for the user switch route.
- */
-class SwitchAccess implements RoutingAccessInterface {
-
-  /**
-   * CSRF token validator.
-   *
-   * @var \Drupal\Core\Access\CsrfTokenGenerator
-   */
-  protected $csrfToken;
-
-  /**
-   * Constructs a SwitchAccessCheck object.
-   *
-   * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
-   *   CSRF token validator.
-   */
-  public function __construct(CsrfTokenGenerator $csrf_token) {
-    $this->csrfToken = $csrf_token;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function appliesTo() {
-    return array('_devel_switch_user_access');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function access(Route $route, Request $request, AccountInterface $account) {
-    // Suppress notices when on other pages when menu system still checks access.
-    $name = $request->attributes->get('name');
-    $token = $request->query->get('token');
-    $destination = $request->query->get('destination');
-    return $account->hasPermission('switch users') && $this->csrfToken->validate($token, "devel/switch/$name", TRUE) ? static::ALLOW : static::DENY;
-  }
-
-}
diff --git a/src/Controller/DevelController.php b/src/Controller/DevelController.php
index da65deb..f4d19c5 100644
--- a/src/Controller/DevelController.php
+++ b/src/Controller/DevelController.php
@@ -275,40 +275,6 @@ class DevelController extends ControllerBase {
   }
 
   /**
-   * Switches to a different user.
-   *
-   * We don't call session_save_session() because we really want to change users.
-   * Usually unsafe!
-   *
-   * @param string $name
-   *   The username to switch to, or NULL to log out.
-   *
-   * @return \Symfony\Component\HttpFoundation\RedirectResponse
-   */
-  public function switchUser($name = NULL) {
-    // global $user;
-
-    // $module_handler = $this->moduleHandler();
-    // $session_manager = \Drupal::service('session_manager');
-
-    if ($uid = $this->currentUser()->id()) {
-      // @todo Is this needed?
-      // user_logout();
-    }
-    if (isset($name) && $account = user_load_by_name($name)) {
-      // See https://www.drupal.org/node/218104
-      $accountSwitcher = Drupal::service('account_switcher');
-      $accountSwitcher->switchTo(new UserSession(array('uid' => $account->getId())));
-
-      // Send her on her way.
-      $destination = $this->getDestinationArray();
-      $url = $this->getUrlGenerator()
-        ->generateFromPath($destination['destination'], array('absolute' => TRUE));
-      return new RedirectResponse($url);
-    }
-  }
-
-  /**
    * Explain query callback called by the AJAX link in the query log.
    */
   function queryLogExplain($request_id = NULL, $qid = NULL) {
diff --git a/src/Controller/SwitchUserController.php b/src/Controller/SwitchUserController.php
new file mode 100644
index 0000000..1a9af82
--- /dev/null
+++ b/src/Controller/SwitchUserController.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\devel\Controller\SwitchUserController.
+ */
+
+namespace Drupal\devel\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Session\AccountProxyInterface;
+use Drupal\Core\Session\SessionManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Session\Session;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+/**
+ * Controller for switch to another user account.
+ */
+class SwitchUserController extends ControllerBase {
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountProxyInterface
+   */
+  protected $account;
+
+  /**
+   * The user storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $userStorage;
+
+  /**
+   * The session manager service.
+   *
+   * @var \Drupal\Core\Session\SessionManagerInterface
+   */
+  protected $sessionManager;
+
+  /**
+   * The session.
+   *
+   * @var \Symfony\Component\HttpFoundation\Session\Session
+   */
+  protected $session;
+
+  /**
+   * Constructs a new SwitchUserController object
+   *
+   * @param \Drupal\Core\Session\AccountProxyInterface $account
+   *   The current user.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $user_storage
+   *   The user storage.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The user storage.
+   * @param \Drupal\Core\Session\SessionManagerInterface $session_manager
+   *   The session manager service.
+   * @param \Symfony\Component\HttpFoundation\Session\Session $session
+   *   The session.
+   */
+  public function __construct(AccountProxyInterface $account, EntityStorageInterface $user_storage, ModuleHandlerInterface $module_handler, SessionManagerInterface $session_manager, Session $session) {
+    $this->account = $account;
+    $this->userStorage = $user_storage;
+    $this->moduleHandler = $module_handler;
+    $this->sessionManager = $session_manager;
+    $this->session = $session;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('current_user'),
+      $container->get('entity.manager')->getStorage('user'),
+      $container->get('module_handler'),
+      $container->get('session_manager'),
+      $container->get('session')
+    );
+  }
+
+  /**
+   * Switches to a different user.
+   *
+   * We don't call session_save_session() because we really want to change users.
+   * Usually unsafe!
+   *
+   * @param string $name
+   *   The username to switch to, or NULL to log out.
+   *
+   * @return \Symfony\Component\HttpFoundation\RedirectResponse
+   *   A redirect response object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   */
+  public function switchUser($name = NULL) {
+    if (!isset($name) || (!$account = $this->userStorage->loadByProperties(['name' => $name]))) {
+      throw new AccessDeniedHttpException();
+    }
+    $account = reset($account);
+
+    // Call logout hooks when switching from original user.
+    $this->moduleHandler->invokeAll('user_logout', [$this->account]);
+
+    // Regenerate the session ID to prevent against session fixation attacks.
+    $this->sessionManager->regenerate();
+
+    // Based off masquarade module as:
+    // https://www.drupal.org/node/218104 doesn't stick and instead only
+    // keeps context until redirect.
+    $this->account->setAccount($account);
+    $this->session->set('uid', $account->id());
+
+    // Call all login hooks when switching to masquerading user.
+    $this->moduleHandler->invokeAll('user_login', [$account]);
+
+    return $this->redirect('<front>');
+  }
+
+}
diff --git a/src/Form/SwitchUserForm.php b/src/Form/SwitchUserForm.php
index 817293a..1d7490e 100644
--- a/src/Form/SwitchUserForm.php
+++ b/src/Form/SwitchUserForm.php
@@ -10,6 +10,8 @@ namespace Drupal\devel\Form;
 use Drupal\Core\Access\CsrfTokenGenerator;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Drupal\Core\Entity\EntityStorageInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -22,15 +24,26 @@ class SwitchUserForm extends FormBase {
    *
    * @var \Drupal\Core\Access\CsrfTokenGenerator
    */
-  protected $csrfTokenGenerator;
+  protected $csrfToken;
+
+  /**
+   * The user storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $userStorage;
 
   /**
    * Constructs a new SwitchUserForm object.
    *
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token_generator
+   *   The token generator.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $user_storage
+   *   The user storage.
    */
-  public function __construct(CsrfTokenGenerator $csrf_token_generator) {
-    $this->csrfTokenGenerator = $csrf_token_generator;
+  public function __construct(CsrfTokenGenerator $csrf_token_generator, EntityStorageInterface $user_storage) {
+    $this->csrfToken = $csrf_token_generator;
+    $this->userStorage = $user_storage;
   }
 
   /**
@@ -38,7 +51,8 @@ class SwitchUserForm extends FormBase {
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('csrf_token')
+      $container->get('csrf_token'),
+      $container->get('entity.manager')->getStorage('user')
     );
   }
 
@@ -53,19 +67,33 @@ class SwitchUserForm extends FormBase {
    * {@inheritdoc}
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
-    $form['username'] = array(
-      '#type' => 'textfield',
-      '#description' => t('Enter username'),
-      '#autocomplete_route_name' => 'user.autocomplete',
+    $form['autocomplete'] = [
+      '#type' => 'container',
+      '#attributes' => [
+        'class' => ['container-inline'],
+      ],
+    ];
+    $form['autocomplete']['userid'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => $this->t('Username'),
+      '#placeholder' => $this->t('Enter username'),
+      '#target_type' => 'user',
+      '#selection_settings' => [
+        'include_anonymous' => FALSE
+      ],
+      '#process_default_value' => FALSE,
       '#maxlength' => USERNAME_MAX_LENGTH,
-      '#size' => 16,
-    );
-    $form['submit'] = array(
+      '#title_display' => 'invisible',
+      '#required' => TRUE,
+      '#size' => '28',
+    ];
+
+    $form['autocomplete']['actions'] = ['#type' => 'actions'];
+    $form['autocomplete']['actions']['submit'] = [
       '#type' => 'submit',
-      '#value' => t('Switch'),
-      '#button_type' => 'primary',
-    );
-    $form['#attributes'] = array('class' => array('clearfix'));
+      '#value' => $this->t('Switch'),
+    ];
+
     return $form;
   }
 
@@ -73,8 +101,11 @@ class SwitchUserForm extends FormBase {
    * {@inheritdoc}
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
-    if (!$account = user_load_by_name($form_state->getValue('username'))) {
-      $form_state->setErrorByName('username', t('Username not found'));
+    if (!$account = $this->userStorage->load($form_state->getValue('userid'))) {
+      $form_state->setErrorByName('username', $this->t('Username not found'));
+    }
+    else {
+      $form_state->setValue('username', $account->getUsername());
     }
   }
 
@@ -82,8 +113,13 @@ class SwitchUserForm extends FormBase {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $name = $form_state->getValue('username');
-    $path = 'devel/switch/' . $name;
-    $form_state->setRedirect('devel.switch', array('name' => $name), array('query' => array( 'destination' => '', 'token' => $this->csrfTokenGenerator->get($path))));
+    // We cannot rely on automatic token creation, since the csrf seed changes
+    // after the redirect and the generated token is not more valid.
+    // TODO find another way to do this.
+    $url = Url::fromRoute('devel.switch', ['name' => $form_state->getValue('username')]);
+    $url->setOption('query', ['token' => $this->csrfToken->get($url->getInternalPath())]);
+
+    $form_state->setRedirectUrl($url);
   }
+
 }
diff --git a/src/Plugin/Block/DevelSwitchUser.php b/src/Plugin/Block/DevelSwitchUser.php
deleted file mode 100644
index 1fceb7a..0000000
--- a/src/Plugin/Block/DevelSwitchUser.php
+++ /dev/null
@@ -1,268 +0,0 @@
-<?php
-
-/**
-* @file
-* Contains \Drupal\devel\Plugin\Block\DevelSwitchUser.
-*/
-
-namespace Drupal\devel\Plugin\Block;
-
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Access\CsrfTokenGenerator;
-use Drupal\Core\Block\BlockBase;
-use Drupal\Core\Block\Annotation\Block;
-use Drupal\Core\Annotation\Translation;
-use Drupal\Core\Form\FormBuilderInterface;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Routing\RedirectDestinationInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\Session\AccountProxyInterface;
-use Drupal\Core\Session\AnonymousUserSession;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Provides a block for switching users.
- *
- * @Block(
- *   id = "devel_switch_user",
- *   admin_label = @Translation("Switch user"),
- *   category = @Translation("Forms")
- * )
- */
-class DevelSwitchUser extends BlockBase implements ContainerFactoryPluginInterface {
-
-  /**
-   * The csrf token generator.
-   *
-   * @var \Drupal\Core\Access\CsrfTokenGenerator
-   */
-  protected $csrfTokenGenerator;
-
-  /**
-   * The FormBuilder object.
-   *
-   * @var \Drupal\Core\Form\FormBuilderInterface
-   */
-  protected $formBuilder;
-
-  /**
-   * The Cuurent User object.
-   *
-   * @var
-   */
-  protected $currentUser;
-
-  /**
-   * The redirect destination service.
-   *
-   * @var \Drupal\Core\Routing\RedirectDestinationInterface
-   */
-  protected $redirectDestination;
-  /**
-   * Constructs a new DevelSwitchUser object.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token_generator
-   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
-   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, CsrfTokenGenerator $csrf_token_generator, FormBuilderInterface $form_builder, AccountProxyInterface $current_user, RedirectDestinationInterface $redirect_destination) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->csrfTokenGenerator = $csrf_token_generator;
-    $this->formBuilder = $form_builder;
-    $this->currentUser = $current_user;
-    $this->redirectDestination = $redirect_destination;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('csrf_token'),
-      $container->get('form_builder'),
-      $container->get('current_user'),
-      $container->get('redirect.destination')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration() {
-    // By default, the block will contain 12 users.
-    return array(
-      'list_size' => 12,
-      'include_anon' => TRUE,
-      'show_form' => TRUE,
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function blockForm($form, FormStateInterface $form_state) {
-    $anon = new AnonymousUserSession();
-    $form['list_size'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Number of users to display in the list'),
-      '#default_value' => $this->configuration['list_size'],
-      '#size' => '3',
-      '#maxlength' => '4',
-    );
-    $form['include_anon'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Include %anonymous', array('%anonymous' => $anon->getUsername())),
-      '#default_value' => $this->configuration['include_anon'],
-    );
-    $form['show_form'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Allow entering any user name'),
-      '#default_value' => $this->configuration['show_form'],
-    );
-    return $form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function blockSubmit($form, FormStateInterface $form_state) {
-    $this->configuration['list_size'] = $form_state->getValue('list_size');
-    $this->configuration['include_anon'] = $form_state->getValue('include_anon');
-    $this->configuration['show_form'] = $form_state->getValue('show_form');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function blockAccess(AccountInterface $account) {
-    return AccessResult::allowedIfHasPermission($account, 'switch users');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isCacheable() {
-    // Perhaps this can be improved. Low priority.
-    return FALSE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function build() {
-    $links = $this->switchUserList();
-    if (!empty($links)) {
-      $build = array(
-        'devel_links' => array('#theme' => 'links', '#links' => $links),
-        '#attached' => array(
-          'css' => array(
-            drupal_get_path('module', 'devel') . '/css/devel.css')
-          )
-      );
-      if ($this->configuration['show_form']) {
-        $build['devel_form'] = $this->formBuilder->getForm('\Drupal\devel\Form\SwitchUserForm');
-      }
-      return $build;
-    }
-  }
-
-  /**
-   * Provides the Switch user list.
-   */
-  public function switchUserList() {
-    $list_size = $this->configuration['list_size'];
-    $include_anon = $this->configuration['include_anon'];
-    $anon = new AnonymousUserSession();
-    $links = array();
-    if ($this->currentUser->hasPermission('switch users')) {
-      if ($include_anon) {
-        --$list_size;
-      }
-      $dest = $this->redirectDestination->getAsArray();
-      // Try to find at least $list_size users that can switch.
-      // Inactive users are omitted from all of the following db selects.
-      $roles = user_roles(TRUE, 'switch users');
-      $query = db_select('users', 'u');
-      $query->join('users_field_data', 'ufd');
-      $query->addField('u', 'uid');
-      $query->addField('ufd', 'access');
-      $query->distinct();
-      $query->condition('u.uid', 0, '>');
-      $query->condition('ufd.status', 0, '>');
-      $query->orderBy('ufd.access', 'DESC');
-      $query->range(0, $list_size);
-
-      if (!isset($roles[DRUPAL_AUTHENTICATED_RID])) {
-        $query->leftJoin('users_roles', 'r', 'u.uid = r.uid');
-        $or_condition = db_or();
-        $or_condition->condition('u.uid', 1);
-        if (!empty($roles)) {
-          $or_condition->condition('r.rid', array_keys($roles), 'IN');
-        }
-        $query->condition($or_condition);
-      }
-
-      $uids = $query->execute()->fetchCol();
-      $accounts = user_load_multiple($uids);
-
-      foreach ($accounts as $account) {
-        $path = 'devel/switch/' . $account->name->value;
-        $links[$account->id()] = array(
-          'title' => user_format_name($account),
-          'href' => $path,
-          'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)),
-          'attributes' => array('title' => t('This user can switch back.')),
-          'html' => TRUE,
-          'last_access' => $account->access->value,
-        );
-      }
-      $num_links = count($links);
-      if ($num_links < $list_size) {
-        // If we don't have enough, add distinct uids until we hit $list_size.
-        $uids = db_query_range('SELECT u.uid FROM {users} u INNER JOIN {users_field_data} ufd WHERE u.uid > 0 AND u.uid NOT IN (:uids) AND ufd.status > 0 ORDER BY ufd.access DESC', 0, $list_size - $num_links, array(':uids' => array_keys($links)))->fetchCol();
-        $accounts = user_load_multiple($uids);
-        foreach ($accounts as $account) {
-          $path = 'devel/switch/' . $account->name->value;
-          $links[$account->id()] = array(
-            'title' => user_format_name($account),
-            'href' => $path,
-            'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)),
-            'attributes' => array('title' => t('Caution: this user will be unable to switch back.')),
-            'last_access' => $account->access->value,
-          );
-        }
-        uasort($links, '_devel_switch_user_list_cmp');
-      }
-      if ($include_anon) {
-        $path = 'devel/switch';
-        $link = array(
-          'title' => $anon->getUsername(),
-          'href' => $path,
-          'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)),
-          'attributes' => array('title' => t('Caution: the anonymous user will be unable to switch back.')),
-        );
-        if ($this->currentUser->hasPermission('switch users')) {
-          $link['title'] = SafeMarkup::placeholder($link['title']);
-          $link['attributes'] = array('title' => t('This user can switch back.'));
-          $link['html'] = TRUE;
-        }
-        $links[$anon->id()] = $link;
-      }
-    }
-    if (array_key_exists($uid = $this->currentUser->id(), $links)) {
-      $links[$uid]['title'] = '<strong>' . $links[$uid]['title'] . '</strong>';
-    }
-    return $links;
-  }
-}
diff --git a/src/Plugin/Block/SwitchUserBlock.php b/src/Plugin/Block/SwitchUserBlock.php
new file mode 100644
index 0000000..afaffa4
--- /dev/null
+++ b/src/Plugin/Block/SwitchUserBlock.php
@@ -0,0 +1,293 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\devel\Plugin\Block\SwitchUserBlock.
+ */
+
+namespace Drupal\devel\Plugin\Block;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Routing\RedirectDestinationInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Session\AnonymousUserSession;
+use Drupal\Core\Url;
+use Drupal\user\Entity\Role;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a block for switching users.
+ *
+ * @Block(
+ *   id = "devel_switch_user",
+ *   admin_label = @Translation("Switch user"),
+ *   category = @Translation("Forms")
+ * )
+ */
+class SwitchUserBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The FormBuilder object.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
+
+  /**
+   * The Current User object.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * The user storage.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageInterface
+   */
+  protected $userStorage;
+
+  /**
+   * The redirect destination service.
+   *
+   * @var \Drupal\Core\Routing\RedirectDestinationInterface
+   */
+  protected $redirectDestination;
+
+  /**
+   * Constructs a new SwitchUserBlock object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   Current user.
+   * @param \Drupal\Core\Entity\EntityStorageInterface $user_storage
+   *   The user storage.
+   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+   *   The form builder service.
+   * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
+   *   The redirect destination service.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountInterface $current_user, EntityStorageInterface $user_storage, FormBuilderInterface $form_builder, RedirectDestinationInterface $redirect_destination) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->formBuilder = $form_builder;
+    $this->currentUser = $current_user;
+    $this->userStorage = $user_storage;
+    $this->redirectDestination = $redirect_destination;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('current_user'),
+      $container->get('entity.manager')->getStorage('user'),
+      $container->get('form_builder'),
+      $container->get('redirect.destination')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'list_size' => 12,
+      'include_anon' => FALSE,
+      'show_form' => TRUE,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockAccess(AccountInterface $account) {
+    return AccessResult::allowedIfHasPermission($account, 'switch users');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockForm($form, FormStateInterface $form_state) {
+    $anononymous = new AnonymousUserSession();
+    $form['list_size'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Number of users to display in the list'),
+      '#default_value' => $this->configuration['list_size'],
+      '#min' => 1,
+      '#max' => 50,
+    ];
+    $form['include_anon'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Include %anonymous', ['%anonymous' => $anononymous->getUsername()]),
+      '#default_value' => $this->configuration['include_anon'],
+    ];
+    $form['show_form'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Allow entering any user name'),
+      '#default_value' => $this->configuration['show_form'],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function blockSubmit($form, FormStateInterface $form_state) {
+    $this->configuration['list_size'] = $form_state->getValue('list_size');
+    $this->configuration['include_anon'] = $form_state->getValue('include_anon');
+    $this->configuration['show_form'] = $form_state->getValue('show_form');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    return 0;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    $build = [];
+
+    if ($accounts = $this->getUsers()) {
+
+      $build['devel_links'] = [
+        '#theme' => 'links',
+        '#links' => $this->buildUserList($accounts),
+        '#attached' => ['library' => ['devel/devel']],
+      ];
+
+      if ($this->configuration['show_form']) {
+        $build['devel_form'] = $this->formBuilder->getForm('\Drupal\devel\Form\SwitchUserForm');
+      }
+    }
+
+    return $build;
+  }
+
+  /**
+   * Provides the Switch user list.
+   *
+   * Inactive users are omitted from all of the following db selects. Users
+   * with 'switch users' permission are prioritized.
+   *
+   * @return \Drupal\user\Entity\User[]
+   *   List of account to be used for the switch.
+   */
+  protected function getUsers() {
+    $list_size = $this->configuration['list_size'];
+    $include_anonymous = $this->configuration['include_anon'];
+
+    $list_size = $include_anonymous ? $list_size - 1 : $list_size;
+
+    // Users with 'switch users' permission are prioritized so
+    // we try to load first users with this permission.
+    $query = $this->userStorage->getQuery()
+      ->condition('uid', 0, '>')
+      ->condition('status', 0, '>')
+      ->sort('access', 'DESC')
+      ->range(0, $list_size);
+
+    $roles = user_roles(TRUE, 'switch users');
+
+    if (!isset($roles[Role::AUTHENTICATED_ID])) {
+      $query->condition('roles', array_keys($roles), 'IN');
+    }
+
+    $user_ids = $query->execute();
+
+    // If we don't have enough users with 'switch users' permission, add
+    // uids until we hit $list_size.
+    if (count($user_ids) < $list_size) {
+      $users = $this->userStorage->getQuery()
+        ->condition('uid', 0, '>')
+        ->condition('status', 0, '>')
+        ->condition('uid', array_keys($user_ids), 'NOT IN')
+        ->sort('access', 'DESC')
+        ->range(0, $list_size - count($user_ids))
+        ->execute();
+
+      $user_ids += $users;
+    }
+
+    $accounts = $this->userStorage->loadMultiple($user_ids);
+
+    if ($include_anonymous) {
+      $anonymous = new AnonymousUserSession();
+      $accounts[$anonymous->id()] = $anonymous;
+    }
+
+    uasort($accounts, 'static::sortUserList');
+
+    return $accounts;
+  }
+
+  /**
+   * @param \Drupal\user\Entity\User[] $accounts
+   *
+   * @return array
+   */
+  protected function buildUserList(array $accounts) {
+    $links = [];
+
+    foreach ($accounts as $account) {
+      $links[$account->id()] = [
+        'title' => $account->getUsername(),
+        'url' => Url::fromRoute('devel.switch', ['name' => $account->getUsername()]),
+        'query' => $this->redirectDestination->getAsArray(),
+        'attributes' => [
+          'title' => $account->hasPermission('switch users') ? $this->t('This user can switch back.') : $this->t('Caution: this user will be unable to switch back.'),
+        ],
+        'last_access' => $account->getLastAccessedTime(),
+      ];
+
+      if ($account->isAnonymous()) {
+        $links[$account->id()]['url'] = Url::fromRoute('user.logout');
+      }
+
+      if ($this->currentUser->id() === $account->id()) {
+        $links[$account->id()]['title'] = SafeMarkup::format('<strong>%user</strong>', ['%user' => $account->getUsername()]);
+      }
+    }
+
+    return $links;
+  }
+
+  /**
+   * Helper callback for uasort() to sort accounts by last access.
+   */
+  public static function sortUserList(AccountInterface $a, AccountInterface $b) {
+    $a_access = $a->getLastAccessedTime();
+    $b_access = $b->getLastAccessedTime();
+
+    if ($a_access === $b_access) {
+      return 0;
+    }
+
+    // User never access to site.
+    if($a_access === '0') {
+      return 1;
+    }
+
+    return ($a_access > $b_access) ? -1 : 1;
+  }
+
+}
