diff --git a/core/modules/basic_auth/basic_auth.services.yml b/core/modules/basic_auth/basic_auth.services.yml
index be85ccc..d985d0f 100644
--- a/core/modules/basic_auth/basic_auth.services.yml
+++ b/core/modules/basic_auth/basic_auth.services.yml
@@ -1,7 +1,7 @@
 services:
   basic_auth.authentication.basic_auth:
     class: Drupal\basic_auth\Authentication\Provider\BasicAuth
-    arguments: ['@config.factory', '@user.auth', '@flood', '@entity.manager']
+    arguments: ['@user.auth_flood', '@config.factory']
     tags:
       - { name: authentication_provider, provider_id: 'basic_auth', priority: 100 }
   basic_auth.page_cache_request_policy.disallow_basic_auth_requests:
diff --git a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
index eac482c..996c15f 100644
--- a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
+++ b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
@@ -3,12 +3,10 @@
 namespace Drupal\basic_auth\Authentication\Provider;
 
 use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Core\Authentication\AuthenticationProviderInterface;
 use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface;
+use Drupal\Core\Authentication\AuthenticationProviderInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Flood\FloodInterface;
-use Drupal\user\UserAuthInterface;
+use Drupal\user\Authentication\UserAuthFlood;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
 
@@ -17,6 +15,8 @@
  */
 class BasicAuth implements AuthenticationProviderInterface, AuthenticationProviderChallengeInterface {
 
+  protected $userAuthFlood;
+
   /**
    * The config factory.
    *
@@ -25,43 +25,13 @@ class BasicAuth implements AuthenticationProviderInterface, AuthenticationProvid
   protected $configFactory;
 
   /**
-   * The user auth service.
-   *
-   * @var \Drupal\user\UserAuthInterface
-   */
-  protected $userAuth;
-
-  /**
-   * The flood service.
-   *
-   * @var \Drupal\Core\Flood\FloodInterface
-   */
-  protected $flood;
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
    * Constructs a HTTP basic authentication provider object.
    *
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The config factory.
-   * @param \Drupal\user\UserAuthInterface $user_auth
-   *   The user authentication service.
-   * @param \Drupal\Core\Flood\FloodInterface $flood
-   *   The flood service.
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager service.
+   * @param $userAuthFlood
    */
-  public function __construct(ConfigFactoryInterface $config_factory, UserAuthInterface $user_auth, FloodInterface $flood, EntityManagerInterface $entity_manager) {
-    $this->configFactory = $config_factory;
-    $this->userAuth = $user_auth;
-    $this->flood = $flood;
-    $this->entityManager = $entity_manager;
+  public function __construct(UserAuthFlood $userAuthFlood, ConfigFactoryInterface $configFactory) {
+    $this->userAuthFlood = $userAuthFlood;
+    $this->configFactory = $configFactory;
   }
 
   /**
@@ -77,49 +47,14 @@ public function applies(Request $request) {
    * {@inheritdoc}
    */
   public function authenticate(Request $request) {
-    $flood_config = $this->configFactory->get('user.flood');
     $username = $request->headers->get('PHP_AUTH_USER');
     $password = $request->headers->get('PHP_AUTH_PW');
-    // Flood protection: this is very similar to the user login form code.
-    // @see \Drupal\user\Form\UserLoginForm::validateAuthentication()
-    // Do not allow any login from the current user's IP if the limit has been
-    // reached. Default is 50 failed attempts allowed in one hour. This is
-    // independent of the per-user limit to catch attempts from one IP to log
-    // in to many different user accounts.  We have a reasonably high limit
-    // since there may be only one apparent IP for all users at an institution.
-    if ($this->flood->isAllowed('basic_auth.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
-      $accounts = $this->entityManager->getStorage('user')->loadByProperties(array('name' => $username, 'status' => 1));
-      $account = reset($accounts);
-      if ($account) {
-        if ($flood_config->get('uid_only')) {
-          // Register flood events based on the uid only, so they apply for any
-          // IP address. This is the most secure option.
-          $identifier = $account->id();
-        }
-        else {
-          // The default identifier is a combination of uid and IP address. This
-          // is less secure but more resistant to denial-of-service attacks that
-          // could lock out all users with public user names.
-          $identifier = $account->id() . '-' . $request->getClientIP();
-        }
-        // Don't allow login if the limit for this user has been reached.
-        // Default is to allow 5 failed attempts every 6 hours.
-        if ($this->flood->isAllowed('basic_auth.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
-          $uid = $this->userAuth->authenticate($username, $password);
-          if ($uid) {
-            $this->flood->clear('basic_auth.failed_login_user', $identifier);
-            return $this->entityManager->getStorage('user')->load($uid);
-          }
-          else {
-            // Register a per-user failed login event.
-            $this->flood->register('basic_auth.failed_login_user', $flood_config->get('user_window'), $identifier);
-          }
-        }
-      }
+
+    $result = $this->userAuthFlood->authenticate($username, $password, 'basic_auth', $request);
+    if ($result->success) {
+      return $result->user;
     }
-    // Always register an IP-based failed login event.
-    $this->flood->register('basic_auth.failed_login_ip', $flood_config->get('ip_window'));
-    return [];
+    return FALSE;
   }
 
   /**
diff --git a/core/modules/user/src/Authentication/AuthResult.php b/core/modules/user/src/Authentication/AuthResult.php
new file mode 100644
index 0000000..edfdb24
--- /dev/null
+++ b/core/modules/user/src/Authentication/AuthResult.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\user\Authentication;
+
+/**
+ * Represents the result after an authentication attempt.
+ */
+class AuthResult {
+
+  /**
+   * TRUE if the authentication was successful.
+   *
+   * @var bool
+   */
+  public $success = FALSE;
+
+  /**
+   * The loaded user if the authentication was successful.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  public $user;
+
+  /**
+   * The translated error message in case the authentication failed.
+   *
+   * @var string|\Drupal\Component\Render\MarkupInterface
+   */
+  public $error;
+
+  /**
+   * Indicates if the authentication attempt was blocked by flood control.
+   *
+   * @var bool
+   */
+  public $floodControlTriggered = FALSE;
+
+}
diff --git a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php b/core/modules/user/src/Authentication/UserAuthFlood.php
similarity index 52%
copy from core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
copy to core/modules/user/src/Authentication/UserAuthFlood.php
index eac482c..94461c6 100644
--- a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
+++ b/core/modules/user/src/Authentication/UserAuthFlood.php
@@ -1,21 +1,18 @@
 <?php
 
-namespace Drupal\basic_auth\Authentication\Provider;
+namespace Drupal\user\Authentication;
 
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Core\Authentication\AuthenticationProviderInterface;
-use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Flood\FloodInterface;
 use Drupal\user\UserAuthInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
 
 /**
- * HTTP Basic authentication provider.
+ * Authenticates credentials and applies flood protection.
+ *
+ * Also checks if a user is blocked.
  */
-class BasicAuth implements AuthenticationProviderInterface, AuthenticationProviderChallengeInterface {
+class UserAuthFlood {
 
   /**
    * The config factory.
@@ -46,7 +43,7 @@ class BasicAuth implements AuthenticationProviderInterface, AuthenticationProvid
   protected $entityManager;
 
   /**
-   * Constructs a HTTP basic authentication provider object.
+   * Constructor.
    *
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The config factory.
@@ -65,30 +62,30 @@ public function __construct(ConfigFactoryInterface $config_factory, UserAuthInte
   }
 
   /**
-   * {@inheritdoc}
-   */
-  public function applies(Request $request) {
-    $username = $request->headers->get('PHP_AUTH_USER');
-    $password = $request->headers->get('PHP_AUTH_PW');
-    return isset($username) && isset($password);
-  }
-
-  /**
-   * {@inheritdoc}
+   * Authenticates the given credentials and applies flood protection.
+   *
+   * @param string $username
+   * @param string $password
+   * @param string $channel
+   *   The source of the authentication request. Example: 'form', 'http_login',
+   *   'basic_auth'.
+   * @param \Symfony\Component\HttpFoundation\Request|null $request
+   *   The request object.
+   *
+   * @return AuthResult
+   *   An authentication result object providing status information.
    */
-  public function authenticate(Request $request) {
+  public function authenticate($username, $password, $channel, $request) {
+    $auth_result = new AuthResult();
     $flood_config = $this->configFactory->get('user.flood');
-    $username = $request->headers->get('PHP_AUTH_USER');
-    $password = $request->headers->get('PHP_AUTH_PW');
-    // Flood protection: this is very similar to the user login form code.
-    // @see \Drupal\user\Form\UserLoginForm::validateAuthentication()
+
     // Do not allow any login from the current user's IP if the limit has been
     // reached. Default is 50 failed attempts allowed in one hour. This is
     // independent of the per-user limit to catch attempts from one IP to log
     // in to many different user accounts.  We have a reasonably high limit
     // since there may be only one apparent IP for all users at an institution.
-    if ($this->flood->isAllowed('basic_auth.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
-      $accounts = $this->entityManager->getStorage('user')->loadByProperties(array('name' => $username, 'status' => 1));
+    if ($this->flood->isAllowed("$channel.failed_login_ip", $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
+      $accounts = $this->entityManager->getStorage('user')->loadByProperties(array('name' => $username));
       $account = reset($accounts);
       if ($account) {
         if ($flood_config->get('uid_only')) {
@@ -102,35 +99,54 @@ public function authenticate(Request $request) {
           // could lock out all users with public user names.
           $identifier = $account->id() . '-' . $request->getClientIP();
         }
+        if ($account->isBlocked()) {
+          $auth_result->error = t('The user has not been activated or is blocked.');
+        }
         // Don't allow login if the limit for this user has been reached.
         // Default is to allow 5 failed attempts every 6 hours.
-        if ($this->flood->isAllowed('basic_auth.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
+        elseif ($this->flood->isAllowed("$channel.failed_login_user", $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
           $uid = $this->userAuth->authenticate($username, $password);
           if ($uid) {
-            $this->flood->clear('basic_auth.failed_login_user', $identifier);
-            return $this->entityManager->getStorage('user')->load($uid);
+            $this->flood->clear("$channel.failed_login_user", $identifier);
+
+            $auth_result->success = TRUE;
+            $auth_result->user = $this->entityManager->getStorage('user')->load($uid);
+
+            return $auth_result;
           }
           else {
             // Register a per-user failed login event.
-            $this->flood->register('basic_auth.failed_login_user', $flood_config->get('user_window'), $identifier);
+            $this->flood->register("$channel.failed_login_user", $flood_config->get('user_window'), $identifier);
+
+            $auth_result->error = t('Sorry, unrecognized username or password.');
           }
         }
+        else {
+          if ($flood_config->get('uid_only')) {
+            $auth_result->error = t('There have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', [
+              '@count' => $flood_config->get('user_limit'),
+            ]);
+          }
+          else {
+            $auth_result->error = t('Too many failed login attempts from your IP address. This IP address is temporarily blocked.');
+          }
+
+          $auth_result->floodControlTriggered = TRUE;
+        }
+      }
+      else {
+        $auth_result->error = t('Sorry, unrecognized username or password.');
       }
     }
+    else {
+      $auth_result->error = t('Too many failed login attempts from your IP address. This IP address is temporarily blocked.');
+      $auth_result->floodControlTriggered = TRUE;
+    }
+
     // Always register an IP-based failed login event.
-    $this->flood->register('basic_auth.failed_login_ip', $flood_config->get('ip_window'));
-    return [];
-  }
+    $this->flood->register("$channel.failed_login_ip", $flood_config->get('ip_window'));
 
-  /**
-   * {@inheritdoc}
-   */
-  public function challengeException(Request $request, \Exception $previous) {
-    $site_name = $this->configFactory->get('system.site')->get('name');
-    $challenge = SafeMarkup::format('Basic realm="@realm"', array(
-      '@realm' => !empty($site_name) ? $site_name : 'Access restricted',
-    ));
-    return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
+    return $auth_result;
   }
 
 }
diff --git a/core/modules/user/src/Controller/UserAuthenticationController.php b/core/modules/user/src/Controller/UserAuthenticationController.php
index 569a121..3ca64ac 100644
--- a/core/modules/user/src/Controller/UserAuthenticationController.php
+++ b/core/modules/user/src/Controller/UserAuthenticationController.php
@@ -5,11 +5,9 @@
 use Drupal\Core\Access\CsrfTokenGenerator;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\Flood\FloodInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
-use Drupal\user\UserAuthInterface;
+use Drupal\user\Authentication\UserAuthFlood;
 use Drupal\user\UserInterface;
-use Drupal\user\UserStorageInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
@@ -38,18 +36,11 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
   const LOGGED_OUT = 0;
 
   /**
-   * The flood controller.
+   * The user authentication and flood control service.
    *
-   * @var \Drupal\Core\Flood\FloodInterface
+   * @var \Drupal\user\Authentication\UserAuthFlood
    */
-  protected $flood;
-
-  /**
-   * The user storage.
-   *
-   * @var \Drupal\user\UserStorageInterface
-   */
-  protected $userStorage;
+  protected $userAuthFlood;
 
   /**
    * The CSRF token generator.
@@ -59,13 +50,6 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
   protected $csrfToken;
 
   /**
-   * The user authentication.
-   *
-   * @var \Drupal\user\UserAuthInterface
-   */
-  protected $userAuth;
-
-  /**
    * The route provider.
    *
    * @var \Drupal\Core\Routing\RouteProviderInterface
@@ -89,14 +73,10 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
   /**
    * Constructs a new UserAuthenticationController object.
    *
-   * @param \Drupal\Core\Flood\FloodInterface $flood
-   *   The flood controller.
-   * @param \Drupal\user\UserStorageInterface $user_storage
-   *   The user storage.
+   * @param \Drupal\user\Authentication\UserAuthFlood $userAuthFlood
+   *   The user authentication and flood control service.
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token generator.
-   * @param \Drupal\user\UserAuthInterface $user_auth
-   *   The user authentication.
    * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
    *   The route provider.
    * @param \Symfony\Component\Serializer\Serializer $serializer
@@ -104,11 +84,9 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
    * @param array $serializer_formats
    *   The available serialization formats.
    */
-  public function __construct(FloodInterface $flood, UserStorageInterface $user_storage, CsrfTokenGenerator $csrf_token, UserAuthInterface $user_auth, RouteProviderInterface $route_provider, Serializer $serializer, array $serializer_formats) {
-    $this->flood = $flood;
-    $this->userStorage = $user_storage;
+  public function __construct(UserAuthFlood $userAuthFlood, CsrfTokenGenerator $csrf_token, RouteProviderInterface $route_provider, Serializer $serializer, array $serializer_formats) {
+    $this->userAuthFlood = $userAuthFlood;
     $this->csrfToken = $csrf_token;
-    $this->userAuth = $user_auth;
     $this->serializer = $serializer;
     $this->serializerFormats = $serializer_formats;
     $this->routeProvider = $route_provider;
@@ -129,10 +107,8 @@ public static function create(ContainerInterface $container) {
     }
 
     return new static(
-      $container->get('flood'),
-      $container->get('entity_type.manager')->getStorage('user'),
+      $container->get('user.auth_flood'),
       $container->get('csrf_token'),
-      $container->get('user.auth'),
       $container->get('router.route_provider'),
       $serializer,
       $formats
@@ -164,47 +140,38 @@ public function login(Request $request) {
       throw new BadRequestHttpException('Missing credentials.pass.');
     }
 
-    $this->floodControl($request, $credentials['name']);
-
-    if ($this->userIsBlocked($credentials['name'])) {
-      throw new BadRequestHttpException('The user has not been activated or is blocked.');
+    $auth_result = $this->userAuthFlood->authenticate($credentials['name'], $credentials['pass'], 'http_login', $request);
+    if (!$auth_result->success) {
+      if ($auth_result->floodControlTriggered) {
+        throw new AccessDeniedHttpException($auth_result->error);
+      }
+      throw new BadRequestHttpException($auth_result->error);
     }
 
-    if ($uid = $this->userAuth->authenticate($credentials['name'], $credentials['pass'])) {
-      $this->flood->clear('user.http_login', $this->getLoginFloodIdentifier($request, $credentials['name']));
-      /** @var \Drupal\user\UserInterface $user */
-      $user = $this->userStorage->load($uid);
-      $this->userLoginFinalize($user);
+    $user = $auth_result->user;
 
-      // Send basic metadata about the logged in user.
-      $response_data = [];
-      if ($user->get('uid')->access('view', $user)) {
-        $response_data['current_user']['uid'] = $user->id();
-      }
-      if ($user->get('roles')->access('view', $user)) {
-        $response_data['current_user']['roles'] = $user->getRoles();
-      }
-      if ($user->get('name')->access('view', $user)) {
-        $response_data['current_user']['name'] = $user->getAccountName();
-      }
-      $response_data['csrf_token'] = $this->csrfToken->get('rest');
-
-      $logout_route = $this->routeProvider->getRouteByName('user.logout.http');
-      // Trim '/' off path to match \Drupal\Core\Access\CsrfAccessCheck.
-      $logout_path = ltrim($logout_route->getPath(), '/');
-      $response_data['logout_token'] = $this->csrfToken->get($logout_path);
+    $this->userLoginFinalize($user);
 
-      $encoded_response_data = $this->serializer->encode($response_data, $format);
-      return new Response($encoded_response_data);
+    // Send basic metadata about the logged in user.
+    $response_data = [];
+    if ($user->get('uid')->access('view', $user)) {
+      $response_data['current_user']['uid'] = $user->id();
     }
-
-    $flood_config = $this->config('user.flood');
-    if ($identifier = $this->getLoginFloodIdentifier($request, $credentials['name'])) {
-      $this->flood->register('user.http_login', $flood_config->get('user_window'), $identifier);
+    if ($user->get('roles')->access('view', $user)) {
+      $response_data['current_user']['roles'] = $user->getRoles();
+    }
+    if ($user->get('name')->access('view', $user)) {
+      $response_data['current_user']['name'] = $user->getAccountName();
     }
-    // Always register an IP-based failed login event.
-    $this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
-    throw new BadRequestHttpException('Sorry, unrecognized username or password.');
+    $response_data['csrf_token'] = $this->csrfToken->get('rest');
+
+    $logout_route = $this->routeProvider->getRouteByName('user.logout.http');
+    // Trim '/' off path to match \Drupal\Core\Access\CsrfAccessCheck.
+    $logout_path = ltrim($logout_route->getPath(), '/');
+    $response_data['logout_token'] = $this->csrfToken->get($logout_path);
+
+    $encoded_response_data = $this->serializer->encode($response_data, $format);
+    return new Response($encoded_response_data);
   }
 
   /**
@@ -282,64 +249,4 @@ protected function getRequestFormat(Request $request) {
     return $format;
   }
 
-  /**
-   * Enforces flood control for the current login request.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The current request.
-   * @param string $username
-   *   The user name sent for login credentials.
-   */
-  protected function floodControl(Request $request, $username) {
-    $flood_config = $this->config('user.flood');
-    if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
-      throw new AccessDeniedHttpException('Access is blocked because of IP based flood prevention.', NULL, Response::HTTP_TOO_MANY_REQUESTS);
-    }
-
-    if ($identifier = $this->getLoginFloodIdentifier($request, $username)) {
-      // Don't allow login if the limit for this user has been reached.
-      // Default is to allow 5 failed attempts every 6 hours.
-      if (!$this->flood->isAllowed('user.http_login', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
-        if ($flood_config->get('uid_only')) {
-          $error_message = sprintf('There have been more than %s failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', $flood_config->get('user_limit'));
-        }
-        else {
-          $error_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.';
-        }
-        throw new AccessDeniedHttpException($error_message, NULL, Response::HTTP_TOO_MANY_REQUESTS);
-      }
-    }
-  }
-
-  /**
-   * Gets the login identifier for user login flood control.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The current request.
-   * @param string $username
-   *   The username supplied in login credentials.
-   *
-   * @return string
-   *   The login identifier or if the user does not exist an empty string.
-   */
-  protected function getLoginFloodIdentifier(Request $request, $username) {
-    $flood_config = $this->config('user.flood');
-    $accounts = $this->userStorage->loadByProperties(['name' => $username, 'status' => 1]);
-    if ($account = reset($accounts)) {
-      if ($flood_config->get('uid_only')) {
-        // Register flood events based on the uid only, so they apply for any
-        // IP address. This is the most secure option.
-        $identifier = $account->id();
-      }
-      else {
-        // The default identifier is a combination of uid and IP address. This
-        // is less secure but more resistant to denial-of-service attacks that
-        // could lock out all users with public user names.
-        $identifier = $account->id() . '-' . $request->getClientIp();
-      }
-      return $identifier;
-    }
-    return '';
-  }
-
 }
diff --git a/core/modules/user/tests/src/Functional/UserLoginHttpTest.php b/core/modules/user/tests/src/Functional/UserLoginHttpTest.php
index 3565441..33f27ac 100644
--- a/core/modules/user/tests/src/Functional/UserLoginHttpTest.php
+++ b/core/modules/user/tests/src/Functional/UserLoginHttpTest.php
@@ -234,7 +234,7 @@ public function testGlobalLoginFloodControl() {
 
     // IP limit has reached to its limit. Even valid user credentials will fail.
     $response = $this->loginRequest($user->getUsername(), $user->passRaw);
-    $this->assertHttpResponseWithMessage($response, '403', 'Access is blocked because of IP based flood prevention.');
+    $this->assertHttpResponseWithMessage($response, '403', 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.');
   }
 
   /**
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index c273cb3..7866033 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -48,6 +48,9 @@ services:
   user.auth:
     class: Drupal\user\UserAuth
     arguments: ['@entity.manager', '@password']
+  user.auth_flood:
+    class: Drupal\user\Authentication\UserAuthFlood
+    arguments: ['@config.factory', '@user.auth', '@flood', '@entity.manager']
   user.private_tempstore:
     class: Drupal\user\PrivateTempStoreFactory
     arguments: ['@keyvalue.expirable', '@lock', '@current_user', '@request_stack', '%user.tempstore.expire%']
