. * */ namespace Friendica\Module\Settings\TwoFactor; use BaconQrCode\Renderer\Image\SvgImageBackEnd; use BaconQrCode\Renderer\ImageRenderer; use BaconQrCode\Renderer\RendererStyle\RendererStyle; use BaconQrCode\Writer; use Friendica\App; use Friendica\Core\L10n; use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues; use Friendica\Core\Renderer; use Friendica\Core\Session; use Friendica\Module\BaseSettings; use Friendica\Module\Response; use Friendica\Module\Security\Login; use Friendica\Util\Profiler; use PragmaRX\Google2FA\Google2FA; use Psr\Log\LoggerInterface; /** * // Page 4: 2FA enabled but not verified, QR code and verification * * @package Friendica\Module\TwoFactor\Settings */ class Verify extends BaseSettings { /** @var IManagePersonalConfigValues */ protected $pConfig; public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IManagePersonalConfigValues $pConfig, array $server, array $parameters = []) { parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); $this->pConfig = $pConfig; if (!local_user()) { return; } $secret = $this->pConfig->get(local_user(), '2fa', 'secret'); $verified = $this->pConfig->get(local_user(), '2fa', 'verified'); if ($secret && $verified) { $this->baseUrl->redirect('settings/2fa'); } if (!self::checkFormSecurityToken('settings_2fa_password', 't')) { notice($this->t('Please enter your password to access this page.')); $this->baseUrl->redirect('settings/2fa'); } } protected function post(array $request = [], array $post = []) { if (!local_user()) { return; } if (($_POST['action'] ?? '') == 'verify') { self::checkFormSecurityTokenRedirectOnError('settings/2fa/verify', 'settings_2fa_verify'); $google2fa = new Google2FA(); $valid = $google2fa->verifyKey($this->pConfig->get(local_user(), '2fa', 'secret'), $_POST['verify_code'] ?? ''); if ($valid) { $this->pConfig->set(local_user(), '2fa', 'verified', true); Session::set('2fa', true); info($this->t('Two-factor authentication successfully activated.')); $this->baseUrl->redirect('settings/2fa'); } else { notice($this->t('Invalid code, please retry.')); } } } protected function content(array $request = []): string { if (!local_user()) { return Login::form('settings/2fa/verify'); } parent::content(); $company = 'Friendica'; $holder = Session::get('my_address'); $secret = $this->pConfig->get(local_user(), '2fa', 'secret'); $otpauthUrl = (new Google2FA())->getQRCodeUrl($company, $holder, $secret); $renderer = (new \BaconQrCode\Renderer\Image\Svg()) ->setHeight(256) ->setWidth(256); $writer = new Writer($renderer); $qrcode_image = str_replace('', '', $writer->writeString($otpauthUrl)); $shortOtpauthUrl = explode('?', $otpauthUrl)[0]; $manual_message = $this->t('

Or you can submit the authentication settings manually:

Issuer
%s
Account Name
%s
Secret Key
%s
Type
Time-based
Number of digits
6
Hashing algorithm
SHA-1
', $company, $holder, $secret); return Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/twofactor/verify.tpl'), [ '$form_security_token' => self::getFormSecurityToken('settings_2fa_verify'), '$password_security_token' => self::getFormSecurityToken('settings_2fa_password'), '$title' => $this->t('Two-factor code verification'), '$help_label' => $this->t('Help'), '$message' => $this->t('

Please scan this QR Code with your authenticator app and submit the provided code.

'), '$qrcode_image' => $qrcode_image, '$qrcode_url_message' => $this->t('

Or you can open the following URL in your mobile device:

%s

', $otpauthUrl, $shortOtpauthUrl), '$manual_message' => $manual_message, '$company' => $company, '$holder' => $holder, '$secret' => $secret, '$verify_code' => ['verify_code', $this->t('Please enter a code from your authentication app'), '', '', $this->t('Required'), 'autofocus autocomplete="off" placeholder="000000"'], '$verify_label' => $this->t('Verify code and enable two-factor authentication'), ]); } }