fix(session): scope the session cookie to the directory without a trailing slash

UrlHelper::dir() always returns a trailing slash, so the session cookie was
scoped to "/kanboard/". That cookie is not sent back for "/kanboard", which
is exactly the url the bundled web.config and .htaccess serve the application
on: both rewrite to index.php whenever the request is not a file, so the
directory url is never redirected to its trailing-slash form.

The first login then stores redirectAfterLogin="/kanboard", redirects there
after a successful check, arrives without the session cookie and bounces back
to the login form with no error message.

Use the directory without the trailing slash, which is what the browser would
have picked as the default cookie path anyway and still matches every url
below it.
This commit is contained in:
dualfroz committed 2026-10-02 09:50:13 +08:00
1 parent 76ee9f8fd0
commit 19540701b6
2 files changed
+51 -1

No files matched your search

+13 -1
View File
@@ -79,6 +79,18 @@ class SessionManager extends Base
session_destroy();
}
/**
* Get the session cookie path, without the trailing slash
*
* @access public
* @return string
*/
public function getCookiePath()
{
$path = rtrim((string) $this->helper->url->dir(), '/');
return $path === '' ? '/' : $path;
}
/**
* Define session settings
*
@@ -89,7 +101,7 @@ class SessionManager extends Base
// Session cookie: HttpOnly and secure flags
session_set_cookie_params(
SESSION_DURATION,
$this->helper->url->dir() ?: '/',
$this->getCookiePath(),
null,
$this->request->isHTTPS(),
true
@@ -0,0 +1,38 @@
<?php
namespace KanboardTests\units\Core\Session;
use KanboardTests\units\Base;
use Kanboard\Core\Http\Request;
use Kanboard\Core\Session\SessionManager;
class SessionManagerTest extends Base
{
public function testCookiePathAtRoot()
{
$this->assertEquals('/', $this->buildSessionManager('/index.php')->getCookiePath());
}
public function testCookiePathInSubDirectoryHasNoTrailingSlash()
{
$this->assertEquals('/kanboard', $this->buildSessionManager('/kanboard/index.php')->getCookiePath());
}
public function testCookiePathInNestedSubDirectoryHasNoTrailingSlash()
{
$this->assertEquals('/apps/kanboard', $this->buildSessionManager('/apps/kanboard/index.php')->getCookiePath());
}
private function buildSessionManager($phpSelf)
{
$this->container['request'] = new Request(
$this->container,
array(
'PHP_SELF' => $phpSelf,
'REQUEST_METHOD' => 'GET',
)
);
return new SessionManager($this->container);
}
}