From 51847dcf9eeb9eeac29985d14dfd8815b10799c3 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 6 Aug 2026 18:04:50 +0200 Subject: [PATCH] fix: Handle 2fa enforcement earlier Signed-off-by: Joas Schilling --- lib/private/User/Session.php | 9 ++++----- tests/lib/User/SessionTest.php | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index cf63ae5b8bd62..672c8935efc6c 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -403,10 +403,8 @@ public function logClientIn($user, return false; } - if (!$isTokenPassword && $this->isTokenAuthEnforced()) { - throw new PasswordLoginForbiddenException(); - } - if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) { + if (!$isTokenPassword && ($this->isTokenAuthEnforced() || $this->isTwoFactorEnforced($user))) { + $this->handleLoginFailed($throttler, $currentDelay, $remoteAddress, $user, $password); throw new PasswordLoginForbiddenException(); } @@ -576,7 +574,8 @@ public function tryBasicAuthLogin(IRequest $request, // If credentials were provided, they need to be valid, otherwise we do boom throw new LoginException(); } catch (PasswordLoginForbiddenException $ex) { - // Nothing to do + // If credentials were provided, they need to be valid, otherwise we do boom + throw new LoginException(previous: $ex); } } return false; diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 88f0344d12c10..c498582ec586e 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -439,7 +439,7 @@ public function testLogClientInNoTokenPasswordWith2fa(): void { ->method('getRemoteAddress') ->willReturn('192.168.0.1'); $this->throttler - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('sleepDelayOrThrowOnMax') ->with('192.168.0.1'); $this->throttler @@ -448,6 +448,15 @@ public function testLogClientInNoTokenPasswordWith2fa(): void { ->with('192.168.0.1') ->willReturn(0); + $this->throttler + ->expects($this->once()) + ->method('registerAttempt') + ->with('login', '192.168.0.1', ['user' => 'john']); + $this->dispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with(new LoginFailed('john', 'doe')); + $userSession->logClientIn('john', 'doe', $request, $this->throttler); } @@ -550,7 +559,7 @@ public function testLogClientInNoTokenPasswordNo2fa(): void { ->method('getRemoteAddress') ->willReturn('192.168.0.1'); $this->throttler - ->expects($this->once()) + ->expects($this->exactly(2)) ->method('sleepDelayOrThrowOnMax') ->with('192.168.0.1'); $this->throttler @@ -559,6 +568,15 @@ public function testLogClientInNoTokenPasswordNo2fa(): void { ->with('192.168.0.1') ->willReturn(0); + $this->throttler + ->expects($this->once()) + ->method('registerAttempt') + ->with('login', '192.168.0.1', ['user' => 'john']); + $this->dispatcher + ->expects($this->once()) + ->method('dispatchTyped') + ->with(new LoginFailed('john', 'doe')); + $userSession->logClientIn('john', 'doe', $request, $this->throttler); }