first install composer packages like so:
<pre>
Code:
composer require pragmarx/google2fa
composer require bacon/bacon-qr-code
</pre>
Then in /users/init.php we put:
Code:
require_once($abs_us_root.$us_url_root.'vendor/autoload.php');
Then make a page called /usersc/enable2FA.php:
<pre>
Code:
<?php require_once '../users/init.php'; ?>
<?php require_once $abs_us_root.$us_url_root.'users/includes/header.php'; ?>
<?php require_once $abs_us_root.$us_url_root.'users/includes/navigation.php'; ?>
<?php if (!securePage($_SERVER['PHP_SELF'])){die();}?>
<?php
//dealing with if the user is logged in
if($user->isLoggedIn() || !$user->isLoggedIn() && !checkMenu(2,$user->data()->id)){
if (($settings->site_offline==1) && (!in_array($user->data()->id, $master_account)) && ($currentPage != 'login.php') && ($currentPage != 'maintenance.php')){
$user->logout();
Redirect::to($us_url_root.'users/maintenance.php');
}
}
$currentUser = $user->data();
use PragmaRX\Google2FA\Google2FA;
$google2fa = new Google2FA();
$google2fa_url = $google2fa->getQRCodeGoogleUrl(
'Your Site Name',
$currentUser->email,
$currentUser->twoKey
);
?>
<section class="cid-qABkfm0Pyl mbr-fullscreen mbr-parallax-background" id="header2-0" data-rv-view="1854">
<div class="mbr-overlay" style="opacity: 0.4; background-color: rgb(40, 0, 60);"></div>
<div class="container">
<div class="row">
<div class="mbr-white col-md-10">
<div class="well">
<div class="row">
<div class="col-xs-12 col-md-3">
<p><a href="/users/account.php" class="btn btn-primary">Account Home</a></p>
<p><a class="btn btn-primary " href="/usersc/wallet.php" role="button">Manage Wallet</a></p>
</div>
<div class="col-xs-12 col-md-9">
<h1>Enable 2-Factor</h1>
<p>Scan this QR code with your authenticator app or input the key: <b><?php echo $currentUser->twoKey; ?></b></p>
<p><img src="<?php echo $google2fa_url; ?>"></p>
<p>Then enter one of your one-time passkeys here:</p>
<p>
<table border="0">
<tr>
<td><input class="form-control" placeholder="2FA Code" type="text" name="twoCode" id="twoCode" size="10"></td>
<td><button id="twoBtn" class="btn btn-primary">Verify</button></td>
</tr>
</table>
</p>
</div>
</div>
</div>
</div> <!-- /container -->
</div> <!-- /#page-wrapper -->
</div>
</section>
<!-- footers -->
<?php require_once $abs_us_root.$us_url_root.'users/includes/page_footer.php'; // the final html footer copyright row + the external js calls ?>
<!-- Place any per-page javascript here -->
<script>
$(document).ready(function() {
$("#twoBtn").click(function(e){
e.preventDefault();
$.ajax({
type: "POST",
url: "/api/",
data: {
action: "verify2FA",
twoCode: $("#twoCode").val()
},
success: function(result) {
var resultO = JSON.parse(result);
if(!resultO.error){
alert('2FA verified and enabled.');
}else{
alert('Incorrect 2FA code.');
}
},
error: function(result) {
alert('There was a problem verifying 2FA. Please check Internet or contact support.');
}
});
});
});
</script>
<?php require_once $abs_us_root.$us_url_root.'users/includes/html_footer.php'; // currently just the closing /body and /html ?>
</pre>
Do you have an API on your site? I always make one of these at the location /api/index.php. Something like this:
<pre>
Code:
<?php
$responseAr = array();
require_once('../users/init.php');
require_once('../vendor/autoload.php');
use PragmaRX\Google2FA\Google2FA;
$google2fa = new Google2FA();
$action = "";
$responseAr['success'] = true;
if (!securePage($_SERVER['PHP_SELF'])){die();}
if(isset($_REQUEST['action'])){
$action = $_REQUEST['action'];
$responseAr['error'] = false;
$db = DB::getInstance();
}else{
returnError('No API action specified.');
}
if(isset($user->data())) {
$currentUser = $user->data();
$loggedIn = true;
if (userHasPermission($currentUser->id, 4) || userHasPermission($currentUser->id, 5) || userHasPermission($currentUser->id, 6)) {
$loggedIn = true;
}else {
$loggedIn = false;
}
}}else{
$loggedIn = false;
}
switch($action){
case "pingEndpoint":
$responseAr['testResponse'] = 'testData';
break;
case "verify2FA":
$requestAr = requestCheck(['twoCode']);
$twoQ = $db->query("select twoKey, twoEnabled from users where id = ?", [$currentUser->id]);
if($twoQ->count() > 0){
$twoO = $twoQ->results()[0];
$twoVal = $google2fa->verifyKey($twoO->twoKey, $requestAr['twoCode']);
if($twoVal){
$responseAr['2FAValidated'] = true;
if($twoO->twoEnabled == 0){
$db->query("update users set twoEnabled = 1 where id = ?", [$currentUser->id]);
}
}else{
returnError('Incorrect 2FA code.');
}
}else{
returnError('Invalid user or not logged in.');
}
break;
default:
returnError('Invalid API action specified.');
break;
}
echo json_encode($responseAr);
</pre>
Near the top of users/login.php (After init) we need:
<pre>
Code:
use PragmaRX\Google2FA\Google2FA;
$google2fa = new Google2FA();
</pre>
We also need to add to users/login.php:
<pre>
Code:
<div class="form-group">
<label for="twoCode">2-Factor (If enabled)</label>
<input type="text" class="form-control" name="twoCode" id="twoCode" placeholder="2FA Code" autocomplete="off">
</div>
</pre>
Also in users/login.php on the line after
if ($validation->passed()) { add this:
<pre>
Code:
$twoPassed = true;
$twoQ = $db->query("select twoKey from users where username = ? and twoEnabled = 1", [Input::get('username')]);
if($twoQ->count() > 0){
$twoKey = $twoQ->results()[0]->twoKey;
$twoCode = trim(Input::get('twoCode'));
if($google2fa->verifyKey($twoKey, $twoCode) == false){
$twoPassed = false;
}
}
//Log user in if all is good
if($twoPassed){
$remember = (Input::get('remember') === 'on') ? true : false;
$user = new User();
$login = $user->loginEmail(Input::get('username'), trim(Input::get('password')), $remember);
}else{
$login = false;
}
</pre>
In users/join.php near the top (After init) we add:
<pre>
Code:
use PragmaRX\Google2FA\Google2FA;
$google2fa = new Google2FA();
</pre>
Also in users/join.php in the line before
$user->create(array( we add:
Code:
$twoKey = $google2fa->generateSecretKey();
And then in the $user->create(array we add along with the other stuff:
In MySQL you need to add to the users table 2 fields:
<pre>
Code:
twoKey | varchar(16) | uf8_bin | no default
twoEnabled | tinyint(1) | default: 0
</pre>
Lastly, you will want to add to users/account.php a link to the 2fa page. Here's how I did it. In the PHP code before the html I put:
<pre>
Code:
$twoQ = $db->query("select twoKey from users where id = ? and twoEnabled = 0", [$userdetails->id]);
if($twoQ->count() > 0){
$twoText = 'We recommend that you enable two-factor authentication to help protect your account. <a href="/usersc/enable2FA.php">Click here to setup 2FA</a>';
}else{
$twoText = '';
}
</pre>
Then in the HTML I put:
Code:
<?php echo $twoText; ?>
In your usersc/includes/custom_functions.php add:
<pre>
Code:
function returnError($errorMsg){
$responseAr = [];
$responseAr['success'] = true;
$responseAr['error'] = true;
$responseAr['errorMsg'] = $errorMsg;
die(json_encode($responseAr));
}
function userHasPermission($userID,$permissionID) {
$permissionsAr = fetchUserPermissions($userID);
//if($permissions[0])
foreach($permissionsAr as $perm)
{
if($perm->permission_id == $permissionID)
{
return TRUE;
}
}
return FALSE;
}
function requestCheck($expectedAr)
{
if(isset($_GET) && isset($_POST))
{
$requestAr = array_replace_recursive($_GET, $_POST);
}elseif(isset($_GET)){
$requestAr = $_GET;
}elseif(isset($_POST)){
$requestAr = $_POST;
}else{
$requestAr = array();
}
$diffAr = array_diff_key(array_flip($expectedAr),$requestAr);
if(count($diffAr) > 0)
{
returnError("Missing variables: ".implode(',',array_flip($diffAr)).".");
}else {
return $requestAr;
}
}
</pre>
I think that's it. Here's a paste of these instructions with better formatting:
https://hastebin.com/webayenehi.xml