diff --git a/modules/jsonapi_security_tfa/jsonapi_security_tfa.module b/modules/jsonapi_security_tfa/jsonapi_security_tfa.module
index f15f669..ee248bd 100644
--- a/modules/jsonapi_security_tfa/jsonapi_security_tfa.module
+++ b/modules/jsonapi_security_tfa/jsonapi_security_tfa.module
@@ -5,6 +5,8 @@
  * Contains jsonapi_security_tfa.module.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\jsonapi_security_tfa\Hook\JsonapiSecurityTfaHooks;
 use Drupal\Core\Session\AccountInterface;
 
 /**
@@ -18,21 +20,7 @@ use Drupal\Core\Session\AccountInterface;
  *
  * The TfaEnforcementSubscriber checks this flag to authorize JSON:API requests.
  */
+#[LegacyHook]
 function jsonapi_security_tfa_user_login(AccountInterface $account): void {
-  // Check if the user has TFA enabled via user.data service.
-  // This follows the same pattern used internally by the TFA module.
-  /** @var \Drupal\user\UserDataInterface $user_data */
-  $user_data = \Drupal::service('user.data');
-  $tfa_data = $user_data->get('tfa', $account->id(), 'tfa_user_settings');
-
-  if (empty($tfa_data) || empty($tfa_data['status'])) {
-    // User has not enabled TFA. Do not set the completion flag.
-    // The TfaEnforcementSubscriber will block their JSON:API access.
-    return;
-  }
-
-  $request = \Drupal::request();
-  if ($request->hasSession()) {
-    $request->getSession()->set('tfa_enabled', TRUE);
-  }
+  \Drupal::service(JsonapiSecurityTfaHooks::class)->userLogin($account);
 }
diff --git a/modules/jsonapi_security_tfa/jsonapi_security_tfa.services.yml b/modules/jsonapi_security_tfa/jsonapi_security_tfa.services.yml
index d5587c2..6d9db23 100644
--- a/modules/jsonapi_security_tfa/jsonapi_security_tfa.services.yml
+++ b/modules/jsonapi_security_tfa/jsonapi_security_tfa.services.yml
@@ -9,3 +9,7 @@ services:
       - '@jsonapi_security.helper'
     tags:
       - { name: event_subscriber }
+
+  Drupal\jsonapi_security_tfa\Hook\JsonapiSecurityTfaHooks:
+    class: Drupal\jsonapi_security_tfa\Hook\JsonapiSecurityTfaHooks
+    autowire: true
diff --git a/modules/jsonapi_security_tfa/src/Hook/JsonapiSecurityTfaHooks.php b/modules/jsonapi_security_tfa/src/Hook/JsonapiSecurityTfaHooks.php
new file mode 100644
index 0000000..39911d5
--- /dev/null
+++ b/modules/jsonapi_security_tfa/src/Hook/JsonapiSecurityTfaHooks.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Drupal\jsonapi_security_tfa\Hook;
+
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+
+/**
+ * Hook implementations for jsonapi_security_tfa.
+ */
+class JsonapiSecurityTfaHooks {
+
+  /**
+   * Implements hook_user_login().
+   *
+   * Sets the TFA completion flag for users who have TFA enabled.
+   * This hook is triggered by user_login_finalize(), which is called:
+   * - After successful TFA entry form submission
+   * - After trusted browser bypass
+   * - After grace period bypass.
+   *
+   * The TfaEnforcementSubscriber checks this flag to authorize JSON:API requests.
+   */
+  #[Hook('user_login')]
+  public static function userLogin(AccountInterface $account): void {
+    // Check if the user has TFA enabled via user.data service.
+    // This follows the same pattern used internally by the TFA module.
+    /** @var \Drupal\user\UserDataInterface $user_data */
+    $user_data = \Drupal::service('user.data');
+    $tfa_data = $user_data->get('tfa', $account->id(), 'tfa_user_settings');
+    if (empty($tfa_data) || empty($tfa_data['status'])) {
+      // User has not enabled TFA. Do not set the completion flag.
+      // The TfaEnforcementSubscriber will block their JSON:API access.
+      return;
+    }
+    $request = \Drupal::request();
+    if ($request->hasSession()) {
+      $request->getSession()->set('tfa_enabled', TRUE);
+    }
+  }
+
+}
diff --git a/tests/src/Kernel/DepthLimitingTest.php b/tests/src/Kernel/DepthLimitingTest.php
index 6f80bdf..552a1b4 100644
--- a/tests/src/Kernel/DepthLimitingTest.php
+++ b/tests/src/Kernel/DepthLimitingTest.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\jsonapi_security\Kernel;
 
+use Drupal\Tests\user\Traits\UserCreationTrait;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\node\Entity\NodeType;
 use Symfony\Component\HttpFoundation\Request;
@@ -17,7 +18,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
  */
 class DepthLimitingTest extends KernelTestBase {
 
-  use \Drupal\Tests\user\Traits\UserCreationTrait;
+  use UserCreationTrait;
 
   /**
    * {@inheritdoc}
diff --git a/tests/src/Kernel/ReadOnlyModeTest.php b/tests/src/Kernel/ReadOnlyModeTest.php
index 8a0854e..7d4c79d 100644
--- a/tests/src/Kernel/ReadOnlyModeTest.php
+++ b/tests/src/Kernel/ReadOnlyModeTest.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\jsonapi_security\Kernel;
 
+use Drupal\Tests\user\Traits\UserCreationTrait;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\node\Entity\Node;
 use Drupal\node\Entity\NodeType;
@@ -18,7 +19,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
  */
 class ReadOnlyModeTest extends KernelTestBase {
 
-  use \Drupal\Tests\user\Traits\UserCreationTrait;
+  use UserCreationTrait;
 
   /**
    * The test node.
