verify 2FA code once before storing secret and activation for login to be sure it works; fixes #1030
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
61
2fa.php
61
2fa.php
@@ -34,6 +34,7 @@ use Froxlor\FroxlorTwoFactorAuth;
|
||||
use Froxlor\Settings;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\PhpHelper;
|
||||
|
||||
if (Settings::Get('2fa.enabled') != '1') {
|
||||
Response::dynamicError('2fa.2fa_not_activated');
|
||||
@@ -60,22 +61,71 @@ if ($action == 'delete') {
|
||||
'id' => $uid
|
||||
]);
|
||||
Response::standardSuccess('2fa.2fa_removed');
|
||||
} elseif ($action == 'add') {
|
||||
} elseif ($action == 'preadd') {
|
||||
$type = isset($_POST['type_2fa']) ? $_POST['type_2fa'] : '0';
|
||||
|
||||
if ($type == 0 || $type == 1) {
|
||||
$data = "";
|
||||
}
|
||||
if ($type == 2) {
|
||||
if ($type > 0) {
|
||||
// generate secret for TOTP
|
||||
$data = $tfa->createSecret();
|
||||
|
||||
$userinfo['type_2fa'] = $type;
|
||||
$userinfo['data_2fa'] = $data;
|
||||
$userinfo['2fa_unsaved'] = true;
|
||||
|
||||
// if type = email, send a code there for confirmation
|
||||
if ($type == 1) {
|
||||
$code = $tfa->getCode($tfa->createSecret());
|
||||
$_mailerror = false;
|
||||
$mailerr_msg = "";
|
||||
$replace_arr = [
|
||||
'CODE' => $code
|
||||
];
|
||||
$mail_body = html_entity_decode(PhpHelper::replaceVariables(lng('mails.2fa.mailbody'), $replace_arr));
|
||||
|
||||
try {
|
||||
$mail->Subject = lng('mails.2fa.subject');
|
||||
$mail->AltBody = $mail_body;
|
||||
$mail->MsgHTML(str_replace("\n", "<br />", $mail_body));
|
||||
$mail->AddAddress($userinfo['email'], User::getCorrectUserSalutation($userinfo));
|
||||
$mail->Send();
|
||||
} catch (\PHPMailer\PHPMailer\Exception $e) {
|
||||
$mailerr_msg = $e->errorMessage();
|
||||
$_mailerror = true;
|
||||
} catch (Exception $e) {
|
||||
$mailerr_msg = $e->getMessage();
|
||||
$_mailerror = true;
|
||||
}
|
||||
|
||||
if ($_mailerror) {
|
||||
Response::dynamicError($mailerr_msg);
|
||||
}
|
||||
}
|
||||
UI::twig()->addGlobal('userinfo', $userinfo);
|
||||
} else {
|
||||
Response::dynamicError('Select one of the possible values for 2FA');
|
||||
}
|
||||
} elseif ($action == 'add') {
|
||||
$type = isset($_POST['type_2fa']) ? $_POST['type_2fa'] : '0';
|
||||
$data = isset($_POST['data_2fa']) ? $_POST['data_2fa'] : '';
|
||||
$code = isset($_POST['codevalidation']) ? $_POST['codevalidation'] : '';
|
||||
|
||||
// validate
|
||||
$result = $tfa->verifyCode($data, $code, 3);
|
||||
|
||||
if ($result) {
|
||||
if ($type == 0 || $type == 1) {
|
||||
// no fixed secret for email validation, the validation code will be set on the fly
|
||||
$data = "";
|
||||
}
|
||||
Database::pexecute($upd_stmt, [
|
||||
't2fa' => $type,
|
||||
'd2fa' => $data,
|
||||
'id' => $uid
|
||||
]);
|
||||
Response::standardSuccess('2fa.2fa_added', [$filename]);
|
||||
Response::standardSuccess('2fa.2fa_added', $filename);
|
||||
}
|
||||
Response::dynamicError('Invalid/wrong code');
|
||||
}
|
||||
|
||||
$log->logAction(FroxlorLogger::USR_ACTION, LOG_NOTICE, "viewed 2fa::overview");
|
||||
@@ -98,7 +148,6 @@ if ($userinfo['type_2fa'] == '0') {
|
||||
}
|
||||
|
||||
UI::view('user/2fa.html.twig', [
|
||||
'themes' => $themes_avail,
|
||||
'type_select_values' => $type_select_values,
|
||||
'ga_qrcode' => $ga_qrcode
|
||||
]);
|
||||
|
||||
@@ -65,7 +65,7 @@ if ($action == '2fa_entercode') {
|
||||
}
|
||||
$code = isset($_POST['2fa_code']) ? $_POST['2fa_code'] : null;
|
||||
// verify entered code
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor');
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
|
||||
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code
|
||||
// which is temporarily stored for the customer when using email-2fa
|
||||
@@ -283,7 +283,7 @@ if ($action == '2fa_entercode') {
|
||||
// send mail if type_2fa = 1 (email)
|
||||
if ($userinfo['type_2fa'] == 1) {
|
||||
// generate code
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor');
|
||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||
$code = $tfa->getCode($tfa->createSecret());
|
||||
// set code for user
|
||||
$stmt = Database::prepare("UPDATE $table SET `data_2fa` = :d2fa WHERE `$uid` = :uid");
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
{% if userinfo.type_2fa == 0 %}
|
||||
{% set linkeraction = 'preadd' %}
|
||||
{% elseif userinfo['2fa_unsaved'] is defined and userinfo['2fa_unsaved'] %}
|
||||
{% set linkeraction = 'add' %}
|
||||
{% else %}
|
||||
{% set linkeraction = 'delete' %}
|
||||
@@ -24,9 +26,17 @@
|
||||
|
||||
{% elseif userinfo.type_2fa == 2 %}
|
||||
<label for="qrcode" class="col-form-label">{{ lng('2fa.2fa_ga_desc')|raw }}</label>
|
||||
<img src="{{ ga_qrcode }}" class="img-fluid" alt="QRCode" id="qrcode"/>
|
||||
<img src="{{ ga_qrcode }}" class="img-fluid" alt="QRCode" id="qrcode"/><br>
|
||||
<span>Code: <code>{{ userinfo.data_2fa }}</code></span>
|
||||
{% endif %}
|
||||
|
||||
{% if userinfo['2fa_unsaved'] is defined and userinfo['2fa_unsaved'] %}
|
||||
<br>
|
||||
<label for="codevalidation" class="col-form-label">{{ lng('login.2facode') }}</label>
|
||||
<input type="text" name="codevalidation" id="codevalidation" class="form-control" required/>
|
||||
<input type="hidden" name="type_2fa" id="type_2fa" value="{{ userinfo.type_2fa }}"/>
|
||||
<input type="hidden" name="data_2fa" id="data_2fa" value="{{ userinfo.data_2fa }}"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,6 +44,9 @@
|
||||
<input type="hidden" name="page" value="{{ page }}"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
{% if userinfo.type_2fa == 0 %}
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="preadd">
|
||||
{{ lng('2fa.2fa_add') }}</button>
|
||||
{% elseif userinfo['2fa_unsaved'] is defined and userinfo['2fa_unsaved'] %}
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="add">
|
||||
{{ lng('2fa.2fa_add') }}</button>
|
||||
{% else %}
|
||||
|
||||
Reference in New Issue
Block a user