diff --git a/composer.json b/composer.json
index c22b206bd19c757af84aa9b5a3bb738147261698..d49a26ea1b37c875e7673692c7a00b908c60bf29 100644
--- a/composer.json
+++ b/composer.json
@@ -38,7 +38,7 @@
 		"ext-pcre": "*",
 		"ext-session": "*",
 		"ext-xml": "*",
-		"composer-runtime-api": "^2.0",
+		"composer-runtime-api": "^2.1",
 		"bacon/bacon-qr-code": "^2.0",
 		"christian-riesen/base32": "^1.6",
 		"cogpowered/finediff": "~0.3.1",
@@ -50,8 +50,8 @@
 		"egulias/email-validator": "^3.1",
 		"enshrined/svg-sanitize": "^0.14.1",
 		"guzzlehttp/guzzle": "^7.3.0",
-		"guzzlehttp/psr7": "^1.7.0 || ^2.0",
 		"guzzlehttp/promises": "^1.4.0",
+		"guzzlehttp/psr7": "^1.7.0 || ^2.0",
 		"nikic/php-parser": "^4.10.4",
 		"phpdocumentor/reflection-docblock": "^5.2",
 		"phpdocumentor/type-resolver": "^1.4",
diff --git a/composer.lock b/composer.lock
index 994fca3ac9387085081e34e79a52c242944b1e55..dfe49e158eb1b809b4518966bc36e8c3376814fe 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "e2dcfec64d2bf1cd1df99b52b5c362ca",
+    "content-hash": "1b0a189cecb7784c25b2a2e33e2de299",
     "packages": [
         {
             "name": "bacon/bacon-qr-code",
@@ -8275,7 +8275,7 @@
         "ext-pcre": "*",
         "ext-session": "*",
         "ext-xml": "*",
-        "composer-runtime-api": "^2.0"
+        "composer-runtime-api": "^2.1"
     },
     "platform-dev": [],
     "platform-overrides": {
diff --git a/typo3/sysext/core/Classes/Composer/PackageArtifactBuilder.php b/typo3/sysext/core/Classes/Composer/PackageArtifactBuilder.php
index f05a8edc17b71d6039b7dea5e1bcb073b76771e6..2b143f4730587ed0b48c52f7c57fa3d0c626359a 100644
--- a/typo3/sysext/core/Classes/Composer/PackageArtifactBuilder.php
+++ b/typo3/sysext/core/Classes/Composer/PackageArtifactBuilder.php
@@ -47,6 +47,7 @@ use TYPO3\CMS\Core\Utility\PathUtility;
 class PackageArtifactBuilder extends PackageManager implements InstallerScript
 {
     private Event $event;
+    private Config $config;
 
     /**
      * Array of package keys that are installed by Composer but have no relation to TYPO3 extension API
@@ -78,11 +79,12 @@ class PackageArtifactBuilder extends PackageManager implements InstallerScript
     public function run(Event $event): bool
     {
         $this->event = $event;
+        $this->config = Config::load($this->event->getComposer(), $this->event->getIO());
         $composer = $this->event->getComposer();
         $basePath = Config::load($composer)->get('base-dir');
         $this->packagesBasePath = $basePath . '/';
         foreach ($this->extractPackageMapFromComposer() as [$composerPackage, $path, $extensionKey]) {
-            $packagePath = PathUtility::sanitizeTrailingSeparator($path ?: $basePath);
+            $packagePath = PathUtility::sanitizeTrailingSeparator($path);
             $package = new Package($this, $extensionKey, $packagePath, true);
             $package->makePathRelative(new Filesystem(), $basePath);
             $package->getPackageMetaData()->setVersion($composerPackage->getPrettyVersion());
@@ -180,17 +182,18 @@ class PackageArtifactBuilder extends PackageManager implements InstallerScript
      */
     private function handleRootPackage(PackageInterface $rootPackage, string $extensionKey): array
     {
-        if ($rootPackage->getType() !== 'typo3-cms-extension' || !file_exists($this->packagesBasePath . 'Resources/Public/')) {
-            return [$rootPackage, $this->packagesBasePath, $extensionKey];
+        $baseDir = $this->config->get('base-dir');
+        if ($rootPackage->getType() !== 'typo3-cms-extension' || !file_exists($baseDir . '/Resources/Public/')) {
+            return [$rootPackage, $baseDir, $extensionKey];
         }
         $composer = $this->event->getComposer();
         $typo3ExtensionInstallPath = $composer->getInstallationManager()->getInstaller('typo3-cms-extension')->getInstallPath($rootPackage);
         $filesystem = new Filesystem();
         $filesystem->ensureDirectoryExists(dirname($typo3ExtensionInstallPath));
         if (!file_exists($typo3ExtensionInstallPath) && !$filesystem->isSymlinkedDirectory($typo3ExtensionInstallPath)) {
-            $filesystem->relativeSymlink($this->packagesBasePath, $typo3ExtensionInstallPath);
+            $filesystem->relativeSymlink($baseDir, $typo3ExtensionInstallPath);
         }
-        if (realpath($this->packagesBasePath) !== realpath($typo3ExtensionInstallPath)) {
+        if (realpath($baseDir) !== realpath($typo3ExtensionInstallPath)) {
             $this->event->getIO()->warning('The root package is of type "typo3-cms-extension" and has public resources, but could not be linked to typo3conf/ext directory, because target directory already exits.');
         }
 
diff --git a/typo3/sysext/core/Classes/Core/Environment.php b/typo3/sysext/core/Classes/Core/Environment.php
index 8ce8c9eb3f76dc4d13bbc1bf4b5b0f50999d8e1a..dfe0ac1d56124e0fd63fb3d62e9ef359553761e9 100644
--- a/typo3/sysext/core/Classes/Core/Environment.php
+++ b/typo3/sysext/core/Classes/Core/Environment.php
@@ -17,6 +17,9 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Core\Core;
 
+use Composer\InstalledVersions;
+use TYPO3\CMS\Core\Utility\PathUtility;
+
 /**
  * This class is initialized once in the SystemEnvironmentBuilder, and can then
  * be used throughout the application to access common variables
@@ -70,6 +73,11 @@ class Environment
      */
     protected static $projectPath;
 
+    /**
+     * @var string
+     */
+    protected static $composerRootPath;
+
     /**
      * @var string
      */
@@ -126,6 +134,7 @@ class Environment
         self::$composerMode = $composerMode;
         self::$context = $context;
         self::$projectPath = $projectPath;
+        self::$composerRootPath = $composerMode ? PathUtility::getCanonicalPath(InstalledVersions::getRootPackage()['install_path']) : '';
         self::$publicPath = $publicPath;
         self::$varPath = $varPath;
         self::$configPath = $configPath;
@@ -181,6 +190,24 @@ class Environment
         return self::$projectPath;
     }
 
+    /**
+     * In most cases in composer-mode setups this is the same as project path.
+     * However since the project path is configurable, the paths may differ.
+     * In future versions this configurability will go away and this method will be removed.
+     * This path is only required for some internal path handling regarding package paths until then.
+     * @internal
+     *
+     * @return string The absolute path to the composer root directory without the trailing slash
+     */
+    public static function getComposerRootPath(): string
+    {
+        if (self::$composerMode === false) {
+            throw new \BadMethodCallException('Composer root path is only available in Composer mode', 1631700480);
+        }
+
+        return self::$composerRootPath;
+    }
+
     /**
      * The public web folder where index.php (= the frontend application) is put, without trailing slash.
      * For non-composer installations, the project path = the public path.
diff --git a/typo3/sysext/core/Classes/Package/Package.php b/typo3/sysext/core/Classes/Package/Package.php
index be1b7e36ac0ce6e5c60a46880015e1f0951941d4..2b5d2eb97246627ccb20069a7b02b733043fdc1c 100644
--- a/typo3/sysext/core/Classes/Package/Package.php
+++ b/typo3/sysext/core/Classes/Package/Package.php
@@ -15,12 +15,11 @@
 
 namespace TYPO3\CMS\Core\Package;
 
-use Composer\InstalledVersions;
 use Composer\Util\Filesystem;
+use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Package\Exception\InvalidPackageKeyException;
 use TYPO3\CMS\Core\Package\Exception\InvalidPackagePathException;
 use TYPO3\CMS\Core\Package\MetaData\PackageConstraint;
-use TYPO3\CMS\Core\Utility\PathUtility;
 
 /**
  * A Package representing the details of an extension and/or a composer package
@@ -235,7 +234,7 @@ class Package implements PackageInterface
         }
         $this->isRelativePackagePath = false;
 
-        return $this->packagePath = PathUtility::getCanonicalPath(InstalledVersions::getRootPackage()['install_path'] . '/' . $this->packagePath) . '/';
+        return $this->packagePath = Environment::getComposerRootPath() . '/' . $this->packagePath;
     }
 
     /**
@@ -248,7 +247,7 @@ class Package implements PackageInterface
     public function makePathRelative(Filesystem $filesystem, string $composerRootPath): void
     {
         $this->isRelativePackagePath = true;
-        $this->packagePath = $filesystem->findShortestPath($composerRootPath, $this->packagePath, true);
+        $this->packagePath = ($composerRootPath . '/') === $this->packagePath ? '' : $filesystem->findShortestPath($composerRootPath, $this->packagePath, true) . '/';
     }
 
     /**
diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json
index a45bc7c679a9a55d0bef25288defe9db15b5c45e..30184367c65339b84b6ee1ca9e240fb5b549ce5c 100644
--- a/typo3/sysext/core/composer.json
+++ b/typo3/sysext/core/composer.json
@@ -26,7 +26,7 @@
 		"ext-pcre": "*",
 		"ext-session": "*",
 		"ext-xml": "*",
-		"composer-runtime-api": "^2.0",
+		"composer-runtime-api": "^2.1",
 		"bacon/bacon-qr-code": "^2.0",
 		"christian-riesen/base32": "^1.6",
 		"cogpowered/finediff": "~0.3.1",