When a user is prompted for their password to set up TFA on a site with LDAP authentication their password does not pass validation. That's because the user doesn't really have a Drupal account password.

The problem has also been described in #2856520: tfa and tfa basic issues and is mentioned in #2931150: Confirmation forms should not require passwords.

Original Report

Have you tried having a LDAP and TFA enabled on the same site? Do you know if they will work together? We're trying it here and it seems to conflict with each other - both work independently, but not when both enabled. Any thoughts on it?

Comments

stella created an issue. See original summary.

sanduhrs’s picture

Status: Active » Closed (outdated)

Please reopen if this is still active.

sorin_cocorada’s picture

I'm having the same problem. Users are successfully authenticated against LDAP, but when a LDAP user tries to setup TFA he/she gets 'Incorrect password'. The same process works for the admin user(local account)

sorin_cocorada’s picture

Issue may be caused by these lines of code(modules/tfa/src/Form/BasicSetup.php) which expect the password to be stored locally

 $current_pass = \Drupal::service('password')
        ->check(trim($form_state->getValue('current_pass')), $account->getPassword());
//      if (!$current_pass) {
//        $form_state->setErrorByName('current_pass', $this->t("Incorrect password."));
//      }

Commenting the if block allows me enabling TFO by skipping password verification

dcam’s picture

Title: LDAP » Users cannot set up TFA on sites with LDAP authentication
Category: Support request » Bug report
Priority: Normal » Major
Issue summary: View changes
Status: Closed (outdated) » Active

This is still an issue. Yesterday I encountered the problem as described in comment 3.

I don't have a solution for this yet and I'm reluctant to implement any work-around that by-passes the validation step. I'm not certain, but it seems like that would allow anyone to modify TFA for any account.

greggles’s picture

Title: Users cannot set up TFA on sites with LDAP authentication » Support TFA on sites with LDAP authentication
Category: Bug report » Feature request

I don't think anyone has intentionally built this, so it seems like a feature.

I think someone who has an LDAP site and wants TFA might want to do TFA at the LDAP point, and not inside of Drupal.

I guess that if this gets built in Drupal, it will require someone with an LDAP site and TFA to write the code or hire someone to write the code. From a code maintenance perspective, I think this should live as a sub-module of the main TFA codebase in some way.

samtheman’s picture

Can anyone advise on workarounds, other than commenting out the password validation in #4?

jcnventura’s picture

Status: Active » Closed (duplicate)

I'm closing this again. If you use an IDP (identity provider), you have 100% trust in that system. If you don't trust it, pretty please don't use it!

And yes, that does mean that TFA should be handled at the IDP side of things, and Drupal just accepts that the user being authenticated by the remote system is indeed the person who owns that account.

And finally, this seems to be a duplicate for the request in #2931150: Confirmation forms should not require passwords. so let's keep the discussion going there.

yekaterina k’s picture

Hello @jcnventura,

And if we will decide anyway to use TFA and SAML Drupal Login at the same time, what would be the correct approach to do so (knowing that when user is logged with sso it bypasses TFA) ?

joancatala’s picture

I have the same problem: the user can't set up the TFA (OTP) application because Drupal doesn't recognize his LDAP password.
Any solution?

input’s picture

Even this is marked as duplicate and closed, the duplicate issue is about "disabling password" check. This seems not a solution.
I implemented TFA together with Lightweight Directory Access Protocol.

TFA checks directly on vanilla Drupal\Core\Password\PasswordInterface. LDAP Module does not apply here, so I "reauth" if the vanilla verification/validation fails via a custom module.

As this is an direct intervention into security, maybe someone could verify this method as "a" way to go.

use Drupal\Core\Form\FormState;
use Drupal\ldap_servers\Helper\CredentialsStorage;

/*
 * Manual credential check implementation
 * see https://www.drupal.org/project/ldap/issues/2698159#comment-12394385
 */
 
function _MYMODULE_authenticate($name, $pass) {
  $form_state = new FormState();
  $form_state->setValue('name', $name);
  CredentialsStorage::storeUserPassword($pass);
  $validator = \Drupal::service('ldap_authentication.login_validator');
  $form_state = $validator->validateLogin($form_state);
  return $form_state->get('uid');
}


/**
 * Implements hook_form_alter()
 * Adds custom validation to TFA-Setup forms
 */

function MYMODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
   if ( in_array($form_id, array('tfa_setup','tfa_disable')) ) {
     $form['#validate'][] = 'MYMODULE_tfa_form_submit_validate';
   }
}

/**
 * custom re-validation of tfa-setup form
 */

function MYMODULE_tfa_form_submit_validate($form, FormStateInterface $form_state) {
  $form_errors = $form_state->getErrors();
  if ( $form_state->hasAnyErrors()  && isset($form_errors['current_pass'])  ) {
    // Drupal Core PasswordCheck failed so
    // get entered passwd and current user account name
    $username = \Drupal::currentUser()->getAccountName();
    $entered_pass = $form_state->getValue('current_pass');

    if ( isset($username) && isset($entered_pass) ) {
      //check against LDAP with my custom check
      $manual_pw_check = _MYMODULE_authenticate($username, $entered_pass);
      if ($manual_pw_check) {
        //get current errors
        $form_errors = $form_state->getErrors();
        $form_state->clearErrors();
        // Remove the current_pass form error as we have LDAP-Auth.
        unset($form_errors['current_pass']);
        //reapply all other form erros
        foreach ($form_errors as $name => $error_message) {
          $form_state->setErrorByName($name, $error_message);
        }
      }
    }
  }
}