Implementing Two-Factor Authentication (2FA) During Login in Drupal 9 Programmatically

Two-Factor Authentication (2FA) is an essential security measure that adds an extra layer of protection to user accounts in your Drupal 9 website. This blog post will guide you through implementing 2FA during the login process programmatically, ensuring your website's security is enhanced. We'll discuss the steps involved, necessary modules, and code examples to achieve this.

Prerequisites

Before implementing 2FA in your Drupal 9 website, you need to ensure:

  1. A functioning Drupal 9 website.
  2. Administrative access to your Drupal website.
  3. Familiarity with Drupal module development.

Step 1: Install Required Modules

To enable 2FA functionality in Drupal 9, you need to install the TFA (Two-Factor Authentication) module and a TFA plugin for your preferred 2FA method (e.g., Time-based One-Time Passwords or TOTP). You can install these modules using Composer:

composer require drupal/tfa
composer require drupal/tfa_totp

After installation, enable both the TFA and TOTP modules in the Drupal admin interface.

Step 2: Configure TFA Settings

In the Drupal admin, navigate to Configuration > People > Account settings and scroll down to the "Two-Factor Authentication" section. Enable 2FA for user roles you want to require 2FA authentication for during login. Choose TOTP as your preferred 2FA method.

Step 3: Implement Custom Code

To enforce 2FA during login programmatically, you can implement custom code within your custom module. You'll need to create a custom login form and add logic to enforce 2FA.

Below is a simplified example:

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\tfa\Plugin\TfaValidationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class CustomLoginForm extends FormBase {
  protected $tfaValidation;

  public function __construct(TfaValidationInterface $tfaValidation) {
    $this->tfaValidation = $tfaValidation;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('tfa.validation')
    );
  }

  public function getFormId() {
    return 'custom_login_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Username'),
    ];
    $form['pass'] = [
      '#type' => 'password',
      '#title' => $this->t('Password'),
    ];
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Log in'),
    ];
    return $form;
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $username = $form_state->getValue('name');
    $password = $form_state->getValue('pass');

    // Validate the user's credentials.
    if ($this->validateUserCredentials($username, $password)) {
      // Check if the user has 2FA enabled.
      $user = user_load_by_name($username);
      if ($user && $user->hasPermission('use tfa validation')) {
        // Check 2FA status.
        if (!$this->tfaValidation->userHasTfa($user)) {
          // Redirect the user to set up 2FA.
          // You can provide a link to the 2FA setup page.
        } else {
          // Check if 2FA validation succeeds.
          if (!$this->tfaValidation->loginValidate($user)) {
            // Redirect to the 2FA verification page.
            // You can provide a link to the 2FA verification page.
          }
        }
      } else {
        // No 2FA required. Proceed with the login.
      }
    }
  }

  private function validateUserCredentials($username, $password) {
    // Implement your custom logic to validate user credentials.
    // Return true if credentials are valid, otherwise false.
  }
}

In this example, we create a custom login form, and when the user submits the form, we perform the following steps:

  • Validate the user's credentials.
  • Check if the user has 2FA enabled and if 2FA validation succeeds.

You can customize the behavior of your 2FA enforcement logic based on your specific requirements.

Step 4: Display Setup and Verification Pages

If the user does not have 2FA enabled or needs to verify 2FA, you can create setup and verification pages that guide the user through the process. This is typically done within your custom module and templates.

Step 5: Test and Refine

Test your custom 2FA implementation thoroughly to ensure it works as intended. You can refine the user experience, implement custom error handling, and improve the visual aspects of your setup and verification pages.

USING DRUPAL DEFAULT LOGIN FORM

If you want to implement Two-Factor Authentication (2FA) on the default Drupal login form without creating a custom login form, you can follow these steps:

Step 1: Install and Configure TFA Modules

Start by installing and configuring the necessary TFA modules in your Drupal 9 website. As previously mentioned, you need the TFA and TFA plugin modules, such as TFA TOTP, to set up 2FA. You can install these modules using Composer:

composer require drupal/tfa
composer require drupal/tfa_totp

Once the modules are installed, enable them in your Drupal admin interface and configure the TFA settings, specifying your preferred 2FA method (e.g., TOTP).

Step 2: Implement Custom Code for 2FA

You can use custom code to enforce 2FA on the default Drupal login form. Below is an example of how to do this:

use Drupal\Core\Form\FormStateInterface;
use Drupal\tfa\Plugin\TfaValidationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class CustomLoginFormAlter {

  protected $tfaValidation;

  public function __construct(TfaValidationInterface $tfaValidation) {
    $this->tfaValidation = $tfaValidation;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('tfa.validation')
    );
  }

  public function alterForm(array &$form, FormStateInterface $form_state) {
    // Check if the user is submitting the login form.
    if ($form_state->getFormObject()->getFormId() === 'user_login_form') {
      $username = $form_state->getValue('name');
      $user = user_load_by_name($username);

      // Check if the user has 2FA enabled.
      if ($user && $user->hasPermission('use tfa validation')) {
        // Check if the user has configured 2FA.
        if (!$this->tfaValidation->userHasTfa($user)) {
          // Redirect the user to set up 2FA.
          // You can provide a link to the 2FA setup page.
          drupal_set_message('Please set up Two-Factor Authentication (2FA) for your account.');
          $form_state->setRedirect('tfa.tfa.setup');
        } else {
          // Check if 2FA validation succeeds.
          if (!$this->tfaValidation->loginValidate($user)) {
            // Redirect to the 2FA verification page.
            // You can provide a link to the 2FA verification page.
            drupal_set_message('Please verify your Two-Factor Authentication (2FA) code.');
            $form_state->setRedirect('tfa.tfa.login');
          }
        }
      }
    }
  }
}

In this example, we use a custom form alter to check if the user is submitting the default Drupal login form. We then perform the following steps:

  • Verify if the user has 2FA enabled and, if not, redirect them to set up 2FA.
  • Check if 2FA validation is required, and if so, redirect them to the verification page.

By implementing this code, you can enforce 2FA on the default login form without creating a custom form.

Step 3: Display Setup and Verification Pages

As mentioned earlier, you should have setup and verification pages that guide the user through the 2FA process. You can configure and style these pages based on your requirements.

Step 4: Test and Refine

Thoroughly test your 2FA implementation to ensure it works correctly. You can refine the user experience, implement custom error handling, and improve the visual aspects of your setup and verification pages.

By following these steps, you can enforce Two-Factor Authentication during login on the default Drupal login form, enhancing the security of your website without the need for a custom login form.

 

Share on social media

Add new comment