diff --git a/core/Controller/ClientFlowLoginController.php b/core/Controller/ClientFlowLoginController.php index 99074e6ff597f..deaf26b348d04 100644 --- a/core/Controller/ClientFlowLoginController.php +++ b/core/Controller/ClientFlowLoginController.php @@ -36,6 +36,8 @@ use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; use OCP\Session\Exceptions\SessionNotAvailableException; +use function hash_equals; +use function OCP\Log\logger; #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] class ClientFlowLoginController extends Controller { @@ -69,9 +71,20 @@ private function getClientName(): string { private function isValidToken(string $stateToken): bool { $currentToken = $this->session->get(self::STATE_NAME); if (!is_string($currentToken)) { + logger('core')->error('Client login flow state token is not set', [ + 'sessionToken' => $currentToken, + 'requestToken' => $stateToken, + ]); return false; } - return hash_equals($currentToken, $stateToken); + $hashEquals = hash_equals($currentToken, $stateToken); + if (!$hashEquals) { + logger('core')->error('Client login flow state token does not match', [ + 'sessionToken' => $currentToken, + 'requestToken' => $stateToken, + ]); + } + return $hashEquals; } private function stateTokenForbiddenResponse(): StandaloneTemplateResponse { @@ -123,6 +136,9 @@ public function showAuthPickerPage(string $clientIdentifier = '', string $user = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS ); $this->session->set(self::STATE_NAME, $stateToken); + logger('core')->error('Client login flow state token set', [ + 'token' => $stateToken, + ]); $csp = new Http\ContentSecurityPolicy(); if ($client) { diff --git a/core/Controller/ClientFlowLoginV2Controller.php b/core/Controller/ClientFlowLoginV2Controller.php index 1ce43c1993222..8e07a2cb1d055 100644 --- a/core/Controller/ClientFlowLoginV2Controller.php +++ b/core/Controller/ClientFlowLoginV2Controller.php @@ -33,6 +33,8 @@ use OCP\IUser; use OCP\IUserSession; use OCP\Security\ISecureRandom; +use function hash_equals; +use function OCP\Log\logger; /** * @psalm-import-type CoreLoginFlowV2Credentials from ResponseDefinitions @@ -93,6 +95,11 @@ public function landing(string $token, $user = '', int $direct = 0): Response { } $this->session->set(self::TOKEN_NAME, $token); + logger('core')->debug('Client login flow state token set on landing page', [ + 'sessionId' => $this->session->getId(), + 'sessionToken' => $token, + 'user' => $user, + ]); return new RedirectResponse( $this->urlGenerator->linkToRouteAbsolute('core.ClientFlowLoginV2.showAuthPickerPage', ['user' => $user, 'direct' => $direct]) @@ -116,6 +123,11 @@ public function showAuthPickerPage(string $user = '', int $direct = 0): Standalo ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS ); $this->session->set(self::STATE_NAME, $stateToken); + logger('core')->debug('Client login flow state token set on auth picker page', [ + 'sessionId' => $this->session->getId(), + 'sessionToken' => $stateToken, + 'user' => $user, + ]); return new StandaloneTemplateResponse( $this->appName, @@ -299,9 +311,22 @@ public function init(): JSONResponse { private function isValidStateToken(string $stateToken): bool { $currentToken = $this->session->get(self::STATE_NAME); if (!is_string($stateToken) || !is_string($currentToken)) { + logger('core')->error('Client login flow state token is not set', [ + 'sessionId' => $this->session->getId(), + 'sessionToken' => $currentToken, + 'requestToken' => $stateToken, + ]); return false; } - return hash_equals($currentToken, $stateToken); + $hashEquals = hash_equals($currentToken, $stateToken); + if (!$hashEquals) { + logger('core')->error('Client login flow state token does not match', [ + 'sessionId' => $this->session->getId(), + 'sessionToken' => $currentToken, + 'requestToken' => $stateToken, + ]); + } + return $hashEquals; } private function stateTokenMissingResponse(): StandaloneTemplateResponse {