--- /dev/null
+# Configuring two-factor authentication
+
+* [Home](help)
+
+You can configure two-factor authentication using a mobile app.
+A time-based one-time password (TOTP) application automatically generates an authentication code that changes after a certain period of time.
+
+**Tip**: To configure authentication via TOTP on multiple devices, during setup, scan the QR code using each device at the same time.
+If 2FA is already enabled and you want to add another device, you must re-configure 2FA from your security settings.
+
+## Enabling two-factor authentication
+
+### 1. Download an authenticator app
+
+Any authenticator app should work with Friendica.
+Notheless, we recommend:
+
+ - For iOS, [Matt Rubin's MIT-licensed Authenticator app](https://mattrubin.me/authenticator).
+ - For Android, [andOTP](https://github.com/andOTP/andOTP).
+
+### 2. Record your one-use recovery codes
+
+From your [two-factor authentication user settings](/settings/2fa), enter your password and click on "Enable two-factor authentication".
+
+You will be presented with a list of one-use recovery codes.
+Please save those in the same place you are saving your Friendica password (ideally, in a password manager like [KeePass](https://keepass.info)).
+
+When you're done, click on "Next".
+
+### 3. Setup your authenticator app
+
+You have three methods to setup your authenticator app:
+
+1. Scan the QR Code with your device camera.
+ This will automatically configure your account on the app.
+2. Click/tap on the provided **totp://** URl.
+ Ideally your authenticator app should be called with this URL and set up your account.
+3. Enter your account settings manually.
+ Friendica is using default settings for token type, code digit count and hashing algorithm but you may be required to enter them in your app.
+
+**Tip**: If you have multiple devices, configure them all at this point.
+
+Then verify your app is correctly configured by submitting a code provided by your app.
+This will conclude two-factor authentication configuration.
+
+**Note:** If you leave this screen at any point without having submitted a verification code, two-factor authentication won't be enabled on your account.
+To complete the configuration, just come back to your [two-factor authentication user settings](/settings/2fa) and click on "Finish configuration" after entering your current password.
+
+## Disabling two-factor authentication
+
+You can disable two-factor authentication at any time by going to your [two-factor authentication user settings](/settings/2fa) and click on "Disable two-factor authentication" after entering your current password.
+
+You should remove your Friendica account from your authenticator app as it won't work again even if you reenable two-factor authentication.
+In this case you will have to configure your authenticator app again using the process above.
+
+## Managing your one-time recovery codes
+
+When two-factor authentication is enabled, you can show your recovery codes, including the ones you've already used.
+
+You can freely regenerate a new set of fresh recovery codes, just be sure to replace the previous ones where you saved them as they won't be active anymore.
],
];
+ $tabs[] = [
+ 'label' => L10n::t('Two-factor authentication'),
+ 'url' => 'settings/2fa',
+ 'selected' => (($a->argc > 1) && ($a->argv[1] === '2fa') ? 'active' : ''),
+ 'accesskey' => 'o',
+ ];
+
$tabs[] = [
'label' => L10n::t('Profiles'),
'url' => 'profiles',
$collector->addRoute(['GET'], '/{sub1}/{url}' , Module\Proxy::class);
$collector->addRoute(['GET'], '/{sub1}/{sub2}/{url}' , Module\Proxy::class);
});
+
+ $this->routeCollector->addGroup('/settings', function (RouteCollector $collector) {
+ $collector->addGroup('/2fa', function (RouteCollector $collector) {
+ $collector->addRoute(['GET', 'POST'], '[/]' , Module\Settings\TwoFactor\Index::class);
+ $collector->addRoute(['GET', 'POST'], '/recovery' , Module\Settings\TwoFactor\Recovery::class);
+ $collector->addRoute(['GET', 'POST'], '/verify' , Module\Settings\TwoFactor\Verify::class);
+ });
+ });
$this->routeCollector->addRoute(['GET', 'POST'], '/register', Module\Register::class);
$this->routeCollector->addRoute(['GET'], '/robots.txt', Module\RobotsTxt::class);
$this->routeCollector->addRoute(['GET'], '/rsd.xml', Module\ReallySimpleDiscovery::class);
--- /dev/null
+<?php
+
+namespace Friendica\Module;
+
+use Friendica\BaseModule;
+use Friendica\Content\Feature;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+
+class BaseSettingsModule extends BaseModule
+{
+ public static function content()
+ {
+ $a = self::getApp();
+
+ $tpl = Renderer::getMarkupTemplate('settings/head.tpl');
+ $a->page['htmlhead'] .= Renderer::replaceMacros($tpl, [
+ '$ispublic' => L10n::t('everybody')
+ ]);
+
+ $tabs = [];
+
+ $tabs[] = [
+ 'label' => L10n::t('Account'),
+ 'url' => 'settings',
+ 'selected' => (($a->argc == 1) && ($a->argv[0] === 'settings') ? 'active' : ''),
+ 'accesskey' => 'o',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Two-factor authentication'),
+ 'url' => 'settings/2fa',
+ 'selected' => (($a->argc > 1) && ($a->argv[1] === '2fa') ? 'active' : ''),
+ 'accesskey' => 'o',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Profiles'),
+ 'url' => 'profiles',
+ 'selected' => (($a->argc == 1) && ($a->argv[0] === 'profiles') ? 'active' : ''),
+ 'accesskey' => 'p',
+ ];
+
+ if (Feature::get()) {
+ $tabs[] = [
+ 'label' => L10n::t('Additional features'),
+ 'url' => 'settings/features',
+ 'selected' => (($a->argc > 1) && ($a->argv[1] === 'features') ? 'active' : ''),
+ 'accesskey' => 't',
+ ];
+ }
+
+ $tabs[] = [
+ 'label' => L10n::t('Display'),
+ 'url' => 'settings/display',
+ 'selected' => (($a->argc > 1) && ($a->argv[1] === 'display') ? 'active' : ''),
+ 'accesskey' => 'i',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Social Networks'),
+ 'url' => 'settings/connectors',
+ 'selected' => (($a->argc > 1) && ($a->argv[1] === 'connectors') ? 'active' : ''),
+ 'accesskey' => 'w',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Addons'),
+ 'url' => 'settings/addon',
+ 'selected' => (($a->argc > 1) && ($a->argv[1] === 'addon') ? 'active' : ''),
+ 'accesskey' => 'l',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Delegations'),
+ 'url' => 'delegate',
+ 'selected' => (($a->argc == 1) && ($a->argv[0] === 'delegate') ? 'active' : ''),
+ 'accesskey' => 'd',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Connected apps'),
+ 'url' => 'settings/oauth',
+ 'selected' => (($a->argc > 1) && ($a->argv[1] === 'oauth') ? 'active' : ''),
+ 'accesskey' => 'b',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Export personal data'),
+ 'url' => 'uexport',
+ 'selected' => (($a->argc == 1) && ($a->argv[0] === 'uexport') ? 'active' : ''),
+ 'accesskey' => 'e',
+ ];
+
+ $tabs[] = [
+ 'label' => L10n::t('Remove account'),
+ 'url' => 'removeme',
+ 'selected' => (($a->argc == 1) && ($a->argv[0] === 'removeme') ? 'active' : ''),
+ 'accesskey' => 'r',
+ ];
+
+
+ $tabtpl = Renderer::getMarkupTemplate("generic_links_widget.tpl");
+ $a->page['aside'] = Renderer::replaceMacros($tabtpl, [
+ '$title' => L10n::t('Settings'),
+ '$class' => 'settings-widget',
+ '$items' => $tabs,
+ ]);
+ }
+}
--- /dev/null
+<?php
+
+
+namespace Friendica\Module\Settings\TwoFactor;
+
+
+use Friendica\Core\L10n;
+use Friendica\Core\PConfig;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session;
+use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Model\User;
+use Friendica\Module\BaseSettingsModule;
+use Friendica\Module\Login;
+use PragmaRX\Google2FA\Google2FA;
+
+class Index extends BaseSettingsModule
+{
+ public static function post()
+ {
+ if (!local_user()) {
+ return;
+ }
+
+ self::checkFormSecurityTokenRedirectOnError('settings/2fa', 'settings_2fa');
+
+ try {
+ User::getIdFromPasswordAuthentication(local_user(), defaults($_POST, 'password', ''));
+
+ $has_secret = (bool) PConfig::get(local_user(), '2fa', 'secret');
+ $verified = PConfig::get(local_user(), '2fa', 'verified');
+
+ switch (defaults($_POST, 'action', '')) {
+ case 'enable':
+ if (!$has_secret && !$verified) {
+ $Google2FA = new Google2FA();
+
+ PConfig::set(local_user(), '2fa', 'secret', $Google2FA->generateSecretKey(32));
+
+ self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ }
+ break;
+ case 'disable':
+ if ($has_secret) {
+ TwoFactorRecoveryCode::deleteForUser(local_user());
+ PConfig::delete(local_user(), '2fa', 'secret');
+ PConfig::delete(local_user(), '2fa', 'verified');
+ Session::remove('2fa');
+
+ notice(L10n::t('Two-factor authentication successfully disabled.'));
+ self::getApp()->internalRedirect('settings/2fa');
+ }
+ break;
+ case 'recovery':
+ if ($has_secret) {
+ self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ }
+ break;
+ case 'configure':
+ if (!$verified) {
+ self::getApp()->internalRedirect('settings/2fa/verify?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ }
+ break;
+ }
+ } catch (\Exception $e) {
+ notice(L10n::t('Wrong Password'));
+ }
+ }
+
+ public static function content()
+ {
+ if (!local_user()) {
+ return Login::form('settings/2fa');
+ }
+
+ parent::content();
+
+ $has_secret = (bool) PConfig::get(local_user(), '2fa', 'secret');
+ $verified = PConfig::get(local_user(), '2fa', 'verified');
+
+ return Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/twofactor/index.tpl'), [
+ '$form_security_token' => self::getFormSecurityToken('settings_2fa'),
+ '$title' => L10n::t('Two-factor authentication'),
+ '$help_label' => L10n::t('Help'),
+
+ '$status_title' => L10n::t('Status'),
+ '$message' => L10n::t('<p>Use an application on a mobile device to get two-factor authentication codes when prompted on login.</p>'),
+
+ '$has_secret' => $has_secret,
+ '$verified' => $verified,
+
+ '$auth_app_label' => L10n::t('Authenticator app'),
+ '$app_status' => $has_secret ? $verified ? L10n::t('Configured') : L10n::t('Not Configured') : L10n::t('Disabled'),
+ '$not_configured_message' => L10n::t('<p>You haven\'t finished configuring your authenticator app.</p>'),
+ '$configured_message' => L10n::t('<p>Your authenticator app is correctly configured.</p>'),
+
+ '$recovery_codes_title' => L10n::t('Recovery codes'),
+ '$recovery_codes_remaining' => L10n::t('Remaining valid codes'),
+ '$recovery_codes_count' => TwoFactorRecoveryCode::countValidForUser(local_user()),
+ '$recovery_codes_message' => L10n::t('<p>These one-use codes can replace an authenticator app code in case you have lost access to it.</p>'),
+
+ '$action_title' => L10n::t('Actions'),
+ '$password' => ['password', L10n::t('Current password:'), '', L10n::t('You need to provide your current password to change two-factor authentication settings.'), 'required', 'autofocus'],
+
+ '$enable_label' => L10n::t('Enable two-factor authentication'),
+ '$disable_label' => L10n::t('Disable two-factor authentication'),
+ '$recovery_codes_label' => L10n::t('Show recovery codes'),
+ '$configure_label' => L10n::t('Finish app configuration'),
+ ]);
+ }
+}
--- /dev/null
+<?php
+
+
+namespace Friendica\Module\Settings\TwoFactor;
+
+
+use Friendica\Core\L10n;
+use Friendica\Core\PConfig;
+use Friendica\Core\Renderer;
+use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Module\BaseSettingsModule;
+use Friendica\Module\Login;
+
+/**
+ * // Page 3: 2FA enabled but not verified, show recovery codes
+ *
+ * @package Friendica\Module\TwoFactor
+ */
+class Recovery extends BaseSettingsModule
+{
+ public static function init()
+ {
+ if (!local_user()) {
+ return;
+ }
+
+ $secret = PConfig::get(local_user(), '2fa', 'secret');
+
+ if (!$secret) {
+ self::getApp()->internalRedirect('settings/2fa');
+ }
+
+ if (!self::checkFormSecurityToken('settings_2fa_password', 't')) {
+ notice(L10n::t('Please enter your password to access this page.'));
+ self::getApp()->internalRedirect('settings/2fa');
+ }
+ }
+
+ public static function post()
+ {
+ if (!local_user()) {
+ return;
+ }
+
+ if (!empty($_POST['action'])) {
+ self::checkFormSecurityTokenRedirectOnError('settings/2fa/recovery', 'settings_2fa_recovery');
+
+ if ($_POST['action'] == 'regenerate') {
+ TwoFactorRecoveryCode::regenerateForUser(local_user());
+ notice(L10n::t('New recovery codes successfully generated.'));
+ self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ }
+ }
+ }
+
+ public static function content()
+ {
+ if (!local_user()) {
+ return Login::form('settings/2fa/recovery');
+ }
+
+ parent::content();
+
+ if (!TwoFactorRecoveryCode::countValidForUser(local_user())) {
+ TwoFactorRecoveryCode::generateForUser(local_user());
+ }
+
+ $recoveryCodes = TwoFactorRecoveryCode::getListForUser(local_user());
+
+ $verified = PConfig::get(local_user(), '2fa', 'verified');
+
+ return Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/twofactor/recovery.tpl'), [
+ '$form_security_token' => self::getFormSecurityToken('settings_2fa_recovery'),
+ '$password_security_token' => self::getFormSecurityToken('settings_2fa_password'),
+ '$title' => L10n::t('Two-factor recovery codes'),
+ '$help_label' => L10n::t('Help'),
+ '$message' => L10n::t('<p>Recovery codes can be used to access your account in the event you lose access to your device and cannot receive two-factor authentication codes.</p><p><strong>Put these in a safe spot!</strong> If you lose your device and don’t have the recovery codes you will lose access to your account.</p>'),
+ '$recovery_codes' => $recoveryCodes,
+ '$password' => ['password', L10n::t('Please enter your password for verification:'), '', L10n::t('You need to provide your current password to enable or disable two-factor authentication.'), 'required', 'autofocus'],
+ '$regenerate_message' => L10n::t('When you generate new recovery codes, you must copy the new codes. Your old codes won’t work anymore.'),
+ '$regenerate_label' => L10n::t('Generate new recovery codes'),
+ '$verified' => $verified,
+ '$verify_label' => L10n::t('Next: Verification'),
+ ]);
+ }
+}
--- /dev/null
+<?php
+
+
+namespace Friendica\Module\Settings\TwoFactor;
+
+
+use BaconQrCode\Renderer\Image\SvgImageBackEnd;
+use BaconQrCode\Renderer\ImageRenderer;
+use BaconQrCode\Renderer\RendererStyle\RendererStyle;
+use BaconQrCode\Writer;
+use Friendica\BaseModule;
+use Friendica\Core\L10n;
+use Friendica\Core\PConfig;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session;
+use Friendica\Module\BaseSettingsModule;
+use Friendica\Module\Login;
+use PragmaRX\Google2FA\Google2FA;
+
+/**
+ * // Page 4: 2FA enabled but not verified, QR code and verification
+ *
+ * @package Friendica\Module\TwoFactor\Settings
+ */
+class Verify extends BaseSettingsModule
+{
+ public static function init()
+ {
+ if (!local_user()) {
+ return;
+ }
+
+ $secret = PConfig::get(local_user(), '2fa', 'secret');
+ $verified = PConfig::get(local_user(), '2fa', 'verified');
+
+ if ($secret && $verified) {
+ self::getApp()->internalRedirect('settings/2fa');
+ }
+
+ if (!self::checkFormSecurityToken('settings_2fa_password', 't')) {
+ notice(L10n::t('Please enter your password to access this page.'));
+ self::getApp()->internalRedirect('settings/2fa');
+ }
+ }
+
+ public static function post()
+ {
+ if (!local_user()) {
+ return;
+ }
+
+ if (defaults($_POST, 'action', null) == 'verify') {
+ self::checkFormSecurityTokenRedirectOnError('settings/2fa/verify', 'settings_2fa_verify');
+
+ $google2fa = new Google2FA();
+
+ $valid = $google2fa->verifyKey(PConfig::get(local_user(), '2fa', 'secret'), defaults($_POST, 'verify_code', ''));
+
+ if ($valid) {
+ PConfig::set(local_user(), '2fa', 'verified', true);
+ Session::set('2fa', true);
+
+ notice(L10n::t('Two-factor authentication successfully activated.'));
+
+ self::getApp()->internalRedirect('settings/2fa');
+ } else {
+ notice(L10n::t('Invalid code, please retry.'));
+ }
+ }
+ }
+
+ public static function content()
+ {
+ if (!local_user()) {
+ return Login::form('settings/2fa/verify');
+ }
+
+ parent::content();
+
+ $company = 'Friendica';
+ $holder = Session::get('my_address');
+ $secret = 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('<?xml version="1.0" encoding="UTF-8"?>', '', $writer->writeString($otpauthUrl));
+
+ $shortOtpauthUrl = explode('?', $otpauthUrl)[0];
+
+ $manual_message = L10n::t('<p>Or you can submit the authentication settings manually:</p>
+<dl>
+ <dt>Issuer</dt>
+ <dd>%s</dd>
+ <dt>Account Name</dt>
+ <dd>%s</dd>
+ <dt>Secret Key</dt>
+ <dd>%s</dd>
+ <dt>Type</dt>
+ <dd>Time-based</dd>
+ <dt>Number of digits</dt>
+ <dd>6</dd>
+ <dt>Hashing algorithm</dt>
+ <dd>SHA-1</dd>
+</dl>', $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' => L10n::t('Two-factor code verification'),
+ '$help_label' => L10n::t('Help'),
+ '$message' => L10n::t('<p>Please scan this QR Code with your authenticator app and submit the provided code.</p>'),
+ '$qrcode_image' => $qrcode_image,
+ '$qrcode_url_message' => L10n::t('<p>Or you can open the following URL in your mobile devicde:</p><p><a href="%s">%s</a></p>', $otpauthUrl, $shortOtpauthUrl),
+ '$manual_message' => $manual_message,
+ '$company' => $company,
+ '$holder' => $holder,
+ '$secret' => $secret,
+
+ '$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 enable two-factor authentication'),
+ ]);
+ }
+}
--- /dev/null
+<div class="generic-page-wrapper">
+ <h1>{{$title}} <a href="help/Two-Factor-Authentication" title="{{$help_label}}" class="btn btn-default btn-sm"><i aria-hidden="true" class="fa fa-question fa-2x"></i></a></h1>
+ <div>{{$message nofilter}}</div>
+ <h2>{{$status_title}}</h2>
+ <p><strong>{{$auth_app_label}}</strong>: {{$app_status}} </p>
+{{if $has_secret && $verified}}
+ <div>{{$configured_message nofilter}}</div>
+{{/if}}
+{{if $has_secret && !$verified}}
+ <div>{{$not_configured_message nofilter}}</div>
+{{/if}}
+
+{{if $has_secret && $verified}}
+ <h2>{{$recovery_codes_title}}</h2>
+ <p><strong>{{$recovery_codes_remaining}}</strong>: {{$recovery_codes_count}}</p>
+ <div>{{$recovery_codes_message nofilter}}</div>
+{{/if}}
+
+ <form action="settings/2fa" method="post">
+ <h2>{{$action_title}}</h2>
+ <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+
+ {{include file="field_password.tpl" field=$password}}
+
+ <div class="form-group settings-submit-wrapper" >
+{{if !$has_secret}}
+ <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="enable">{{$enable_label}}</button>
+{{else}}
+ <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="disable">{{$disable_label}}</button>
+{{/if}}
+{{if $has_secret && $verified}}
+ <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="recovery">{{$recovery_codes_label}}</button>
+{{/if}}
+{{if $has_secret && !$verified}}
+ <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="configure">{{$configure_label}}</button>
+{{/if}}
+ </div>
+ </form>
+</div>
--- /dev/null
+<div class="generic-page-wrapper">
+ <h1>{{$title}} <a href="help/Two-Factor-Authentication" title="{{$help_label}}" class="btn btn-default btn-sm"><i aria-hidden="true" class="fa fa-question fa-2x"></i></a></h1>
+ <div>{{$message nofilter}}</div>
+
+ <ul class="recovery-codes">
+{{foreach $recovery_codes as $recovery_code}}
+ <li>
+ {{if $recovery_code.used}}<s>{{/if}}
+ {{$recovery_code.code}}
+ {{if $recovery_code.used}}</s>{{/if}}
+ </li>
+{{/foreach}}
+ </ul>
+
+{{if $verified}}
+ <form action="settings/2fa/recovery?t={{$password_security_token}}" method="post">
+ <h2>{{$regenerate_label}}</h2>
+ <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+ <div>{{$regenerate_message}}</div>
+
+ <div class="form-group pull-right settings-submit-wrapper" >
+ <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="regenerate">{{$regenerate_label}}</button>
+ </div>
+ </form>
+{{else}}
+ <p class="text-right"><a href="settings/2fa/verify?t={{$password_security_token}}" class="btn btn-primary">{{$verify_label}}</a></p>
+{{/if}}
+</div>
--- /dev/null
+<div class="generic-page-wrapper">
+ <h1>{{$title}} <a href="help/Two-Factor-Authentication" title="{{$help_label}}" class="btn btn-default btn-sm"><i aria-hidden="true" class="fa fa-question fa-2x"></i></a></h1>
+ <div>{{$message nofilter}}</div>
+
+ <div class="text-center">
+ {{$qrcode_image nofilter}}
+ </div>
+
+ <form action="settings/2fa/verify?t={{$password_security_token}}" 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>{{$qrcode_url_message nofilter}}</div>
+
+ <div>{{$manual_message nofilter}}</div>
+</div>