mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2024-11-25 10:20:29 -06:00
Make sure 2FA works as expected.
This commit is contained in:
parent
3dff8dfaf8
commit
c32f3254c5
@ -46,7 +46,6 @@ use Illuminate\Support\Collection;
|
|||||||
use Laravel\Passport\ClientRepository;
|
use Laravel\Passport\ClientRepository;
|
||||||
use Log;
|
use Log;
|
||||||
use PragmaRX\Recovery\Recovery;
|
use PragmaRX\Recovery\Recovery;
|
||||||
use Preferences;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ProfileController.
|
* Class ProfileController.
|
||||||
@ -139,19 +138,38 @@ class ProfileController extends Controller
|
|||||||
public function code()
|
public function code()
|
||||||
{
|
{
|
||||||
$domain = $this->getDomain();
|
$domain = $this->getDomain();
|
||||||
|
$secret = null;
|
||||||
|
|
||||||
|
// generate secret if not in session
|
||||||
|
if (!session()->has('temp-mfa-secret')) {
|
||||||
|
// generate secret + store + flash
|
||||||
$secret = Google2FA::generateSecretKey();
|
$secret = Google2FA::generateSecretKey();
|
||||||
|
session()->put('temp-mfa-secret', $secret);
|
||||||
session()->flash('two-factor-secret', $secret);
|
session()->flash('two-factor-secret', $secret);
|
||||||
|
}
|
||||||
|
// re-use secret if in session
|
||||||
|
if (session()->has('temp-mfa-secret')) {
|
||||||
|
// get secret from session and flash
|
||||||
|
$secret = session()->get('temp-mfa-secret');
|
||||||
|
session()->flash('two-factor-secret', $secret);
|
||||||
|
}
|
||||||
|
|
||||||
// generate recovery codes:
|
// generate codes if not in session:
|
||||||
|
if (!session()->has('temp-mfa-codes')) {
|
||||||
|
// generate codes + store + flash:
|
||||||
$recovery = app(Recovery::class);
|
$recovery = app(Recovery::class);
|
||||||
$recoveryCodes =$recovery->lowercase()
|
$recoveryCodes = $recovery->lowercase()->setCount(8)->setBlocks(2)->setChars(6)->toArray();
|
||||||
->setCount(8) // Generate 8 codes
|
session()->put('temp-mfa-codes', $recoveryCodes);
|
||||||
->setBlocks(2) // Every code must have 7 blocks
|
session()->flash('two-factor-codes', $recoveryCodes);
|
||||||
->setChars(6) // Each block must have 16 chars
|
}
|
||||||
->toArray();
|
|
||||||
$codes = implode("\r\n", $recoveryCodes);
|
|
||||||
|
|
||||||
Preferences::set('mfa_recovery', $recoveryCodes);
|
// get codes from session if there already:
|
||||||
|
if (session()->has('temp-mfa-codes')) {
|
||||||
|
$recoveryCodes = session()->get('temp-mfa-codes');
|
||||||
|
session()->flash('two-factor-codes', $recoveryCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
$codes = implode("\r\n", $recoveryCodes);
|
||||||
|
|
||||||
$image = Google2FA::getQRCodeInline($domain, auth()->user()->email, $secret);
|
$image = Google2FA::getQRCodeInline($domain, auth()->user()->email, $secret);
|
||||||
|
|
||||||
@ -290,7 +308,7 @@ class ProfileController extends Controller
|
|||||||
$subTitle = $user->email;
|
$subTitle = $user->email;
|
||||||
$userId = $user->id;
|
$userId = $user->id;
|
||||||
$enabled2FA = null !== $user->mfa_secret;
|
$enabled2FA = null !== $user->mfa_secret;
|
||||||
$mfaBackupCount = count(Preferences::get('mfa_recovery', [])->data);
|
$mfaBackupCount = count(app('preferences')->get('mfa_recovery', [])->data);
|
||||||
|
|
||||||
// get access token or create one.
|
// get access token or create one.
|
||||||
$accessToken = app('preferences')->get('access_token', null);
|
$accessToken = app('preferences')->get('access_token', null);
|
||||||
@ -316,8 +334,8 @@ class ProfileController extends Controller
|
|||||||
->toArray();
|
->toArray();
|
||||||
$codes = implode("\r\n", $recoveryCodes);
|
$codes = implode("\r\n", $recoveryCodes);
|
||||||
|
|
||||||
Preferences::set('mfa_recovery', $recoveryCodes);
|
app('preferences')->set('mfa_recovery', $recoveryCodes);
|
||||||
Preferences::mark();
|
app('preferences')->mark();
|
||||||
return view('profile.new-backup-codes', compact('codes'));
|
return view('profile.new-backup-codes', compact('codes'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,17 +449,26 @@ class ProfileController extends Controller
|
|||||||
$repository = app(UserRepositoryInterface::class);
|
$repository = app(UserRepositoryInterface::class);
|
||||||
/** @var string $secret */
|
/** @var string $secret */
|
||||||
$secret = session()->get('two-factor-secret');
|
$secret = session()->get('two-factor-secret');
|
||||||
|
|
||||||
$repository->setMFACode($user, $secret);
|
$repository->setMFACode($user, $secret);
|
||||||
|
|
||||||
session()->flash('success', (string)trans('firefly.saved_preferences'));
|
session()->flash('success', (string)trans('firefly.saved_preferences'));
|
||||||
app('preferences')->mark();
|
app('preferences')->mark();
|
||||||
|
|
||||||
|
// also save the code so replay attack is prevented.
|
||||||
|
$mfaCode = $request->get('code');
|
||||||
|
$this->addToMFAHistory($mfaCode);
|
||||||
|
|
||||||
|
// save backup codes in preferences:
|
||||||
|
app('preferences')->set('mfa_recovery', session()->get('temp-mfa-codes'));
|
||||||
|
|
||||||
// make sure MFA is logged out.
|
// make sure MFA is logged out.
|
||||||
if ('testing' !== config('app.env')) {
|
if ('testing' !== config('app.env')) {
|
||||||
Google2FA::logout();
|
Google2FA::logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// drop all info from session:
|
||||||
|
session()->forget(['temp-mfa-secret', 'two-factor-secret', 'temp-mfa-codes', 'two-factor-codes']);
|
||||||
|
|
||||||
return redirect(route('profile.index'));
|
return redirect(route('profile.index'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,5 +574,44 @@ class ProfileController extends Controller
|
|||||||
return redirect(route('login'));
|
return redirect(route('login'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO duplicate code.
|
||||||
|
*
|
||||||
|
* @param string $mfaCode
|
||||||
|
*/
|
||||||
|
private function addToMFAHistory(string $mfaCode): void
|
||||||
|
{
|
||||||
|
/** @var array $mfaHistory */
|
||||||
|
$mfaHistory = app('preferences')->get('mfa_history', [])->data;
|
||||||
|
$entry = [
|
||||||
|
'time' => time(),
|
||||||
|
'code' => $mfaCode,
|
||||||
|
];
|
||||||
|
$mfaHistory[] = $entry;
|
||||||
|
|
||||||
|
app('preferences')->set('mfa_history', $mfaHistory);
|
||||||
|
$this->filterMFAHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove old entries from the preferences array.
|
||||||
|
*/
|
||||||
|
private function filterMFAHistory(): void
|
||||||
|
{
|
||||||
|
/** @var array $mfaHistory */
|
||||||
|
$mfaHistory = app('preferences')->get('mfa_history', [])->data;
|
||||||
|
$newHistory = [];
|
||||||
|
$now = time();
|
||||||
|
foreach ($mfaHistory as $entry) {
|
||||||
|
$time = $entry['time'];
|
||||||
|
$code = $entry['code'];
|
||||||
|
if ($now - $time <= 300) {
|
||||||
|
$newHistory[] = [
|
||||||
|
'time' => $time,
|
||||||
|
'code' => $code,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app('preferences')->set('mfa_history', $newHistory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user