1

I set some session data for a user I auto logged out. Then tried to retrieve that session data in a form validation. It is not there.

class MFAController extends ControllerBase {

  public function sendOTP(int $uid, Request $request): RedirectResponse {
    if (empty($uid)) {
      \Drupal::logger('mfa')->error('Invalid request. The uid is missing.');
      return $this->redirect('system.403');
    }

    user_logout();

    $user = User::load($uid); // reload the desired user for uid
    $passcode = Functions::passcode($user);

    $session = $request->getSession();
    $session->set('mfa_otp', $passcode);
    $session->set('mfa_otp_time', time());

    Functions::mailPasscode($user, $passcode);

    return $this->redirect('mfa.otp_form', ['uid' => $uid, 'resend' => 0]);
  }

  public function resendOTP(int $uid, Request $request): RedirectResponse {
    if (empty($uid)) {
      \Drupal::logger('mfa')->error('Invalid request. The uid is missing.');
      return $this->redirect('system.403');
    }

    $user = User::load($uid); // reload the desired user for uid
    $passcode = Functions::passcode($user);

    $session = $request->getSession();
    $session->set('mfa_otp', $passcode);
    $session->set('mfa_otp_time', time());

    Functions::mailPasscode($user, $passcode);

    return $this->redirect('mfa.otp_form', ['uid' => $uid, 'resend' => 1]);
  }
}

The form validation is the following one.

public function validateForm(array &$form, FormStateInterface $form_state): void {
  $passcode = $form_state->getValue('passcode');
  $session = $this->getRequest()->getSession();
  $otp = (int) ($session->get('mfa_otp') ?: 0);
  $otp_time = (int) ($session->get('mfa_otp_time') ?: 0);

  if ($otp == 0 || $otp_time == 0) {
    $form_state->setErrorByName('passcode', 'Onetime passcode is missing in your session.');
    $uid = $form_state->getValue('uid');
    $user = User::load($uid);
    \Drupal::logger('mfa')->error('Onetime passcode is missing in ' . $user->getDisplayName() . "($uid)'s" . ' session.');
    return;
  }

  if ($passcode != $otp)
    $form_state->setErrorByName('passcode', "Passcode is not valid.");

    if (time() - $otp_time > \Drupal::config('mfa.settings')->get('passcode_lifetime')) {
      $form_state->setErrorByName('passcode', "Passcode has expired. Please click the resend link below and generate a new passcode and try again.");
    }
  }
}

resendOTP() in my controller works. It does the same thing except does not call user_logout() because the user is already logged out. It has access restricted to the anonymous role. So calling user_logout() is messing up the session and is probably not the same session accessed by the form validate function.

How do I fix this?

0

1 Answer 1

3

When logging out Drupal doesn't mess with the session, it gets destroyed to make sure the next request is anonymous without any session cookie and is fully cacheable. If that's not what you want then invalidate the session, which clears the content and regenerates the session ID:

  public function build(Request $request) {
    $session = $request->getSession();
    $session->invalidate();
    // Now you can use the regenerated session, for example set a message
    $this->messenger()->addStatus($this->t('You have been logged out!'));
    return $this->redirect('<front>');
  }

You might want to run the other code in user_logout() for housekeeping, but when you are directly redirecting this is not absolutely necessary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.