- Add 2FA login interception in Session::setAuthenticatedForUser - Add 2fa session variable holding the last auth codepull/7135/head
@ -0,0 +1,71 @@ | |||
<?php | |||
namespace Friendica\Module\TwoFactor; | |||
use Friendica\BaseModule; | |||
use Friendica\Core\L10n; | |||
use Friendica\Core\Renderer; | |||
use Friendica\Core\Session; | |||
use Friendica\Model\TwoFactorRecoveryCode; | |||
/** | |||
* // Page 1a: Recovery code verification | |||
* | |||
* @package Friendica\Module\TwoFactor | |||
*/ | |||
class Recovery extends BaseModule | |||
{ | |||
public static function init() | |||
{ | |||
if (!local_user()) { | |||
return; | |||
} | |||
} | |||
public static function post() | |||
{ | |||
if (!local_user()) { | |||
return; | |||
} | |||
if (defaults($_POST, 'action', null) == 'recover') { | |||
self::checkFormSecurityTokenRedirectOnError('2fa', 'twofactor_recovery'); | |||
$a = self::getApp(); | |||
$recovery_code = defaults($_POST, 'recovery_code', ''); | |||
if (TwoFactorRecoveryCode::existsForUser(local_user(), $recovery_code)) { | |||
TwoFactorRecoveryCode::markUsedForUser(local_user(), $recovery_code); | |||
Session::set('2fa', true); | |||
notice(L10n::t('Remaining recovery codes: %d', TwoFactorRecoveryCode::countValidForUser(local_user()))); | |||
// Resume normal login workflow | |||
Session::setAuthenticatedForUser($a, $a->user, true, true); | |||
} else { | |||
notice(L10n::t('Invalid code, please retry.')); | |||
} | |||
} | |||
} | |||
public static function content() | |||
{ | |||
if (!local_user()) { | |||
self::getApp()->internalRedirect(); | |||
} | |||
// Already authenticated with 2FA token | |||
if (Session::get('2fa')) { | |||
self::getApp()->internalRedirect(); | |||
} | |||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('twofactor/recovery.tpl'), [ | |||
'$form_security_token' => self::getFormSecurityToken('twofactor_recovery'), | |||
'$title' => L10n::t('Two-factor recovery'), | |||
'$message' => L10n::t('<p>You can enter one of your one-time recovery codes in case you lost access to your mobile device.</p>'), | |||
'$recovery_message' => L10n::t('Don’t have your phone? <a href="%s">Enter a two-factor recovery code</a>', '2fa/recovery'), | |||
'$recovery_code' => ['recovery_code', L10n::t('Please enter a recovery code'), '', '', '', 'placeholder="000000-000000"'], | |||
'$recovery_label' => L10n::t('Submit recovery code and complete login'), | |||
]); | |||
} | |||
} |
@ -0,0 +1,66 @@ | |||
<?php | |||
namespace Friendica\Module\TwoFactor; | |||
use Friendica\BaseModule; | |||
use Friendica\Core\L10n; | |||
use Friendica\Core\PConfig; | |||
use Friendica\Core\Renderer; | |||
use Friendica\Core\Session; | |||
use PragmaRX\Google2FA\Google2FA; | |||
/** | |||
* Page 1: Authenticator code verification | |||
* | |||
* @package Friendica\Module\TwoFactor | |||
*/ | |||
class Verify extends BaseModule | |||
{ | |||
public static function post() | |||
{ | |||
if (!local_user()) { | |||
return; | |||
} | |||
if (defaults($_POST, 'action', null) == 'verify') { | |||
self::checkFormSecurityTokenRedirectOnError('2fa', 'twofactor_verify'); | |||
$a = self::getApp(); | |||
$code = defaults($_POST, 'verify_code', ''); | |||
$valid = (new Google2FA())->verifyKey(PConfig::get(local_user(), '2fa', 'secret'), $code); | |||
// The same code can't be used twice even if it's valid | |||
if ($valid && Session::get('2fa') !== $code) { | |||
Session::set('2fa', $code); | |||
// Resume normal login workflow | |||
Session::setAuthenticatedForUser($a, $a->user, true, true); | |||
} else { | |||
notice(L10n::t('Invalid code, please retry.')); | |||
} | |||
} | |||
} | |||
public static function content() | |||
{ | |||
if (!local_user()) { | |||
self::getApp()->internalRedirect(); | |||
} | |||
// Already authenticated with 2FA token | |||
if (Session::get('2fa')) { | |||
self::getApp()->internalRedirect(); | |||
} | |||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('twofactor/verify.tpl'), [ | |||
'$form_security_token' => self::getFormSecurityToken('twofactor_verify'), | |||
'$title' => L10n::t('Two-factor authentication'), | |||
'$message' => L10n::t('<p>Open the two-factor authentication app on your device to get an authentication code and verify your identity.</p>'), | |||
'$recovery_message' => L10n::t('Don’t have your phone? <a href="%s">Enter a two-factor recovery code</a>', '2fa/recovery'), | |||
'$verify_code' => ['verify_code', L10n::t('Please enter a code from your authentication app'), '', '', 'required', 'autofocus placeholder="000000"'], | |||
'$verify_label' => L10n::t('Verify code and complete login'), | |||
]); | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
<div class="generic-page-wrapper"> | |||
<h1>{{$title}}</h1> | |||
<div>{{$message nofilter}}</div> | |||
<form action="" method="post"> | |||
<input type="hidden" name="form_security_token" value="{{$form_security_token}}"> | |||
{{include file="field_input.tpl" field=$recovery_code}} | |||
<div class="form-group settings-submit-wrapper"> | |||
<button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="recover">{{$recovery_label}}</button> | |||
</div> | |||
</form> | |||
</div> |
@ -0,0 +1,15 @@ | |||
<div class="generic-page-wrapper"> | |||
<h1>{{$title}}</h1> | |||
<div>{{$message nofilter}}</div> | |||
<form action="" method="post"> | |||
<input type="hidden" name="form_security_token" value="{{$form_security_token}}"> | |||
{{include file="field_input.tpl" field=$verify_code}} | |||
<div class="form-group settings-submit-wrapper"> | |||
<button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="verify">{{$verify_label}}</button> | |||
</div> | |||
</form> | |||
<div>{{$recovery_message nofilter}}</div> | |||
</div> |