diff --git a/access_code.install b/access_code.install
index 37cf927cb7c0f01ad9ff393db72af2ca90e5e4fb..2e28872f2d1f0330755a0193f5d2668d2aaca1a7 100644
--- a/access_code.install
+++ b/access_code.install
@@ -48,5 +48,7 @@ function access_code_uninstall() {
   $config->clear('auto_code_format');
   $config->clear('expiration_default');
   $config->clear('display_input');
+  $config->clear('login_attempts_limit');
+  $config->clear('login_attempts_window');
   $config->save(TRUE);
 }
diff --git a/src/Controller/UseCodeController.php b/src/Controller/UseCodeController.php
index a3af1589c6ee33d1155d6cf39e047f3099f35fb9..51fb4c7ff0dcb2fe3d236c269d15da1eb7013e48 100644
--- a/src/Controller/UseCodeController.php
+++ b/src/Controller/UseCodeController.php
@@ -5,6 +5,7 @@ namespace Drupal\access_code\Controller;
 use Drupal\access_code\Service\AccessCodeManager;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Flood\FloodInterface;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Url;
@@ -39,37 +40,67 @@ class UseCodeController extends ControllerBase {
    */
   protected $accessCodeManager;
 
+  /**
+   * The flood service.
+   *
+   * @var \Drupal\Core\Flood\FloodInterface
+   */
+  protected $flood;
+
   /**
    * Constructor.
    */
-  public function __construct(LoggerChannelFactoryInterface $logger_factory, Connection $database, MessengerInterface $messenger, AccessCodeManager $manager) {
+  public function __construct(
+    LoggerChannelFactoryInterface $logger_factory,
+    Connection $database,
+    MessengerInterface $messenger,
+    AccessCodeManager $manager,
+    FloodInterface $flood,
+  ) {
     $this->logger = $logger_factory->get('access_code');
     $this->database = $database;
     $this->messenger = $messenger;
     $this->accessCodeManager = $manager;
+    $this->flood = $flood;
   }
 
   /**
    * @inheritdoc
    */
   public static function create(ContainerInterface $container) {
-    return new static($container->get('logger.factory'), $container->get('database'), $container->get('messenger'), $container->get('access_code.manager'));
+    return new static(
+      $container->get('logger.factory'),
+      $container->get('database'),
+      $container->get('messenger'),
+      $container->get('access_code.manager'),
+      $container->get('flood'),
+    );
   }
 
   /**
    * Page callback for the use code link.
    */
   public function useCode($access_code, Request $request) {
-    $uid = $this->accessCodeManager->validateAccessCode($access_code);
+    $ip_address = $request->getClientIp();
+    $limit = $this->config('access_code.settings')->get('login_attempts_limit') ?: 5;
+    $window = $this->config('access_code.settings')->get('login_attempts_window') ?: 3600;
 
-    if ($uid) {
-      $user = User::load($uid);
+    if ($this->flood->isAllowed('access_code_login', $limit, $window, $ip_address)) {
+      $uid = $this->accessCodeManager->validateAccessCode($access_code);
 
-      $url = $this->accessCodeManager->processLogin($user);
-      return new RedirectResponse($url->toString());
-    }
-    else {
-      throw new AccessDeniedHttpException();
+      if ($uid) {
+        $user = User::load($uid);
+
+        $this->flood->clear('access_code_login', $ip_address);
+
+        $url = $this->accessCodeManager->processLogin($user);
+        return new RedirectResponse($url->toString());
+      } else {
+        $this->flood->register('access_code_login', $window, $ip_address);
+        throw new AccessDeniedHttpException();
+      }
+    } else {
+      throw new AccessDeniedHttpException('Too many failed attempts. Please try again later.');
     }
   }
 
diff --git a/src/Form/LoginForm.php b/src/Form/LoginForm.php
index 0396b5a9330673a36e5ec631831c2960aa50c886..ebea5d1493e73fd1448172ea93fb640d38e03685 100644
--- a/src/Form/LoginForm.php
+++ b/src/Form/LoginForm.php
@@ -5,6 +5,7 @@ namespace Drupal\access_code\Form;
 use Drupal\access_code\Service\AccessCodeManager;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Flood\FloodInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\user\Entity\User;
@@ -30,13 +31,26 @@ class LoginForm extends FormBase {
    */
   private $config;
 
+  /**
+   * The flood service.
+   *
+   * @var \Drupal\Core\Flood\FloodInterface
+   */
+  protected $flood;
+
   /**
    * {@inheritdoc}
    */
-  public function __construct(ModuleHandlerInterface $handler, AccessCodeManager $manager, ConfigFactoryInterface $config_factory) {
+  public function __construct(
+    ModuleHandlerInterface $handler,
+    AccessCodeManager $manager,
+    ConfigFactoryInterface $config_factory,
+    FloodInterface $flood,
+  ) {
     $this->moduleHandler = $handler;
     $this->accessCodeManager = $manager;
     $this->config = $config_factory->get('access_code.settings');
+    $this->flood = $flood;
   }
 
   /**
@@ -46,7 +60,8 @@ class LoginForm extends FormBase {
     return new static(
       $container->get('module_handler'),
       $container->get('access_code.manager'),
-      $container->get('config.factory')
+      $container->get('config.factory'),
+      $container->get('flood'),
     );
   }
 
@@ -86,13 +101,21 @@ class LoginForm extends FormBase {
    * {@inheritdoc}
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
-    $uid = $this->accessCodeManager->validateAccessCode($form_state->getValue('access_code'));
-
-    if (!$uid) {
-      $form_state->setErrorByName('access_code', $this->t('Invalid access code.'));
-    }
-    else {
-      $form_state->set('uid', $uid);
+    $ip_address = $this->getRequest()->getClientIp();
+    $limit = $this->config('access_code.settings')->get('login_attempts_limit') ?: 5;
+    $window = $this->config('access_code.settings')->get('login_attempts_window') ?: 3600;
+
+    if ($this->flood->isAllowed('access_code_login', $limit, $window, $ip_address)) {
+      $uid = $this->accessCodeManager->validateAccessCode($form_state->getValue('access_code'));
+
+      if (!$uid) {
+        $this->flood->register('access_code_login', $window, $ip_address);
+        $form_state->setErrorByName('access_code', $this->t('Invalid access code.'));
+      } else {
+        $form_state->set('uid', $uid);
+      }
+    } else {
+      $form_state->setErrorByName('access_code', $this->t('Too many failed login attempts. Please try again later.'));
     }
   }
 
@@ -106,6 +129,9 @@ class LoginForm extends FormBase {
 
     $user = User::load($uid);
 
+    $ip_address = $this->getRequest()->getClientIp();
+    $this->flood->clear('access_code_login', $ip_address);
+
     $url = $this->accessCodeManager->processLogin($user);
     $form_state->setRedirectUrl($url);
   }
diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php
index 08c325fe6855a04ba6585ed3e81bb415b0521f5d..7d7f2053543cf727e462866138118f39f903f68c 100644
--- a/src/Form/SettingsForm.php
+++ b/src/Form/SettingsForm.php
@@ -143,6 +143,27 @@ class SettingsForm extends ConfigFormBase {
       '#description' => $this->t('Display the entered characters in the access code field when logging in.'),
     ];
 
+    $form['flood_protection'] = [
+      '#type' => 'fieldset',
+      '#title' => $this->t('Flood protection settings'),
+    ];
+
+    $form['flood_protection']['login_attempts_limit'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Login attempts limit'),
+      '#default_value' => $config->get('login_attempts_limit') ?: 5,
+      '#description' => $this->t('The number of allowed login attempts before blocking.'),
+      '#min' => 1,
+    ];
+
+    $form['flood_protection']['login_attempts_window'] = [
+      '#type' => 'number',
+      '#title' => $this->t('Login attempts window (in seconds)'),
+      '#default_value' => $config->get('login_attempts_window') ?: 3600,
+      '#description' => $this->t('The time window in seconds for counting login attempts.'),
+      '#min' => 1,
+    ];
+
     return $form;
   }
 
@@ -160,6 +181,8 @@ class SettingsForm extends ConfigFormBase {
       ->set('expiration_default', $form_state->getValue('expiration_default'))
       ->set('blocked_roles', $form_state->getValue('blocked_roles'))
       ->set('display_input', $form_state->getValue('display_input'))
+      ->set('login_attempts_limit', intval($form_state->getValue('login_attempts_limit')))
+      ->set('login_attempts_window', intval($form_state->getValue('login_attempts_window')))
       ->save();
   }