You are here

authenticate_user.php in Google Authenticator / 2 Factor Authentication - 2FA 8

Same filename and directory in other branches
  1. 8.2 src/Form/authenticate_user.php

This is used to authenticate user during login.

File

src/Form/authenticate_user.php
View source
<?php

namespace Drupal\miniorange_2fa\form;

use Drupal\user\Entity\User;
use Drupal\Core\Form\FormBase;
use Drupal\miniorange_2fa\MiniorangeUser;
use Drupal\miniorange_2fa\MoAuthConstants;
use Drupal\miniorange_2fa\MoAuthUtilities;
use Drupal\miniorange_2fa\AuthenticationType;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\miniorange_2fa\AuthenticationAPIHandler;
use Drupal\miniorange_2fa\MiniorangeCustomerProfile;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * @file
 *  This is used to authenticate user during login.
 */
class authenticate_user extends FormBase {
  public function getFormId() {
    return 'mo_auth_authenticate_user';
  }
  public function buildForm(array $form, \Drupal\Core\Form\FormStateInterface $form_state) {
    \Drupal::service('page_cache_kill_switch')
      ->trigger();
    global $base_url;
    $form['markup_library'] = array(
      '#attached' => array(
        'library' => array(
          "miniorange_2fa/miniorange_2fa.admin",
          "miniorange_2fa/miniorange_2fa.license",
        ),
      ),
    );
    $query_param = \Drupal::service('path.current')
      ->getPath();
    $url_parts = explode('/', $query_param);
    end($url_parts);
    $user_id = prev($url_parts);
    $custom_attribute = MoAuthUtilities::get_users_custom_attribute($user_id);
    $user_email = $custom_attribute[0]->miniorange_registered_email;
    if (\Drupal::config('miniorange_2fa.settings')
      ->get('mo_auth_challanged') == 0) {
      $this
        ->mo_auth_challenge_user($form, $user_email, $user_id, $form_state);
    }
    \Drupal::configFactory()
      ->getEditable('miniorange_2fa.settings')
      ->set('mo_auth_challanged', 1)
      ->save();
    if (empty($_SESSION['mo_auth']['mo_challenge_response'])) {
      return;
    }
    if (isset($_SESSION['mo_auth']['status']) && $_SESSION['mo_auth']['status'] === '1ST_FACTOR_AUTHENTICATED') {
      $challenge_response = $_SESSION['mo_auth']['mo_challenge_response'];
      $authType = $challenge_response->authType;
      $form['actions'] = array(
        '#type' => 'actions',
      );
      if (!empty($authType)) {
        $form['authType'] = array(
          '#type' => 'hidden',
          '#value' => $authType,
        );
        $authType = AuthenticationType::getAuthType($authType);
        $form = self::mo_auth_build_form($form, $base_url, $authType, $challenge_response, TRUE);
        unset($form['mo_message']);
      }
      else {
        $form['actions']['submit'] = array(
          '#type' => 'submit',
          '#value' => t('Save'),
          '#attributes' => array(
            'class' => array(
              'hidebutton',
            ),
          ),
        );
      }
      return $form;
    }
    $response = new RedirectResponse($base_url . '/user/login');
    $response
      ->send();
    exit;
  }
  public function mo_auth_challenge_user($form, $user_email, $user_id, \Drupal\Core\Form\FormStateInterface $form_state) {
    global $base_url;
    $customer = new MiniorangeCustomerProfile();
    $connection = \Drupal::database();
    $query = $connection
      ->query("SELECT * FROM {UserAuthenticationType} where miniorange_registered_email = '{$user_email}'");
    $result = $query
      ->fetchAll();
    $miniorange_user = new MiniorangeUser($customer
      ->getCustomerID(), $user_email, NULL, NULL, NULL);
    $auth_api_handler = new AuthenticationAPIHandler($customer
      ->getCustomerID(), $customer
      ->getAPIKey());
    $response = $auth_api_handler
      ->challenge($miniorange_user);

    /*redirected so already 1st_factor_authenticated*/
    $_SESSION['mo_auth']['status'] = '1ST_FACTOR_AUTHENTICATED';
    if ($response->status == 'SUCCESS') {
      $session_data = array(
        'status' => $_SESSION['mo_auth']['status'],
        'mo_challenge_response' => $response,
      );
      if (session_status() == PHP_SESSION_NONE) {
        session_start();
      }
      $_SESSION['mo_auth'] = $session_data;
    }
    else {
      global $base_url;
      \Drupal::messenger()
        ->addMessage(t('An error occured while processing your request. Please contact administrator.'), 'error', TRUE);
      $response = new RedirectResponse($base_url);
      $response
        ->send();
      exit;
    }
  }
  function mo_auth_authenticate_user_submit(array $form, \Drupal\Core\Form\FormStateInterface $form_state) {
    global $base_url;
    $input = $form_state
      ->getUserInput();
    $challenge_response = $_SESSION['mo_auth']['mo_challenge_response'];
    $form_state
      ->setRebuild();
    $query_param = \Drupal::service('path.current')
      ->getPath();
    $url_parts = explode('/', $query_param);
    end($url_parts);
    $user_id = prev($url_parts);
    $custom_attribute = MoAuthUtilities::get_users_custom_attribute($user_id);
    $user_email = $custom_attribute[0]->miniorange_registered_email;
    $authType = AuthenticationType::getAuthType($challenge_response->authType);
    if ($authType['oob'] === FALSE) {
      $token = '';
      if (array_key_exists('token', $input)) {
        $token = $input['token'];
      }
      $txId = '';
      $kba = array();
      if ($authType['challenge'] === TRUE) {
        $txId = $challenge_response->txId;
        if ($challenge_response->authType == AuthenticationType::$KBA['code']) {
          $count = count($challenge_response->questions);
          for ($i = 1; $i <= $count; $i++) {
            $ques = $input['mo2f_kbaquestion' . $i];
            $ans = $input['mo2f_kbaanswer' . $i];
            $qa = array(
              "question" => $ques,
              "answer" => $ans,
            );
            array_push($kba, $qa);
          }
        }
      }
      $customer = new MiniorangeCustomerProfile();
      $miniorange_user = new MiniorangeUser($customer
        ->getCustomerID(), $user_email, NULL, NULL, NULL);
      $auth_api_handler = new AuthenticationAPIHandler($customer
        ->getCustomerID(), $customer
        ->getAPIKey());
      $response = $auth_api_handler
        ->validate($miniorange_user, $txId, $token, $kba);
    }
    else {
      $txId = $input['txId'];
      $customer = new MiniorangeCustomerProfile();
      $auth_api_handler = new AuthenticationAPIHandler($customer
        ->getCustomerID(), $customer
        ->getAPIKey());
      $response = $auth_api_handler
        ->getAuthStatus($txId);
    }

    // read API response
    if ($response->status == 'SUCCESS') {
      $user = User::load($user_id);
      user_login_finalize($user);
      unset($_SESSION['mo_auth']);
      $response = new RedirectResponse($base_url . '/user/login');
      $response
        ->send();
      exit;
    }
    elseif ($response->status == 'DENIED') {
      unset($_SESSION['mo_auth']);
      \Drupal::messenger()
        ->addMessage(t('Authentication denied.'), 'error', TRUE);
      $response = new RedirectResponse($base_url);
      $response
        ->send();
      exit;
    }
    elseif ($response->status == 'FAILED') {
      unset($_SESSION['mo_auth']);
      \Drupal::messenger()
        ->addMessage(t("Authentication failed try again."), 'error', TRUE);
      $response = new RedirectResponse($base_url);
      $response
        ->send();
      exit;
    }
    else {
      unset($_SESSION['mo_auth']);
      \Drupal::messenger()
        ->addMessage(t('An error occured while processing your request. Please try again.'), 'error', TRUE);
      $response = new RedirectResponse($base_url);
      $response
        ->send();
      exit;
    }
  }
  function mo_auth_build_form($form, $base_url, $authType, $challenge_response, $success_form) {
    $form['main-header']['#markup'] = t('<style>#messages div.messages{visibility:hidden;}</style>');
    $form['header']['#markup'] = t('<div class="mo2f-modal">
              <div class="mo2f-modal-content">
                <div class="mo2f-modal-container mo2f-modal-header">Verify your identity</div>
                <div class="mo2f-modal-container">');
    $form = $this
      ->mo_auth_build_form_content($form, $base_url, $authType, $challenge_response, $success_form);
    $authTypeToHideButton = $authType['code'];
    $hide_button = '';
    if ($authTypeToHideButton == 'OUT OF BAND EMAIL' || $authTypeToHideButton == 'MOBILE AUTHENTICATION' || $authTypeToHideButton == 'PUSH NOTIFICATIONS') {
      $hide_button = 'hidebutton';
    }
    $form['loader']['#markup'] = '</div><div class="mo2f-modal-container mo2f-modal-footer">';
    $form['actions'] = array(
      '#type' => 'actions',
    );
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Verify'),
      '#attributes' => array(
        'class' => array(
          $hide_button,
        ),
      ),
      '#submit' => array(
        '::mo_auth_authenticate_user_submit',
      ),
    );
    $query_param = \Drupal::service('path.current')
      ->getPath();
    $url_parts = explode('/', $query_param);
    end($url_parts);
    $user_id = prev($url_parts);
    if ($authType['code'] != 'KBA' && !MoAuthUtilities::mo_auth_is_kba_configured($user_id) === FALSE) {
      $form['actions']['forgot'] = array(
        '#type' => 'submit',
        '#attributes' => array(
          'class' => array(
            'mo_auth_forgot_phone_btn_class',
          ),
        ),
        '#value' => t('forgot phone'),
        '#submit' => array(
          '::mo_auth_forgot_phone',
        ),
        '#limit_validation_errors' => array(),
      );
    }
    $form['actions']['cancel'] = array(
      '#markup' => '&nbsp;<a href="' . $base_url . '"> Cancel</a>',
      '#suffix' => '</div></div></div>',
    );
    return $form;
  }
  function mo_auth_forgot_phone($form, \Drupal\Core\Form\FormStateInterface $form_state) {
    global $base_url;
    $customer = new MiniorangeCustomerProfile();
    $query_param = \Drupal::service('path.current')
      ->getPath();
    $url_parts = explode('/', $query_param);
    end($url_parts);
    $user_id = prev($url_parts);
    $custom_attribute = MoAuthUtilities::get_users_custom_attribute($user_id);
    $user_email = $custom_attribute[0]->miniorange_registered_email;
    $miniorange_user = new MiniorangeUser($customer
      ->getCustomerID(), $user_email, NULL, NULL, 'KBA');
    $auth_api_handler = new AuthenticationAPIHandler($customer
      ->getCustomerID(), $customer
      ->getAPIKey());
    $response = $auth_api_handler
      ->challenge($miniorange_user);
    if ($response->status == 'SUCCESS') {
      $session_data = array(
        'status' => $_SESSION['mo_auth']['status'],
        'mo_challenge_response' => $response,
      );
      if (session_status() == PHP_SESSION_NONE) {
        session_start();
      }
      $_SESSION['mo_auth'] = $session_data;
      $authType = AuthenticationType::getAuthType('KBA');
      $form_state
        ->setRebuild();
      return self::mo_auth_build_form($form, $base_url, $authType, $response, TRUE);
    }
    else {
      global $base_url;
      \Drupal::messenger()
        ->addMessage(t('An error occured while processing your request. Please contact administrator.'), 'error', TRUE);
      $response = new RedirectResponse($base_url);
      $response
        ->send();
    }
  }
  function mo_auth_build_form_content($form, $base_url, $authType, $challenge_response, $success_form) {
    switch ($authType['code']) {
      case AuthenticationType::$EMAIL_VERIFICATION['code']:
        return self::mo_auth_build_oobemail_form($form, $base_url, $challenge_response);
      case AuthenticationType::$GOOGLE_AUTHENTICATOR['code']:
        return self::mo_auth_build_google_authenticator_form($form, $base_url, $challenge_response, $success_form);
      case AuthenticationType::$QR_CODE['code']:
        return self::mo_auth_build_qrcode_authentication_form($form, $base_url, $challenge_response);
      case AuthenticationType::$KBA['code']:
        return self::mo_auth_build_kba_authentication_form($form, $base_url, $challenge_response, $success_form);
      case AuthenticationType::$SOFT_TOKEN['code']:
        return self::mo_auth_build_soft_token_form($form, $base_url, $challenge_response, $success_form);
      case AuthenticationType::$PUSH_NOTIFICATIONS['code']:
        return self::mo_auth_build_push_notifications_form($form, $base_url, $challenge_response);
      case AuthenticationType::$SMS['code']:
        return self::mo_auth_build_otp_over_sms_form($form, $base_url, $challenge_response, $success_form);
      case AuthenticationType::$SMS_AND_EMAIL['code']:
        return self::mo_auth_build_otp_over_sms_and_email_form($form, $base_url, $challenge_response, $success_form);
      case AuthenticationType::$EMAIL['code']:
        return self::mo_auth_build_otp_over_email_form($form, $base_url, $challenge_response, $success_form);
      case AuthenticationType::$OTP_OVER_PHONE['code']:
        return $this
          ->mo_auth_build_otp_over_phone_form($form, $base_url, $challenge_response, $success_form);
      default:
        return $form;
    }
  }
  function mo_auth_build_google_authenticator_form($form, $base_url, $challenge_response, $success_message = TRUE) {
    if ($success_message === TRUE) {
      $message = 'Please enter the passcode generated on your Google Authenticator app.';
    }
    else {
      $message = 'The passcode you have entered is incorrect. Please enter the valid  passcode.';
    }
    $message_div_class = $success_message === TRUE ? 'mo2f-message-status' : 'mo2f-message-error';
    $form['header']['#markup'] .= t('<div class="mo2f-message ' . $message_div_class . '">' . $message . '</div>');
    $form['header']['#markup'] .= t('<div class="mo2f-info">Enter the passcode: </div>');
    $form['token'] = array(
      '#type' => 'textfield',
      '#attributes' => array(
        'placeholder' => t('Enter  passcode.'),
        'class' => array(
          'mo2f-textbox',
          'mo2f-textbox-otp',
        ),
        'autofocus' => 'true',
      ),
      '#required' => TRUE,
      '#maxlength' => 6,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_qrcode_authentication_form($form, $base_url, $challenge_response) {
    $message = 'Please scan the below QR Code from miniOrange Authenticator app to authenticate yourself.';
    $form['header']['#markup'] .= t('<div class="mo2f-message mo2f-message-status">' . $message . '</div>');
    $form['header']['#markup'] .= '<div class="mo2f-text-center">';
    $image = new FormattableMarkup('<img src="data:image/jpg;base64, ' . $challenge_response->qrCode . '"/>', [
      ':src' => $challenge_response->qrCode,
    ]);
    $form['actions_qrcode'] = array(
      '#markup' => $image,
    );
    $form['header']['#markup'] .= '</div>';
    $form['txId'] = array(
      '#type' => 'hidden',
      '#value' => $challenge_response->txId,
    );
    $form['url'] = array(
      '#type' => 'hidden',
      '#value' => MoAuthConstants::$AUTH_STATUS_API,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_oobemail_form($form, $base_url, $challenge_response) {
    $response = $_SESSION['mo_auth']['mo_challenge_response'];
    $user_email = $challenge_response->emailDelivery->contact;
    $hidden_email = MoAuthUtilities::getHiddenEmail($user_email);
    $message = 'A verification email is sent to <b>' . $hidden_email . '</b>. Please click on accept link to verify your email.';
    $form['header']['#markup'] .= t('<div class="mo2f-message mo2f-message-status">' . $message . '</div>');
    $form['header']['#markup'] .= t('<div class="mo2f-info mo2f-text-center">A verification email is sent to your registered email.</div>
                  <div class="mo2f-info mo2f-text-center">We are waiting for your approval...</div>');
    $image_path = file_create_url($base_url . '/' . drupal_get_path('module', 'miniorange_2fa') . '/includes/images/ajax-loader-login.gif');
    $form['header']['#markup'] .= '<div class="mo2f-text-center"><img src="' . $image_path . '"></div>';
    $form['txId'] = array(
      '#type' => 'hidden',
      '#value' => $response->txId,
    );
    $form['url'] = array(
      '#type' => 'hidden',
      '#value' => MoAuthConstants::$AUTH_STATUS_API,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_kba_authentication_form($form, $base_url, $challenge_response, $success_message) {
    if ($success_message === TRUE) {
      $message = 'Please answer the following questions.';
    }
    else {
      $message = 'The answers you have entered are incorrect.';
    }
    $message_div_class = $success_message === TRUE ? 'mo2f-message-status' : 'mo2f-message-error';
    $form['header']['#markup'] .= t('<div class="mo2f-message ' . $message_div_class . '">' . $message . '</div>');
    $form['header']['#markup'] .= '<div class="mo2f-kba-header mo2f-kba-row">
  			<div class="mo2f-kba-srno"></div>
  			<div class="mo2f-kba-question"></div>
			<div class="mo2f-kba-answer"></div>
  			</div>';
    $i = 0;
    $questions = isset($challenge_response->questions) ? $challenge_response->questions : '';
    if (is_array($questions)) {
      foreach ($questions as $ques) {
        $i++;
        $form['mo2f_kbaanswer' . $i] = array(
          '#type' => 'textfield',
          '#title' => t($i . '. ' . $ques->question),
          '#size' => 45,
          '#required' => TRUE,
          '#attributes' => array(
            'placeholder' => 'Enter your answer',
          ),
        );
        $form['mo2f_kbaquestion' . $i] = array(
          '#type' => 'hidden',
          '#value' => $ques->question,
        );
      }
    }
    $form['txId'] = array(
      '#type' => 'hidden',
      '#value' => isset($challenge_response->txId) ? $challenge_response->txId : '',
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_soft_token_form($form, $base_url, $challenge_response, $success_message = TRUE) {
    if ($success_message === TRUE) {
      $message = 'Please enter the passcode generated on your miniOrange Authenticator app.';
    }
    else {
      $message = 'The passcode you have entered is incorrect. Please enter the valid  passcode.';
    }
    $message_div_class = $success_message === TRUE ? 'mo2f-message-status' : 'mo2f-message-error';
    $form['header']['#markup'] .= t('<div class="mo2f-message ' . $message_div_class . '">' . $message . '</div>');
    $form['header']['#markup'] .= t('<div class="mo2f-info">Enter the passcode:</div>');
    $form['token'] = array(
      '#type' => 'textfield',
      '#attributes' => array(
        'placeholder' => t('Enter passcode.'),
        'class' => array(
          'mo2f-textbox',
          'mo2f-textbox-otp',
        ),
        'autofocus' => 'true',
      ),
      '#required' => TRUE,
      '#maxlength' => 6,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_push_notifications_form($form, $base_url, $challenge_response) {
    $response = $_SESSION['mo_auth']['mo_challenge_response'];
    $message = 'Please accept the push notification sent to your miniOrange Authenticator App.';
    $form['header']['#markup'] .= t('<div class="mo2f-message mo2f-message-status">' . $message . '</div>');
    $form['header']['#markup'] .= t('<div class="mo2f-info mo2f-text-center">A Push Notification has been sent to your miniOrange Authenticator App.</div>
                  <div class="mo2f-info mo2f-text-center">We are waiting for your approval...</div>');
    $image_path = file_create_url($base_url . '/' . drupal_get_path('module', 'miniorange_2fa') . '/includes/images/ajax-loader-login.gif');
    $form['header']['#markup'] .= '<div class="mo2f-text-center"><img src="' . $image_path . '"></div>';
    $form['txId'] = array(
      '#type' => 'hidden',
      '#value' => $response->txId,
    );
    $form['url'] = array(
      '#type' => 'hidden',
      '#value' => MoAuthConstants::$AUTH_STATUS_API,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_otp_over_sms_form($form, $base_url, $challenge_response, $success_message = TRUE) {
    if ($success_message === TRUE) {
      $message = 'Please enter the passcode sent to your mobile phone.';
    }
    else {
      $message = 'The passcode you have entered is incorrect. Please enter the valid  passcode.';
    }
    $message_div_class = $success_message === TRUE ? 'mo2f-message-status' : 'mo2f-message-error';
    $form['header']['#markup'] .= t('<div class="mo2f-message ' . $message_div_class . '">' . $message . '</div>');
    $form['token'] = array(
      '#type' => 'textfield',
      '#title' => t('Please enter the OTP you received:'),
      '#attributes' => array(
        'autofocus' => 'true',
      ),
      '#required' => TRUE,
      '#maxlength' => 6,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_otp_over_phone_form($form, $base_url, $challenge_response, $success_message = TRUE) {
    if ($success_message === TRUE) {
      $message = 'Please enter the 6 digit passcode prompted on phone call.';
    }
    else {
      $message = 'The passcode you have entered is incorrect. Please enter the valid 6 digit passcode.';
    }
    $message_div_class = $success_message === TRUE ? 'mo2f-message-status' : 'mo2f-message-error';
    $form['header']['#markup'] .= t('<div class="mo2f-message ' . $message_div_class . '">' . $message . '</div>');
    $form['header']['#markup'] .= t('<div class="mo2f-info">Enter the passcode:</div>');
    $form['token'] = array(
      '#type' => 'textfield',
      '#title' => t('Please enter the OTP received on phone call'),
      '#attributes' => array(
        'autofocus' => 'true',
      ),
      '#required' => TRUE,
      '#maxlength' => 6,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_otp_over_sms_and_email_form($form, $base_url, $challenge_response, $success_message = TRUE) {
    if ($success_message === TRUE) {
      $message = 'Please enter the passcode sent to your mobile phone and email.';
    }
    else {
      $message = 'The passcode you have entered is incorrect. Please enter the valid  passcode.';
    }
    $message_div_class = $success_message === TRUE ? 'mo2f-message-status' : 'mo2f-message-error';
    $form['header']['#markup'] .= t('<div class="mo2f-message ' . $message_div_class . '">' . $message . '</div>');
    $form['header']['#markup'] .= t('<div class="mo2f-info">Enter the passcode:</div>');
    $form['token'] = array(
      '#type' => 'textfield',
      '#title' => t('Please enter the OTP you received:'),
      '#attributes' => array(
        'autofocus' => 'true',
      ),
      '#required' => TRUE,
      '#maxlength' => 6,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  function mo_auth_build_otp_over_email_form($form, $base_url, $challenge_response, $success_message = TRUE) {
    if ($success_message === TRUE) {
      $message = 'Please enter the passcode sent to your email.';
    }
    else {
      $message = 'The passcode you have entered is incorrect. Please enter the valid  passcode.';
    }
    $message_div_class = $success_message === TRUE ? 'mo2f-message-status' : 'mo2f-message-error';
    $form['header']['#markup'] .= t('<div class="mo2f-message ' . $message_div_class . '">' . $message . '</div>');
    $form['header']['#markup'] .= t('<div class="mo2f-info">Enter the passcode:</div>');
    $form['token'] = array(
      '#type' => 'textfield',
      '#title' => t('Please enter the OTP you received:'),
      '#attributes' => array(
        'autofocus' => 'true',
      ),
      '#required' => TRUE,
      '#maxlength' => 6,
    );
    $form['mo_message'] = $message;
    return $form;
  }
  public function submitForm(array &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  }

}

Classes

Namesort descending Description
authenticate_user @file This is used to authenticate user during login.