Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added optional $secret variable to EnableTwoFactorAuthentication.php #48

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
added optional $secret variable to EnableTwoFactorAuthentication.php
added validation of given secret
  • Loading branch information
jetwes committed Sep 17, 2020
commit 0ff74e6f5292f4ee27bd21c02add0fce986900ed
114 changes: 111 additions & 3 deletions src/Actions/EnableTwoFactorAuthentication.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
use Illuminate\Support\Collection;
use Laravel\Fortify\Contracts\TwoFactorAuthenticationProvider;
use Laravel\Fortify\RecoveryCode;
use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException;
use PragmaRX\Google2FA\Exceptions\InvalidCharactersException;
use PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException;
use PragmaRX\Google2FA\Support\Constants;

class EnableTwoFactorAuthentication
{
Expand All @@ -27,18 +31,122 @@ public function __construct(TwoFactorAuthenticationProvider $provider)
}

/**
* Enable two factor authentication for the user.
* Enable two factor authentication for the user after checking if a optional given secret is valid
*
* @param mixed $user
* @param ?string $secret
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
* @return void
*/
public function __invoke($user)
public function __invoke($user, $secret = null)
{
if ($secret) {
$this->validateSecret($secret);
}

$user->forceFill([
'two_factor_secret' => encrypt($this->provider->generateSecretKey()),
'two_factor_secret' => (!$secret ? encrypt($this->provider->generateSecretKey()) : encrypt($secret)),
'two_factor_recovery_codes' => encrypt(json_encode(Collection::times(8, function () {
return RecoveryCode::generate();
})->all())),
])->save();
}

/**
* Validate the secret.
*
* @param string $b32
*
* @throws InvalidCharactersException
* @throws SecretKeyTooShortException
* @throws IncompatibleWithGoogleAuthenticatorException
*/
protected function validateSecret($b32)
{
$this->checkForValidCharacters($b32);

$this->checkGoogleAuthenticatorCompatibility($b32);

$this->checkIsBigEnough($b32);
}

/**
* Calculate char count bits.
*
* @param string $b32
*
* @return int
*/
protected function charCountBits($b32)
{
return strlen($b32) * 8;
}

/**
* Check if the string length is power of two.
*
* @param string $b32
*
* @return bool
*/
protected function isCharCountNotAPowerOfTwo($b32)
{
return (strlen($b32) & (strlen($b32) - 1)) !== 0;
}

/**
* Check if the secret key is compatible with Google Authenticator.
*
* @param string $b32
*
* @throws IncompatibleWithGoogleAuthenticatorException
*/
protected function checkGoogleAuthenticatorCompatibility($b32)
{
if (
$this->isCharCountNotAPowerOfTwo($b32) // Google Authenticator requires it to be a power of 2 base32 length string
) {
throw new IncompatibleWithGoogleAuthenticatorException();
}
}

/**
* Check if all secret key characters are valid.
*
* @param string $b32
*
* @throws InvalidCharactersException
*/
protected function checkForValidCharacters($b32)
{
if (
preg_replace('/[^'.Constants::VALID_FOR_B32.']/', '', $b32) !==
$b32
) {
throw new InvalidCharactersException();
}
}

/**
* Check if secret key length is big enough.
*
* @param string $b32
*
* @throws SecretKeyTooShortException
*/
protected function checkIsBigEnough($b32)
{
// Minimum = 128 bits
// Recommended = 160 bits
// Compatible with Google Authenticator = 256 bits

if (
$this->charCountBits($b32) < 128
) {
throw new SecretKeyTooShortException();
}
}

}