Skip to content

Commit 4ebda01

Browse files
committed
fix: when the token has expired, only redirect to the login flow when the request comes from a 'navigation' context
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
1 parent 2b1f682 commit 4ebda01

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Swp\Service;
11+
12+
use OCP\IRequest;
13+
14+
class RequestClassificationService {
15+
public static function isTopLevelHtmlNavigation(IRequest $request): bool {
16+
if (strtoupper($request->getMethod()) !== 'GET') {
17+
return false;
18+
}
19+
20+
$accept = strtolower((string)$request->getHeader('Accept'));
21+
if ($accept !== '' && strpos($accept, 'text/html') === false) {
22+
return false;
23+
}
24+
25+
if ($request->getHeader('X-Requested-With') === 'XMLHttpRequest') {
26+
return false;
27+
}
28+
29+
$secFetchMode = strtolower((string)$request->getHeader('Sec-Fetch-Mode'));
30+
if ($secFetchMode !== '' && $secFetchMode !== 'navigate') {
31+
return false;
32+
}
33+
34+
$secFetchDest = strtolower((string)$request->getHeader('Sec-Fetch-Dest'));
35+
if ($secFetchDest !== '' && $secFetchDest !== 'document') {
36+
return false;
37+
}
38+
39+
return true;
40+
}
41+
}

lib/Service/TokenService.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,34 @@ public function reauthenticate() {
212212
return;
213213
}
214214

215+
$accept = (string)$this->request->getHeader('Accept');
216+
$xRequestedWith = (string)$this->request->getHeader('X-Requested-With');
217+
$secFetchMode = (string)$this->request->getHeader('Sec-Fetch-Mode');
218+
$secFetchDest = (string)$this->request->getHeader('Sec-Fetch-Dest');
219+
if (!RequestClassificationService::isTopLevelHtmlNavigation($this->request)) {
220+
$this->userSession->logout();
221+
$this->logger->debug('[TokenService] reauthenticate skipped: request is not a top-level HTML navigation', [
222+
'request_uri' => $this->request->getRequestUri(),
223+
'accept' => $accept,
224+
'x_requested_with' => $xRequestedWith,
225+
'sec_fetch_mode' => $secFetchMode,
226+
'sec_fetch_dest' => $secFetchDest,
227+
]);
228+
return;
229+
}
230+
215231
// Logout the user and redirect to the oidc login flow to gather a fresh token
216232
$this->userSession->logout();
217233
$redirectUrl = $this->urlGenerator->getAbsoluteURL('/index.php/apps/user_oidc/login/' . strval($token->getProviderId()))
218234
. '?redirectUrl=' . urlencode($this->request->getRequestUri());
235+
$this->logger->debug('[TokenService] reauthenticate redirect', [
236+
'redirect_url' => $redirectUrl,
237+
'request_uri' => $this->request->getRequestUri(),
238+
'accept' => $accept,
239+
'x_requested_with' => $xRequestedWith,
240+
'sec_fetch_mode' => $secFetchMode,
241+
'sec_fetch_dest' => $secFetchDest,
242+
]);
219243
header('Location: ' . $redirectUrl);
220244
exit();
221245
}

0 commit comments

Comments
 (0)