XF 2.0 Two-step validation through PHP script

I have everything else supported.. just can't figure out how to handle two-step for the life of me. I need to check if they have it active, then see if it's currently saved and if not, save it. Anybody have any ideas?

Code:
<?php
$ip = $_POST['ip'];
$name = $_POST['name'];
$pass = $_POST['pass'];
$email = $_POST['email'];
if (!isset($ip, $name, $pass)) {
       die();
}
/**
* Create the bridge to Xenforo
**/
$fileDir = '../../';
require($fileDir . '/src/XF.php');
XF::start($fileDir);

/**
* Check if the username is registered (login and prompt for e-mail to create account)
*/
$finder = \XF::finder('XF:User');
$user = $finder->where('username', $name)->fetchOne();
if(!$user) {
    /**
     * Validate the username before proceeding with registration
     */
    $validator = \XF::app()->validator('Username');
    $username = $validator->coerceValue($name);
    if (!$validator->isValid($username, $errorKey)) {
        if($errorKey == 'too_long') {
            die('Username too long.');
        } else if($errorKey == 'disallowed' || $errorKey == 'censored') {
            die('Username contained disallowed words.');
        } else if($errorKey == 'regex' || $errorKey == 'comma') {
            die('Username contains incorrect characters.');
        } else if($errorKey == 'duplicate') {
            die('Username must be unique.');
        }
    }

    /**
     * If the account isn't registered and POST doesn't include email.. request! :-)
     */
    if(empty($email)) {
        die('Email required.');
    }

    /**
     * Ensure email isn't in use
     */
    $emailInUse = \XF::finder('XF:User')->where('email', $email)->fetchOne();
    if ($emailInUse) {
        die('Email in use.');
    }

    /**
     * Validate email
     */
    $emailValidator = \XF::app()->validator('Email');
    $emailValidator->setOption('check_typos', true);

    if (!$emailValidator->isValid($email, $errorKey)) {
        if ($errorKey == 'banned') {
            die('Email banned.');
        } else if ($errorKey == 'typo') {
            die('Email type.');
        } else {
            die('Invalid email.');
        }
    }

    /**
     * Register the new user
     */
    $registration = XF::service('XF:User\Registration');
    $input['username'] = $name;
    $input['email'] = $email;
    $input['password'] = $pass;
    $registration->setFromInput($input);
    $user = $registration->save();

    /**
     * Set the user groups
     */
    $user-> user_group_id = 2;
    //$user->secondary_group_ids = [1, 2, 3];
    $user->save();

    /**
     * Log the user in
     */
    $columns = array("user_id", "username", "user_group_id", "secondary_group_ids");
    $data = array();
    foreach ($columns as $c) {
        $data[$c] = $user[$c];
    }
    die('d=' . json_encode($data));
}

/**
* Check if the user is banned
*/
if($user->is_banned) {
    die('Banned.');
}

/**
* Verify the account details
**/
$loginService = \XF::app()->service('XF:User\Login', $name, $ip);
$success = $loginService->validate($pass);
if(!$success) {
    die('Incorrect password. Please try again.');
}

/**
* Verify two-step authentication
**/


/**
* Successful login
**/
$columns = array("user_id", "username", "user_group_id", "secondary_group_ids");
$data = array();
foreach ($columns as $c) {
    $data[$c] = $user[$c];
}
die('d=' . json_encode($data));

Thanks guys!
 
Last edited:
Check the XF/ControllerPlugin/Login.php file. There is a function
Code:
runTfaCheck
That should help.
 
Check the XF/ControllerPlugin/Login.php file. There is a function
Code:
runTfaCheck
That should help.

Hey, thanks for the reply man. How do I reference the plugin, though?

XF::service('XF:User\Login'); is for the service... but I don't see anything referencing a plugin.
 
Not entire sure how I would get the Controller plugin, but when you need a controller plugin from within a controller, it can be retrieved by
Code:
$this->plugin('XF:Login');

And plugin method in Controller.php is
Code:
public function plugin($name)
{
   $class = \XF::stringToClass($name, '%s\ControllerPlugin\%s');
   $class = $this->app->extendClass($class);

   return new $class($this);
}
 
Not entire sure how I would get the Controller plugin, but when you need a controller plugin from within a controller, it can be retrieved by
Code:
$this->plugin('XF:Login');

And plugin method in Controller.php is
Code:
public function plugin($name)
{
   $class = \XF::stringToClass($name, '%s\ControllerPlugin\%s');
   $class = $this->app->extendClass($class);

   return new $class($this);
}

Thanks man. I'll look into this as soon as I'm home.
 
In case people were curious, I ended up getting this to work. Here's how I did it:

PHP:
$tfaEnabled = $user->Option->use_tfa;
if ($tfaEnabled) {
    $tfaRepository = $user->repository('XF:Tfa');
    if ($tfaRepository->userRequiresTfa($user)) {
        $tfaTrustValue = $_POST['key'];
        $milliseconds = round(microtime(true) * 1000);
        $tfaTrustRepo = $user->repository('XF:UserTfaTrusted');
        if (!$tfaTrustRepo->getTfaTrustRecord($user->user_id, $tfaTrustValue)) {
            $tfaCode = $_POST['code'];
            if ($tfaCode == 0) {
                die("Two-factor authentication required.");
            }

            $providerId = 'totp';
            $tfaService = $loginService->service('XF:User\Tfa', $user);

            $providers = $tfaService->getProviders();
            $provider = $providers[$providerId];
            $providerData = $provider->getUserProviderConfig($user->user_id);
            $handler = $provider->handler;
            if (!$handler->verify('login', $user, $providerData, \XF::app()->request())) {
                die("Two-factor authentication invalid!");
            } else {
                if ($_POST['trust'] === true) {
                    $db = \XF::db();
                    $db->insert('xf_user_tfa_trusted', [
                        'user_id' => $user->user_id,
                        'trusted_key' => $tfaTrustValue,
                        'trusted_until' => \XF::$time + 86400 * 30
                    ]);
                }
            }
        }
    }
}
 
Last edited:
Top Bottom