From e3cb237b047b410202ebbe027548314486958dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 27 Jan 2026 17:05:41 +0100 Subject: [PATCH 1/3] feat: Add SetupCheck to warn about missing second factor provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../composer/composer/autoload_classmap.php | 1 + .../composer/composer/autoload_static.php | 1 + apps/settings/lib/AppInfo/Application.php | 2 + .../SetupChecks/TwoFactorConfiguration.php | 49 +++++++++++++++++++ .../TwoFactorAuth/ProviderLoader.php | 8 ++- 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 apps/settings/lib/SetupChecks/TwoFactorConfiguration.php diff --git a/apps/settings/composer/composer/autoload_classmap.php b/apps/settings/composer/composer/autoload_classmap.php index ba9b5354e78dc..f856bd7aa8c42 100644 --- a/apps/settings/composer/composer/autoload_classmap.php +++ b/apps/settings/composer/composer/autoload_classmap.php @@ -138,6 +138,7 @@ 'OCA\\Settings\\SetupChecks\\TaskProcessingSuccessRate' => $baseDir . '/../lib/SetupChecks/TaskProcessingSuccessRate.php', 'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => $baseDir . '/../lib/SetupChecks/TempSpaceAvailable.php', 'OCA\\Settings\\SetupChecks\\TransactionIsolation' => $baseDir . '/../lib/SetupChecks/TransactionIsolation.php', + 'OCA\\Settings\\SetupChecks\\TwoFactorConfiguration' => $baseDir . '/../lib/SetupChecks/TwoFactorConfiguration.php', 'OCA\\Settings\\SetupChecks\\WellKnownUrls' => $baseDir . '/../lib/SetupChecks/WellKnownUrls.php', 'OCA\\Settings\\SetupChecks\\Woff2Loading' => $baseDir . '/../lib/SetupChecks/Woff2Loading.php', 'OCA\\Settings\\UserMigration\\AccountMigrator' => $baseDir . '/../lib/UserMigration/AccountMigrator.php', diff --git a/apps/settings/composer/composer/autoload_static.php b/apps/settings/composer/composer/autoload_static.php index 0e13fd42f216d..d1c8d9b9eae77 100644 --- a/apps/settings/composer/composer/autoload_static.php +++ b/apps/settings/composer/composer/autoload_static.php @@ -153,6 +153,7 @@ class ComposerStaticInitSettings 'OCA\\Settings\\SetupChecks\\TaskProcessingSuccessRate' => __DIR__ . '/..' . '/../lib/SetupChecks/TaskProcessingSuccessRate.php', 'OCA\\Settings\\SetupChecks\\TempSpaceAvailable' => __DIR__ . '/..' . '/../lib/SetupChecks/TempSpaceAvailable.php', 'OCA\\Settings\\SetupChecks\\TransactionIsolation' => __DIR__ . '/..' . '/../lib/SetupChecks/TransactionIsolation.php', + 'OCA\\Settings\\SetupChecks\\TwoFactorConfiguration' => __DIR__ . '/..' . '/../lib/SetupChecks/TwoFactorConfiguration.php', 'OCA\\Settings\\SetupChecks\\WellKnownUrls' => __DIR__ . '/..' . '/../lib/SetupChecks/WellKnownUrls.php', 'OCA\\Settings\\SetupChecks\\Woff2Loading' => __DIR__ . '/..' . '/../lib/SetupChecks/Woff2Loading.php', 'OCA\\Settings\\UserMigration\\AccountMigrator' => __DIR__ . '/..' . '/../lib/UserMigration/AccountMigrator.php', diff --git a/apps/settings/lib/AppInfo/Application.php b/apps/settings/lib/AppInfo/Application.php index de007a6978fe5..7f837bfd0ae1a 100644 --- a/apps/settings/lib/AppInfo/Application.php +++ b/apps/settings/lib/AppInfo/Application.php @@ -74,6 +74,7 @@ use OCA\Settings\SetupChecks\TaskProcessingPickupSpeed; use OCA\Settings\SetupChecks\TempSpaceAvailable; use OCA\Settings\SetupChecks\TransactionIsolation; +use OCA\Settings\SetupChecks\TwoFactorConfiguration; use OCA\Settings\SetupChecks\WellKnownUrls; use OCA\Settings\SetupChecks\Woff2Loading; use OCA\Settings\UserMigration\AccountMigrator; @@ -213,6 +214,7 @@ public function register(IRegistrationContext $context): void { $context->registerSetupCheck(TaskProcessingPickupSpeed::class); $context->registerSetupCheck(TempSpaceAvailable::class); $context->registerSetupCheck(TransactionIsolation::class); + $context->registerSetupCheck(TwoFactorConfiguration::class); $context->registerSetupCheck(PushService::class); $context->registerSetupCheck(WellKnownUrls::class); $context->registerSetupCheck(Woff2Loading::class); diff --git a/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php b/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php new file mode 100644 index 0000000000000..f95c5358fdc7d --- /dev/null +++ b/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php @@ -0,0 +1,49 @@ +l10n->t('Two factor configuration'); + } + + public function getCategory(): string { + return 'security'; + } + + public function run(): SetupResult { + $providers = $this->providerLoader->getProviders(); + if (count($providers) === 0) { + return SetupResult::warning($this->l10n->t('This instance has no second factor provider available.')); + } else { + return SetupResult::success( + $this->l10n->t( + 'Second factor providers are available: %s.', + [ + implode(', ', array_map( + fn ($p) => '"' . $p->getDisplayName() . '"', + $providers) + ) + ] + ) + ); + } + } +} diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php b/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php index 7e674a01dd860..d3ba27088b23d 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php @@ -30,8 +30,12 @@ public function __construct( * @return IProvider[] * @throws Exception */ - public function getProviders(IUser $user): array { - $allApps = $this->appManager->getEnabledAppsForUser($user); + public function getProviders(?IUser $user = null): array { + if ($user === null) { + $allApps = $this->appManager->getEnabledApps(); + } else { + $allApps = $this->appManager->getEnabledAppsForUser($user); + } $providers = []; foreach ($allApps as $appId) { From 5b60fb23db02812fb4101ef3cf5da2da872037b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 2 Feb 2026 11:38:28 +0100 Subject: [PATCH 2/3] fix(settings): Only consider primary providers for 2FA setup check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In practice this filters out backup codes. Also fixed the english formulation and the copyright year. Signed-off-by: Côme Chilliet --- .../lib/SetupChecks/TwoFactorConfiguration.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php b/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php index f95c5358fdc7d..52e1966a7be93 100644 --- a/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php +++ b/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php @@ -3,12 +3,13 @@ declare(strict_types=1); /** - * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Settings\SetupChecks; use OC\Authentication\TwoFactorAuth\ProviderLoader; +use OC\Authentication\TwoFactorAuth\ProviderSet; use OCP\IL10N; use OCP\SetupCheck\ISetupCheck; use OCP\SetupCheck\SetupResult; @@ -21,7 +22,7 @@ public function __construct( } public function getName(): string { - return $this->l10n->t('Two factor configuration'); + return $this->l10n->t('Second factor configuration'); } public function getCategory(): string { @@ -30,7 +31,9 @@ public function getCategory(): string { public function run(): SetupResult { $providers = $this->providerLoader->getProviders(); - if (count($providers) === 0) { + $providerSet = new ProviderSet($providers, false); + $primaryProviders = $providerSet->getPrimaryProviders(); + if (count($primaryProviders) === 0) { return SetupResult::warning($this->l10n->t('This instance has no second factor provider available.')); } else { return SetupResult::success( @@ -39,7 +42,7 @@ public function run(): SetupResult { [ implode(', ', array_map( fn ($p) => '"' . $p->getDisplayName() . '"', - $providers) + $primaryProviders) ) ] ) From f062358fe4ddbe512c58f0929c7bd242645c92d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 2 Feb 2026 14:29:53 +0100 Subject: [PATCH 3/3] feat: Add info level result if 2FA is not enforced MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- .../lib/SetupChecks/TwoFactorConfiguration.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php b/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php index 52e1966a7be93..584191401cafa 100644 --- a/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php +++ b/apps/settings/lib/SetupChecks/TwoFactorConfiguration.php @@ -6,8 +6,10 @@ * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ + namespace OCA\Settings\SetupChecks; +use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor; use OC\Authentication\TwoFactorAuth\ProviderLoader; use OC\Authentication\TwoFactorAuth\ProviderSet; use OCP\IL10N; @@ -18,6 +20,7 @@ class TwoFactorConfiguration implements ISetupCheck { public function __construct( private IL10N $l10n, private ProviderLoader $providerLoader, + private MandatoryTwoFactor $mandatoryTwoFactor, ) { } @@ -35,10 +38,20 @@ public function run(): SetupResult { $primaryProviders = $providerSet->getPrimaryProviders(); if (count($primaryProviders) === 0) { return SetupResult::warning($this->l10n->t('This instance has no second factor provider available.')); + } + + $state = $this->mandatoryTwoFactor->getState(); + + if (!$state->isEnforced()) { + return SetupResult::info( + $this->l10n->t( + 'Second factor providers are available but two-factor authentication is not enforced.' + ) + ); } else { return SetupResult::success( $this->l10n->t( - 'Second factor providers are available: %s.', + 'Second factor providers are available and enforced: %s.', [ implode(', ', array_map( fn ($p) => '"' . $p->getDisplayName() . '"',