From d7fdb42488a6fd9b27e3793308f542fccef8c867 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 22 Jul 2019 07:39:28 -0400 Subject: [PATCH 1/6] Database Structure Version 1320 - Add 2fa_app_specific_password --- static/dbstructure.config.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 4f75eac46..aca6cf16b 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -34,10 +34,25 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1319); + define('DB_UPDATE_VERSION', 1320); } return [ + "2fa_app_specific_password" => [ + "comment" => "Two-factor app-specific _password", + "fields" => [ + "id" => ["type" => "mediumint unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "Password ID for revocation"], + "uid" => ["type" => "mediumint unsigned", "not null" => "1", "relation" => ["user" => "uid"], "comment" => "User ID"], + "description" => ["type" => "varchar(255)", "comment" => "Description of the usage of the password"], + "hashed_password" => ["type" => "varchar(255)", "not null" => "1", "primary" => "1", "comment" => "Hashed password"], + "generated" => ["type" => "datetime", "not null" => "1", "comment" => "Datetime the password was generated"], + "last_used" => ["type" => "datetime", "comment" => "Datetime the password was last used"], + ], + "indexes" => [ + "PRIMARY" => ["id"], + "uid_description" => ["uid", "description"], + ] + ], "2fa_recovery_codes" => [ "comment" => "Two-factor authentication recovery codes", "fields" => [ From a149d6ec4448ee6c38f2b6ee4c2e907cdf20d5b3 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 22 Jul 2019 07:41:01 -0400 Subject: [PATCH 2/6] Move recovery code model to sub-folder --- .../RecoveryCode.php} | 4 ++-- src/Module/Settings/TwoFactor/Index.php | 6 +++--- src/Module/Settings/TwoFactor/Recovery.php | 10 +++++----- src/Module/TwoFactor/Recovery.php | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) rename src/Model/{TwoFactorRecoveryCode.php => TwoFactor/RecoveryCode.php} (97%) diff --git a/src/Model/TwoFactorRecoveryCode.php b/src/Model/TwoFactor/RecoveryCode.php similarity index 97% rename from src/Model/TwoFactorRecoveryCode.php rename to src/Model/TwoFactor/RecoveryCode.php index 74dead32d..676b53837 100644 --- a/src/Model/TwoFactorRecoveryCode.php +++ b/src/Model/TwoFactor/RecoveryCode.php @@ -1,6 +1,6 @@ L10n::t('Recovery codes'), '$recovery_codes_remaining' => L10n::t('Remaining valid codes'), - '$recovery_codes_count' => TwoFactorRecoveryCode::countValidForUser(local_user()), + '$recovery_codes_count' => RecoveryCode::countValidForUser(local_user()), '$recovery_codes_message' => L10n::t('

These one-use codes can replace an authenticator app code in case you have lost access to it.

'), '$action_title' => L10n::t('Actions'), diff --git a/src/Module/Settings/TwoFactor/Recovery.php b/src/Module/Settings/TwoFactor/Recovery.php index a2d08cda8..6937fa503 100644 --- a/src/Module/Settings/TwoFactor/Recovery.php +++ b/src/Module/Settings/TwoFactor/Recovery.php @@ -7,7 +7,7 @@ namespace Friendica\Module\Settings\TwoFactor; use Friendica\Core\L10n; use Friendica\Core\PConfig; use Friendica\Core\Renderer; -use Friendica\Model\TwoFactorRecoveryCode; +use Friendica\Model\TwoFactor\RecoveryCode; use Friendica\Module\BaseSettingsModule; use Friendica\Module\Login; @@ -46,7 +46,7 @@ class Recovery extends BaseSettingsModule self::checkFormSecurityTokenRedirectOnError('settings/2fa/recovery', 'settings_2fa_recovery'); if ($_POST['action'] == 'regenerate') { - TwoFactorRecoveryCode::regenerateForUser(local_user()); + RecoveryCode::regenerateForUser(local_user()); notice(L10n::t('New recovery codes successfully generated.')); self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password')); } @@ -61,11 +61,11 @@ class Recovery extends BaseSettingsModule parent::content(); - if (!TwoFactorRecoveryCode::countValidForUser(local_user())) { - TwoFactorRecoveryCode::generateForUser(local_user()); + if (!RecoveryCode::countValidForUser(local_user())) { + RecoveryCode::generateForUser(local_user()); } - $recoveryCodes = TwoFactorRecoveryCode::getListForUser(local_user()); + $recoveryCodes = RecoveryCode::getListForUser(local_user()); $verified = PConfig::get(local_user(), '2fa', 'verified'); diff --git a/src/Module/TwoFactor/Recovery.php b/src/Module/TwoFactor/Recovery.php index 4952d220b..60f443c35 100644 --- a/src/Module/TwoFactor/Recovery.php +++ b/src/Module/TwoFactor/Recovery.php @@ -6,7 +6,7 @@ use Friendica\BaseModule; use Friendica\Core\L10n; use Friendica\Core\Renderer; use Friendica\Core\Session; -use Friendica\Model\TwoFactorRecoveryCode; +use Friendica\Model\TwoFactor\RecoveryCode; /** * // Page 1a: Recovery code verification @@ -35,10 +35,10 @@ class Recovery extends BaseModule $recovery_code = defaults($_POST, 'recovery_code', ''); - if (TwoFactorRecoveryCode::existsForUser(local_user(), $recovery_code)) { - TwoFactorRecoveryCode::markUsedForUser(local_user(), $recovery_code); + if (RecoveryCode::existsForUser(local_user(), $recovery_code)) { + RecoveryCode::markUsedForUser(local_user(), $recovery_code); Session::set('2fa', true); - notice(L10n::t('Remaining recovery codes: %d', TwoFactorRecoveryCode::countValidForUser(local_user()))); + notice(L10n::t('Remaining recovery codes: %d', RecoveryCode::countValidForUser(local_user()))); // Resume normal login workflow Session::setAuthenticatedForUser($a, $a->user, true, true); From 1a164b0dc58551d85897c939f57eb2d8bb0757fb Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 22 Jul 2019 07:56:00 -0400 Subject: [PATCH 3/6] Add two-factor app-specific password settings page - Add two-factor app-specific model - Add link to new page from 2fa settings index page --- src/App/Router.php | 1 + src/Model/TwoFactor/AppSpecificPassword.php | 137 ++++++++++++++++++ src/Module/Settings/TwoFactor/AppSpecific.php | 118 +++++++++++++++ src/Module/Settings/TwoFactor/Index.php | 12 ++ .../settings/twofactor/app_specific.tpl | 57 ++++++++ view/templates/settings/twofactor/index.tpl | 11 +- 6 files changed, 330 insertions(+), 6 deletions(-) create mode 100644 src/Model/TwoFactor/AppSpecificPassword.php create mode 100644 src/Module/Settings/TwoFactor/AppSpecific.php create mode 100644 view/templates/settings/twofactor/app_specific.tpl diff --git a/src/App/Router.php b/src/App/Router.php index 9e30ed351..a54f3a711 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -202,6 +202,7 @@ class Router $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'], '/app_specific' , Module\Settings\TwoFactor\AppSpecific::class); $collector->addRoute(['GET', 'POST'], '/verify' , Module\Settings\TwoFactor\Verify::class); }); }); diff --git a/src/Model/TwoFactor/AppSpecificPassword.php b/src/Model/TwoFactor/AppSpecificPassword.php new file mode 100644 index 000000000..41b1e3bac --- /dev/null +++ b/src/Model/TwoFactor/AppSpecificPassword.php @@ -0,0 +1,137 @@ + $uid]); + } + + public static function checkDuplicateForUser($uid, $description) + { + return DBA::exists('2fa_app_specific_password', ['uid' => $uid, 'description' => $description]); + } + + /** + * Checks the provided hashed_password is available to use for login by the provided user + * + * @param int $uid User ID + * @param string $plaintextPassword + * @return bool + * @throws \Exception + */ + public static function authenticateUser($uid, $plaintextPassword) + { + $appSpecificPasswords = self::getListForUser($uid); + + $return = false; + + foreach ($appSpecificPasswords as $appSpecificPassword) { + if (password_verify($plaintextPassword, $appSpecificPassword['hashed_password'])) { + $fields = ['last_used' => DateTimeFormat::utcNow()]; + if (password_needs_rehash($appSpecificPassword['hashed_password'], PASSWORD_DEFAULT)) { + $fields['hashed_password'] = User::hashPassword($plaintextPassword); + } + + self::update($appSpecificPassword['id'], $fields); + + $return |= true; + } + } + + return $return; + } + + /** + * Returns a complete list of all recovery hashed_passwords for the provided user, including the used status + * + * @param int $uid User ID + * @return array + * @throws \Exception + */ + public static function getListForUser($uid) + { + $appSpecificPasswordsStmt = DBA::select('2fa_app_specific_password', ['id', 'description', 'hashed_password', 'last_used'], ['uid' => $uid]); + + $appSpecificPasswords = DBA::toArray($appSpecificPasswordsStmt); + + array_walk($appSpecificPasswords, function (&$value) { + $value['ago'] = Temporal::getRelativeDate($value['last_used']); + }); + + return $appSpecificPasswords; + } + + /** + * Generates a new app specific password for the provided user and hashes it in the database. + * + * @param int $uid User ID + * @param string $description Password description + * @return array The new app-specific password data structure with the plaintext password added + * @throws \Exception + */ + public static function generateForUser(int $uid, $description) + { + $Random = (new Random())->size(40); + + $plaintextPassword = $Random->get(); + + $generated = DateTimeFormat::utcNow(); + + $fields = [ + 'uid' => $uid, + 'description' => $description, + 'hashed_password' => User::hashPassword($plaintextPassword), + 'generated' => $generated, + ]; + + DBA::insert('2fa_app_specific_password', $fields); + + $fields['id'] = DBA::lastInsertId(); + $fields['plaintext_password'] = $plaintextPassword; + + return $fields; + } + + private static function update($appSpecificPasswordId, $fields) + { + return DBA::update('2fa_app_specific_password', $fields, ['id' => $appSpecificPasswordId]); + } + + /** + * Deletes all the recovery hashed_passwords for the provided user. + * + * @param int $uid User ID + * @return bool + * @throws \Exception + */ + public static function deleteAllForUser(int $uid) + { + return DBA::delete('2fa_app_specific_password', ['uid' => $uid]); + } + + /** + * @param int $uid + * @param int $app_specific_password_id + * @return bool + * @throws \Exception + */ + public static function deleteForUser(int $uid, int $app_specific_password_id) + { + return DBA::delete('2fa_app_specific_password', ['id' => $app_specific_password_id, 'uid' => $uid]); + } +} diff --git a/src/Module/Settings/TwoFactor/AppSpecific.php b/src/Module/Settings/TwoFactor/AppSpecific.php new file mode 100644 index 000000000..17ff89071 --- /dev/null +++ b/src/Module/Settings/TwoFactor/AppSpecific.php @@ -0,0 +1,118 @@ +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/app_specific', 'settings_2fa_app_specific'); + + switch ($_POST['action']) { + case 'generate': + $description = $_POST['description'] ?? ''; + if (empty($description)) { + notice(L10n::t('App-specific password generation failed: The description is empty.')); + self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password')); + } elseif (AppSpecificPassword::checkDuplicateForUser(local_user(), $description)) { + notice(L10n::t('App-specific password generation failed: This description already exists.')); + self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password')); + } else { + self::$appSpecificPassword = AppSpecificPassword::generateForUser(local_user(), $_POST['description'] ?? ''); + notice(L10n::t('New app-specific password generated.')); + } + + break; + case 'revoke_all' : + AppSpecificPassword::deleteAllForUser(local_user()); + notice(L10n::t('App-specific passwords successfully revoked.')); + self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password')); + break; + } + } + + if (!empty($_POST['revoke_id'])) { + self::checkFormSecurityTokenRedirectOnError('settings/2fa/app_specific', 'settings_2fa_app_specific'); + + if (AppSpecificPassword::deleteForUser(local_user(), $_POST['revoke_id'])) { + notice(L10n::t('App-specific password successfully revoked.')); + } + + self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password')); + } + } + + public static function content() + { + if (!local_user()) { + return Login::form('settings/2fa/app_specific'); + } + + parent::content(); + + $appSpecificPasswords = AppSpecificPassword::getListForUser(local_user()); + + var_dump($appSpecificPasswords); + + return Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/twofactor/app_specific.tpl'), [ + '$form_security_token' => self::getFormSecurityToken('settings_2fa_app_specific'), + '$password_security_token' => self::getFormSecurityToken('settings_2fa_password'), + + '$title' => L10n::t('Two-factor app-specific passwords'), + '$help_label' => L10n::t('Help'), + '$message' => L10n::t('

App-specific passwords are randomly generated passwords used instead your regular password to authenticate your account on third-party applications that don\'t support two-factor authentication.

'), + '$generated_message' => L10n::t('Make sure to copy your new app-specific password now. You won’t be able to see it again!'), + '$generated_app_specific_password' => self::$appSpecificPassword, + + '$description_label' => L10n::t('Description'), + '$last_used_label' => L10n::t('Last Used'), + '$revoke_label' => L10n::t('Revoke'), + '$revoke_all_label' => L10n::t('Revoke All'), + + '$app_specific_passwords' => $appSpecificPasswords, + '$generate_message' => L10n::t('When you generate a new app-specific password, you must use it right away, it will be shown to you once after you generate it.'), + '$generate_title' => L10n::t('Generate new app-specific password'), + '$description_placeholder_label' => L10n::t('Friendiqa on my Fairphone 2...'), + '$generate_label' => L10n::t('Generate'), + ]); + } +} diff --git a/src/Module/Settings/TwoFactor/Index.php b/src/Module/Settings/TwoFactor/Index.php index 0ae271a35..79b92f159 100644 --- a/src/Module/Settings/TwoFactor/Index.php +++ b/src/Module/Settings/TwoFactor/Index.php @@ -8,6 +8,7 @@ use Friendica\Core\L10n; use Friendica\Core\PConfig; use Friendica\Core\Renderer; use Friendica\Core\Session; +use Friendica\Model\TwoFactor\AppSpecificPassword; use Friendica\Model\TwoFactor\RecoveryCode; use Friendica\Model\User; use Friendica\Module\BaseSettingsModule; @@ -56,6 +57,11 @@ class Index extends BaseSettingsModule self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password')); } break; + case 'app_specific': + if ($has_secret) { + self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password')); + } + break; case 'configure': if (!$verified) { self::getApp()->internalRedirect('settings/2fa/verify?t=' . self::getFormSecurityToken('settings_2fa_password')); @@ -97,11 +103,17 @@ class Index extends BaseSettingsModule '$recovery_codes_count' => RecoveryCode::countValidForUser(local_user()), '$recovery_codes_message' => L10n::t('

These one-use codes can replace an authenticator app code in case you have lost access to it.

'), + '$app_specific_passwords_title' => L10n::t('App-specific passwords'), + '$app_specific_passwords_remaining' => L10n::t('Generated app-specific passwords'), + '$app_specific_passwords_count' => AppSpecificPassword::countForUser(local_user()), + '$app_specific_passwords_message' => L10n::t('

These randomly generated passwords allow you to authenticate on apps not supporting two-factor authentication.

'), + '$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'), + '$app_specific_passwords_label' => L10n::t('Manage app-specific passwords'), '$configure_label' => L10n::t('Finish app configuration'), ]); } diff --git a/view/templates/settings/twofactor/app_specific.tpl b/view/templates/settings/twofactor/app_specific.tpl new file mode 100644 index 000000000..1f5826744 --- /dev/null +++ b/view/templates/settings/twofactor/app_specific.tpl @@ -0,0 +1,57 @@ +
+

{{$title}}

+
{{$message nofilter}}
+ +{{if $generated_app_specific_password}} +
+
+ ✅ {{$generated_app_specific_password.plaintext_password}} +
+
+ {{$generated_message}} +
+
+{{/if}} + +
+ + + + + + + + + + +{{foreach $app_specific_passwords as $app_specific_password}} + + + + + +{{/foreach}} + +
{{$description_label}}{{$last_used_label}}
+ {{$app_specific_password.description}} + + + + + + +
+
+
+ +

{{$generate_title}}

+

{{$generate_message}}

+
+ + +
+

+ +

+
+
diff --git a/view/templates/settings/twofactor/index.tpl b/view/templates/settings/twofactor/index.tpl index 57bcab19e..6cf3fac11 100644 --- a/view/templates/settings/twofactor/index.tpl +++ b/view/templates/settings/twofactor/index.tpl @@ -22,18 +22,17 @@ {{include file="field_password.tpl" field=$password}} -
{{if !$has_secret}} - +

{{else}} - +

{{/if}} {{if $has_secret && $verified}} - +

+

{{/if}} {{if $has_secret && !$verified}} - +

{{/if}} -
From 8cdc0172e7a678757f38acb326eafc6344146faf Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 22 Jul 2019 07:56:16 -0400 Subject: [PATCH 4/6] Add documentation about app-specific passwords --- doc/Two-Factor-Authentication.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/Two-Factor-Authentication.md b/doc/Two-Factor-Authentication.md index 32aa7308a..eca6f598c 100644 --- a/doc/Two-Factor-Authentication.md +++ b/doc/Two-Factor-Authentication.md @@ -58,3 +58,17 @@ In this case you will have to configure your authenticator app again using the p 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. + +## Third-party applications and API + +Third-party applications using the Friendica API can't accept two-factor time-based authentication codes. +Instead, if you enabled two-factor authentication, you have to generate app-specific randomly generated long passwords to use in your apps instead of your regular account password. + +**Note**: Your regular password won't work at all when prompted in third-party apps if you enabled two-factor authentication. + +You can generate as many app-specific passwords as you want, they will be shown once to you just after you generated it. +Just copy and paste it in your third-party app in the Friendica account password input field at this point. +We recommend generating a single app-specific password for each separate third-party app you are using, using a meaningul description of the target app (like "Frienqa on my Fairphone 2"). + +You can also revoke any and all app-specific password you generated this way. +This may log you out of the third-party application(s) you used the revoked app-specific password to log in with. \ No newline at end of file From cd257dc7e8350f9827315906a936a910c1788a6b Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 22 Jul 2019 07:56:36 -0400 Subject: [PATCH 5/6] Enable app-specific password authentication for API login --- include/api.php | 2 +- src/Model/User.php | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/api.php b/include/api.php index 51ca1e4e1..b1bb49c51 100644 --- a/include/api.php +++ b/include/api.php @@ -236,7 +236,7 @@ function api_login(App $a) if ($addon_auth['authenticated'] && count($addon_auth['user_record'])) { $record = $addon_auth['user_record']; } else { - $user_id = User::authenticate(trim($user), trim($password)); + $user_id = User::authenticate(trim($user), trim($password), true); if ($user_id !== false) { $record = DBA::selectFirst('user', [], ['uid' => $user_id]); } diff --git a/src/Model/User.php b/src/Model/User.php index de6931052..141ecf059 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -17,6 +17,7 @@ use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Model\Photo; +use Friendica\Model\TwoFactor\AppSpecificPassword; use Friendica\Object\Image; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; @@ -267,17 +268,18 @@ class User /** * Authenticate a user with a clear text password * - * @brief Authenticate a user with a clear text password - * @param mixed $user_info + * @brief Authenticate a user with a clear text password + * @param mixed $user_info * @param string $password + * @param bool $third_party * @return int|boolean * @deprecated since version 3.6 - * @see User::getIdFromPasswordAuthentication() + * @see User::getIdFromPasswordAuthentication() */ - public static function authenticate($user_info, $password) + public static function authenticate($user_info, $password, $third_party = false) { try { - return self::getIdFromPasswordAuthentication($user_info, $password); + return self::getIdFromPasswordAuthentication($user_info, $password, $third_party); } catch (Exception $ex) { return false; } @@ -287,16 +289,22 @@ class User * Returns the user id associated with a successful password authentication * * @brief Authenticate a user with a clear text password - * @param mixed $user_info + * @param mixed $user_info * @param string $password + * @param bool $third_party * @return int User Id if authentication is successful * @throws Exception */ - public static function getIdFromPasswordAuthentication($user_info, $password) + public static function getIdFromPasswordAuthentication($user_info, $password, $third_party = false) { $user = self::getAuthenticationInfo($user_info); - if (strpos($user['password'], '$') === false) { + if ($third_party && PConfig::get($user['uid'], '2fa', 'verified')) { + // Third-party apps can't verify two-factor authentication, we use app-specific passwords instead + if (AppSpecificPassword::authenticateUser($user['uid'], $password)) { + return $user['uid']; + } + } elseif (strpos($user['password'], '$') === false) { //Legacy hash that has not been replaced by a new hash yet if (self::hashPasswordLegacy($password) === $user['password']) { self::updatePasswordHashed($user['uid'], self::hashPassword($password)); From 8677428edb3fc34427cdca941dc9883dc8a625ea Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 22 Jul 2019 17:41:21 -0400 Subject: [PATCH 6/6] Remove debug --- src/Module/Settings/TwoFactor/AppSpecific.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Module/Settings/TwoFactor/AppSpecific.php b/src/Module/Settings/TwoFactor/AppSpecific.php index 17ff89071..c62b0bbff 100644 --- a/src/Module/Settings/TwoFactor/AppSpecific.php +++ b/src/Module/Settings/TwoFactor/AppSpecific.php @@ -91,8 +91,6 @@ class AppSpecific extends BaseSettingsModule $appSpecificPasswords = AppSpecificPassword::getListForUser(local_user()); - var_dump($appSpecificPasswords); - return Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/twofactor/app_specific.tpl'), [ '$form_security_token' => self::getFormSecurityToken('settings_2fa_app_specific'), '$password_security_token' => self::getFormSecurityToken('settings_2fa_password'),