Merge pull request #51051 from nextcloud/artonge/fix/login_flow_v2_sessions_2

feat: Close sessions created for login flow v2
This commit is contained in:
Louis 2025-02-27 08:52:00 +01:00 committed by GitHub
commit c7900de4f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 101 additions and 61 deletions

View file

@ -41,6 +41,8 @@ use OCP\Security\ISecureRandom;
class ClientFlowLoginV2Controller extends Controller {
public const TOKEN_NAME = 'client.flow.v2.login.token';
public const STATE_NAME = 'client.flow.v2.state.token';
// Denotes that the session was created for the login flow and should therefore be ephemeral.
public const EPHEMERAL_NAME = 'client.flow.v2.state.ephemeral';
public function __construct(
string $appName,

View file

@ -982,6 +982,7 @@ return array(
'OC\\AppFramework\\Http\\RequestId' => $baseDir . '/lib/private/AppFramework/Http/RequestId.php',
'OC\\AppFramework\\Middleware\\AdditionalScriptsMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php',
'OC\\AppFramework\\Middleware\\CompressionMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/CompressionMiddleware.php',
'OC\\AppFramework\\Middleware\\FlowV2EphemeralSessionsMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php',
'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => $baseDir . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php',
'OC\\AppFramework\\Middleware\\NotModifiedMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php',
'OC\\AppFramework\\Middleware\\OCSMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/OCSMiddleware.php',
@ -1079,6 +1080,7 @@ return array(
'OC\\Authentication\\Login\\CreateSessionTokenCommand' => $baseDir . '/lib/private/Authentication/Login/CreateSessionTokenCommand.php',
'OC\\Authentication\\Login\\EmailLoginCommand' => $baseDir . '/lib/private/Authentication/Login/EmailLoginCommand.php',
'OC\\Authentication\\Login\\FinishRememberedLoginCommand' => $baseDir . '/lib/private/Authentication/Login/FinishRememberedLoginCommand.php',
'OC\\Authentication\\Login\\FlowV2EphemeralSessionsCommand' => $baseDir . '/lib/private/Authentication/Login/FlowV2EphemeralSessionsCommand.php',
'OC\\Authentication\\Login\\LoggedInCheckCommand' => $baseDir . '/lib/private/Authentication/Login/LoggedInCheckCommand.php',
'OC\\Authentication\\Login\\LoginData' => $baseDir . '/lib/private/Authentication/Login/LoginData.php',
'OC\\Authentication\\Login\\LoginResult' => $baseDir . '/lib/private/Authentication/Login/LoginResult.php',

View file

@ -1031,6 +1031,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\AppFramework\\Http\\RequestId' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/RequestId.php',
'OC\\AppFramework\\Middleware\\AdditionalScriptsMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php',
'OC\\AppFramework\\Middleware\\CompressionMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/CompressionMiddleware.php',
'OC\\AppFramework\\Middleware\\FlowV2EphemeralSessionsMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php',
'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php',
'OC\\AppFramework\\Middleware\\NotModifiedMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php',
'OC\\AppFramework\\Middleware\\OCSMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/OCSMiddleware.php',
@ -1128,6 +1129,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Authentication\\Login\\CreateSessionTokenCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/CreateSessionTokenCommand.php',
'OC\\Authentication\\Login\\EmailLoginCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/EmailLoginCommand.php',
'OC\\Authentication\\Login\\FinishRememberedLoginCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/FinishRememberedLoginCommand.php',
'OC\\Authentication\\Login\\FlowV2EphemeralSessionsCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/FlowV2EphemeralSessionsCommand.php',
'OC\\Authentication\\Login\\LoggedInCheckCommand' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/LoggedInCheckCommand.php',
'OC\\Authentication\\Login\\LoginData' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/LoginData.php',
'OC\\Authentication\\Login\\LoginResult' => __DIR__ . '/../../..' . '/lib/private/Authentication/Login/LoginResult.php',

View file

@ -10,6 +10,7 @@ use OC;
use OC\AppFramework\Http;
use OC\AppFramework\Http\Dispatcher;
use OC\AppFramework\Http\Output;
use OC\AppFramework\Middleware\FlowV2EphemeralSessionsMiddleware;
use OC\AppFramework\Middleware\MiddlewareDispatcher;
use OC\AppFramework\Middleware\OCSMiddleware;
use OC\AppFramework\Middleware\Security\CORSMiddleware;
@ -216,7 +217,12 @@ class DIContainer extends SimpleContainer implements IAppContainer {
)
);
$dispatcher->registerMiddleware(
new FlowV2EphemeralSessionsMiddleware(
$c->get(ISession::class),
$c->get(IUserSession::class),
)
);
$securityMiddleware = new SecurityMiddleware(
$c->get(IRequest::class),

View file

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\AppFramework\Middleware;
use OC\Core\Controller\ClientFlowLoginV2Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Middleware;
use OCP\ISession;
use OCP\IUserSession;
use ReflectionMethod;
// Will close the session if the user session is ephemeral.
// Happens when the user logs in via the login flow v2.
class FlowV2EphemeralSessionsMiddleware extends Middleware {
public function __construct(
private ISession $session,
private IUserSession $userSession,
) {
}
public function beforeController(Controller $controller, string $methodName) {
if (!$this->session->get(ClientFlowLoginV2Controller::EPHEMERAL_NAME)) {
return;
}
if (
$controller instanceof ClientFlowLoginV2Controller &&
($methodName === 'grantPage' || $methodName === 'generateAppPassword')
) {
return;
}
$reflectionMethod = new ReflectionMethod($controller, $methodName);
if (!empty($reflectionMethod->getAttributes('PublicPage'))) {
return;
}
$this->userSession->logout();
$this->session->close();
}
}

View file

@ -9,67 +9,21 @@ declare(strict_types=1);
namespace OC\Authentication\Login;
class Chain {
/** @var PreLoginHookCommand */
private $preLoginHookCommand;
/** @var UserDisabledCheckCommand */
private $userDisabledCheckCommand;
/** @var UidLoginCommand */
private $uidLoginCommand;
/** @var EmailLoginCommand */
private $emailLoginCommand;
/** @var LoggedInCheckCommand */
private $loggedInCheckCommand;
/** @var CompleteLoginCommand */
private $completeLoginCommand;
/** @var CreateSessionTokenCommand */
private $createSessionTokenCommand;
/** @var ClearLostPasswordTokensCommand */
private $clearLostPasswordTokensCommand;
/** @var UpdateLastPasswordConfirmCommand */
private $updateLastPasswordConfirmCommand;
/** @var SetUserTimezoneCommand */
private $setUserTimezoneCommand;
/** @var TwoFactorCommand */
private $twoFactorCommand;
/** @var FinishRememberedLoginCommand */
private $finishRememberedLoginCommand;
public function __construct(PreLoginHookCommand $preLoginHookCommand,
UserDisabledCheckCommand $userDisabledCheckCommand,
UidLoginCommand $uidLoginCommand,
EmailLoginCommand $emailLoginCommand,
LoggedInCheckCommand $loggedInCheckCommand,
CompleteLoginCommand $completeLoginCommand,
CreateSessionTokenCommand $createSessionTokenCommand,
ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand,
UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand,
SetUserTimezoneCommand $setUserTimezoneCommand,
TwoFactorCommand $twoFactorCommand,
FinishRememberedLoginCommand $finishRememberedLoginCommand,
public function __construct(
private PreLoginHookCommand $preLoginHookCommand,
private UserDisabledCheckCommand $userDisabledCheckCommand,
private UidLoginCommand $uidLoginCommand,
private EmailLoginCommand $emailLoginCommand,
private LoggedInCheckCommand $loggedInCheckCommand,
private CompleteLoginCommand $completeLoginCommand,
private CreateSessionTokenCommand $createSessionTokenCommand,
private ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand,
private UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand,
private SetUserTimezoneCommand $setUserTimezoneCommand,
private TwoFactorCommand $twoFactorCommand,
private FinishRememberedLoginCommand $finishRememberedLoginCommand,
private FlowV2EphemeralSessionsCommand $flowV2EphemeralSessionsCommand,
) {
$this->preLoginHookCommand = $preLoginHookCommand;
$this->userDisabledCheckCommand = $userDisabledCheckCommand;
$this->uidLoginCommand = $uidLoginCommand;
$this->emailLoginCommand = $emailLoginCommand;
$this->loggedInCheckCommand = $loggedInCheckCommand;
$this->completeLoginCommand = $completeLoginCommand;
$this->createSessionTokenCommand = $createSessionTokenCommand;
$this->clearLostPasswordTokensCommand = $clearLostPasswordTokensCommand;
$this->updateLastPasswordConfirmCommand = $updateLastPasswordConfirmCommand;
$this->setUserTimezoneCommand = $setUserTimezoneCommand;
$this->twoFactorCommand = $twoFactorCommand;
$this->finishRememberedLoginCommand = $finishRememberedLoginCommand;
}
public function process(LoginData $loginData): LoginResult {
@ -80,6 +34,7 @@ class Chain {
->setNext($this->emailLoginCommand)
->setNext($this->loggedInCheckCommand)
->setNext($this->completeLoginCommand)
->setNext($this->flowV2EphemeralSessionsCommand)
->setNext($this->createSessionTokenCommand)
->setNext($this->clearLostPasswordTokensCommand)
->setNext($this->updateLastPasswordConfirmCommand)

View file

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Authentication\Login;
use OC\Core\Controller\ClientFlowLoginV2Controller;
use OCP\ISession;
class FlowV2EphemeralSessionsCommand extends ALoginCommand {
public function __construct(
private ISession $session,
) {
}
public function process(LoginData $loginData): LoginResult {
if (str_starts_with($loginData->getRedirectUrl() ?? '', '/login/v2/grant')) {
$this->session->set(ClientFlowLoginV2Controller::EPHEMERAL_NAME, true);
}
return $this->processNextOrFinishSuccessfully($loginData);
}
}