Skip to content

Commit

Permalink
Merge pull request #36744 from nextcloud/backport/35419/stable23
Browse files Browse the repository at this point in the history
[stable23] Fix login loop if login CSRF fails and user is not logged in
  • Loading branch information
blizzz authored Mar 14, 2023
2 parents 4881230 + b9baa62 commit d78baf4
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 14 deletions.
20 changes: 16 additions & 4 deletions core/Controller/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,23 @@ public function tryLogin(string $user,
string $redirect_url = null,
string $timezone = '',
string $timezone_offset = ''): RedirectResponse {
// If the user is already logged in and the CSRF check does not pass then
// simply redirect the user to the correct page as required. This is the
// case when an user has already logged-in, in another tab.
if (!$this->request->passesCSRFCheck()) {
return $this->generateRedirect($redirect_url);
if ($this->userSession->isLoggedIn()) {
// If the user is already logged in and the CSRF check does not pass then
// simply redirect the user to the correct page as required. This is the
// case when a user has already logged-in, in another tab.
return $this->generateRedirect($redirect_url);
}

// Clear any auth remnants like cookies to ensure a clean login
// For the next attempt
$this->userSession->logout();
return $this->createLoginFailedResponse(
$user,
$user,
$redirect_url,
$this->l10n->t('Please try again')
);
}

$data = new LoginData(
Expand Down
21 changes: 11 additions & 10 deletions tests/Core/Controller/LoginControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ public function testLoginWithValidCredentials() {
$this->assertEquals($expected, $this->loginController->tryLogin($user, $password));
}

public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() {
public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn(): void {
/** @var IUser|MockObject $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
Expand All @@ -513,21 +513,20 @@ public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn() {
->expects($this->once())
->method('passesCSRFCheck')
->willReturn(false);
$this->userSession->expects($this->once())
$this->userSession
->method('isLoggedIn')
->with()
->willReturn(false);
$this->config->expects($this->never())
->method('deleteUserValue');
$this->userSession->expects($this->never())
->method('createRememberMeToken');
$this->urlGenerator
->expects($this->once())
->method('linkToDefaultPageUrl')
->willReturn('/default/foo');

$expected = new RedirectResponse('/default/foo');
$this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
$response = $this->loginController->tryLogin('Jane', $password, $originalUrl);

$expected = new RedirectResponse('');
$expected->throttle(['user' => 'Jane']);
$this->assertEquals($expected, $response);
}

public function testLoginWithoutPassedCsrfCheckAndLoggedIn() {
Expand All @@ -544,7 +543,7 @@ public function testLoginWithoutPassedCsrfCheckAndLoggedIn() {
->expects($this->once())
->method('passesCSRFCheck')
->willReturn(false);
$this->userSession->expects($this->once())
$this->userSession
->method('isLoggedIn')
->with()
->willReturn(true);
Expand All @@ -561,8 +560,10 @@ public function testLoginWithoutPassedCsrfCheckAndLoggedIn() {
->with('remember_login_cookie_lifetime')
->willReturn(1234);

$response = $this->loginController->tryLogin('Jane', $password, $originalUrl);

$expected = new RedirectResponse($redirectUrl);
$this->assertEquals($expected, $this->loginController->tryLogin('Jane', $password, $originalUrl));
$this->assertEquals($expected, $response);
}

public function testLoginWithValidCredentialsAndRedirectUrl() {
Expand Down

0 comments on commit d78baf4

Please sign in to comment.