134 lines
3.5 KiB
PHP
134 lines
3.5 KiB
PHP
<?php
|
|
/**
|
|
* @package solo
|
|
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU General Public License version 3, or later
|
|
*/
|
|
|
|
namespace Solo\Application;
|
|
|
|
use Awf\Encrypt\Totp;
|
|
|
|
class UserAuthenticationGoogle extends UserAuthenticationOtep
|
|
{
|
|
/**
|
|
* Is this user authenticated by this object? The $params array contains at least one key, 'password'.
|
|
*
|
|
* @param array $params The parameters used in the authentication process
|
|
*
|
|
* @return boolean True if the user is authenticated (or this plugin doesn't apply), false otherwise
|
|
*/
|
|
public function onAuthentication($params = array())
|
|
{
|
|
$result = true;
|
|
|
|
$userParams = $this->user->getParameters();
|
|
|
|
if ($userParams->get('tfa.method', 'none') == 'google')
|
|
{
|
|
$result = false;
|
|
|
|
$secret = isset($params['secret']) ? $params['secret'] : '';
|
|
|
|
if (!empty($secret))
|
|
{
|
|
$result = $this->validateGoogleOTP($secret);
|
|
|
|
if (!$result)
|
|
{
|
|
$result = $this->validateOtep($secret);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function onTfaSave($tfaParams)
|
|
{
|
|
$tfaMethod = isset($tfaParams['method']) ? $tfaParams['method'] : '';
|
|
|
|
if ($tfaMethod == 'google')
|
|
{
|
|
// The Google Authenticator key set by the user in the form
|
|
$newKey = isset($tfaParams['google']) ? $tfaParams['google'] : '';
|
|
// The Google Authenticator key in the user object
|
|
$oldKey = $this->user->getParameters()->get('tfa.google', '');
|
|
// The Google Authenticator generated secret code given in the form
|
|
$secret = isset($tfaParams['secret']) ? $tfaParams['secret'] : '';
|
|
// What was the old TFA method?
|
|
$oldTfaMethod = $this->user->getParameters()->get('tfa.method');
|
|
|
|
if (($oldTfaMethod == 'google') && ($newKey == $oldKey))
|
|
{
|
|
// We had already set up Google Authenticator and the code is unchanged. No change performed here.
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Safe fallback until we can verify the new yubikey
|
|
$this->user->getParameters()->set('tfa', null);
|
|
$this->user->getParameters()->set('tfa.method', 'none');
|
|
|
|
if (!empty($secret) && $this->validateGoogleOTP($secret, $newKey))
|
|
{
|
|
$this->user->getParameters()->set('tfa.method', 'google');
|
|
$this->user->getParameters()->set('tfa.google', $newKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validates a Google Authenticator key
|
|
*
|
|
* @param string $otp The OTP generated by Google Authenticator
|
|
* @param string $key The TOTP key (base32 encoded)
|
|
*
|
|
* @return boolean True if it's a valid OTP
|
|
*/
|
|
public function validateGoogleOTP($otp, $key = null)
|
|
{
|
|
// Create a new TOTP class with Google Authenticator compatible settings
|
|
$totp = new Totp(30, 6, 10);
|
|
|
|
// Get the key if none is defined
|
|
if (empty($key))
|
|
{
|
|
$key = $this->user->getParameters()->get('tfa.google', '');
|
|
}
|
|
|
|
// Check the code
|
|
$code = $totp->getCode($key);
|
|
|
|
$check = $code == $otp;
|
|
|
|
/*
|
|
* If the check fails, test the previous 30 second slot. This allow the
|
|
* user to enter the security code when it's becoming red in Google
|
|
* Authenticator app (reaching the end of its 30 second lifetime)
|
|
*/
|
|
if (!$check)
|
|
{
|
|
$time = time() - 30;
|
|
$code = $totp->getCode($key, $time);
|
|
$check = $code == $otp;
|
|
}
|
|
|
|
/*
|
|
* If the check fails, test the next 30 second slot. This allows some
|
|
* time drift between the authentication device and the server
|
|
*/
|
|
if (!$check)
|
|
{
|
|
$time = time() + 30;
|
|
$code = $totp->getCode($key, $time);
|
|
$check = $code == $otp;
|
|
}
|
|
|
|
return $check;
|
|
}
|
|
}
|