diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-92562-FrontendGroupsResolvedDirectlyAfterTheFrontendUserItself.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-92562-FrontendGroupsResolvedDirectlyAfterTheFrontendUserItself.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ab1c8d95762990162eb6b7c2d1d264c430b9765b
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-92562-FrontendGroupsResolvedDirectlyAfterTheFrontendUserItself.rst
@@ -0,0 +1,44 @@
+.. include:: ../../Includes.txt
+
+==================================================================================
+Feature: #92562 - Frontend groups resolved directly after the Frontend User itself
+==================================================================================
+
+See :issue:`92562`
+
+Description
+===========
+
+For legacy purposes, the valid frontend user groups were added while resolving
+the root line in TypoScriptFrontendController. This is much later in the frontend
+request process than the preparation of the Frontend User, which is resolved by
+the session or form credentials.
+
+There are several reasons for the historic behavior:
+* Special functionality like "pages.fe_login_mode" which can override groups
+  based on the current root line
+* Previewing frontend user groups via the Admin Panel for backend users
+
+However, this historic behavior led to inconsistencies, especially with the
+Context API to retrieve the correct usergroups in PSR-15 middlewares to build custom APIs.
+
+In addition, this functionality is now extracted from TSFE and into the middleware,
+further decoupling the User authentication from the TSFE object.
+
+
+Impact
+======
+
+When the PSR-15 middleware is setting up the FrontendUserAuthentication object
+at a very early stage of the frontend request, the groups are resolved directly
+afterwards, leaving the FrontendUserAuthentication object in a consistent state
+for further middlewares to work with the appropriate groups.
+
+Please note that any existing custom AuthenticationService for resolving frontend
+user groups, which might rely on a valid TSFE object will have to be evaluated
+if it still works in TYPO3 v11.
+
+Also: Further middlewares and TSFE itself can still override the assigned groups
+due to previewing behavior.
+
+.. index:: ext:frontend
diff --git a/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php b/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php
index b17fb2b7672a54d3d18fc10bb083dbab19eae6f1..eddd0c339cf233eef34d56fcec434720e8511f97 100644
--- a/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php
+++ b/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Frontend\Authentication;
 use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
 use TYPO3\CMS\Core\Authentication\AuthenticationService;
 use TYPO3\CMS\Core\Configuration\Features;
+use TYPO3\CMS\Core\Context\UserAspect;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Session\Backend\Exception\SessionNotFoundException;
 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
@@ -382,6 +383,49 @@ class FrontendUserAuthentication extends AbstractUserAuthentication
         return !empty($this->groupData['uid']) ? count($this->groupData['uid']) : 0;
     }
 
+    /**
+     * Initializes the front-end user groups for the context API,
+     * based on the user groups and the logged-in state.
+     *
+     * @param bool $respectUserGroups used with the $TSFE->loginAllowedInBranch flag to disable the inclusion of the users' groups
+     * @return UserAspect
+     */
+    public function createUserAspect(bool $respectUserGroups = true): UserAspect
+    {
+        $userGroups = [0];
+        $isUserAndGroupSet = is_array($this->user) && !empty($this->groupData['uid']);
+        if ($isUserAndGroupSet) {
+            // group -2 is not an existing group, but denotes a 'default' group when a user IS logged in.
+            // This is used to let elements be shown for all logged in users!
+            $userGroups[] = -2;
+            $groupsFromUserRecord = $this->groupData['uid'];
+        } else {
+            // group -1 is not an existing group, but denotes a 'default' group when not logged in.
+            // This is used to let elements be hidden, when a user is logged in!
+            $userGroups[] = -1;
+            if ($respectUserGroups) {
+                // For cases where logins are not banned from a branch usergroups can be set based on IP masks so we should add the usergroups uids.
+                $groupsFromUserRecord = $this->groupData['uid'];
+            } else {
+                // Set to blank since we will NOT risk any groups being set when no logins are allowed!
+                $groupsFromUserRecord = [];
+            }
+        }
+        // Make unique and sort the groups
+        $groupsFromUserRecord = array_unique($groupsFromUserRecord);
+        if ($respectUserGroups && !empty($groupsFromUserRecord)) {
+            sort($groupsFromUserRecord);
+            $userGroups = array_merge($userGroups, array_map('intval', $groupsFromUserRecord));
+        }
+
+        // For every 60 seconds the is_online timestamp for a logged-in user is updated
+        if ($isUserAndGroupSet) {
+            $this->updateOnlineTimestamp();
+        }
+
+        $this->logger->debug('Valid frontend usergroups: ' . implode(',', $userGroups));
+        return GeneralUtility::makeInstance(UserAspect::class, $this, $userGroups);
+    }
     /**
      * Returns the parsed TSconfig for the fe_user
      * The TSconfig will be cached in $this->userTS.
diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
index 4cf890f556330965b338276568d7bcabcb4af7a1..1cb3770e1eb4aef510b8a90322d07a4c2aa7fc75 100644
--- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
+++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
@@ -658,43 +658,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      */
     public function initUserGroups()
     {
-        $userGroups = [0];
-        // no matter if we have an active user we try to fetch matching groups which can be set without an user (simulation for instance!)
-        $this->fe_user->fetchGroupData();
-        $isUserAndGroupSet = is_array($this->fe_user->user) && !empty($this->fe_user->groupData['uid']);
-        if ($isUserAndGroupSet) {
-            // group -2 is not an existing group, but denotes a 'default' group when a user IS logged in.
-            // This is used to let elements be shown for all logged in users!
-            $userGroups[] = -2;
-            $groupsFromUserRecord = $this->fe_user->groupData['uid'];
-        } else {
-            // group -1 is not an existing group, but denotes a 'default' group when not logged in.
-            // This is used to let elements be hidden, when a user is logged in!
-            $userGroups[] = -1;
-            if ($this->loginAllowedInBranch) {
-                // For cases where logins are not banned from a branch usergroups can be set based on IP masks so we should add the usergroups uids.
-                $groupsFromUserRecord = $this->fe_user->groupData['uid'];
-            } else {
-                // Set to blank since we will NOT risk any groups being set when no logins are allowed!
-                $groupsFromUserRecord = [];
-            }
-        }
-        // Clean up.
-        // Make unique and sort the groups
-        $groupsFromUserRecord = array_unique($groupsFromUserRecord);
-        if (!empty($groupsFromUserRecord) && !$this->loginAllowedInBranch_mode) {
-            sort($groupsFromUserRecord);
-            $userGroups = array_merge($userGroups, array_map('intval', $groupsFromUserRecord));
-        }
-
-        $this->context->setAspect('frontend.user', GeneralUtility::makeInstance(UserAspect::class, $this->fe_user, $userGroups));
-
-        // For every 60 seconds the is_online timestamp for a logged-in user is updated
-        if ($isUserAndGroupSet) {
-            $this->fe_user->updateOnlineTimestamp();
-        }
-
-        $this->logger->debug('Valid usergroups for TSFE: ' . implode(',', $userGroups));
+        $userAspect = $this->fe_user->createUserAspect((bool)$this->loginAllowedInBranch);
+        $this->context->setAspect('frontend.user', $userAspect);
     }
 
     /**
@@ -777,15 +742,12 @@ class TypoScriptFrontendController implements LoggerAwareInterface
         $this->loginAllowedInBranch = $this->checkIfLoginAllowedInBranch();
         // Logins are not allowed, but there is a login, so will we run this.
         if (!$this->loginAllowedInBranch && $this->isUserOrGroupSet()) {
+            // Clear out user, and the group will be re-set in >initUserGroups() due to
+            // $this->loginAllowedInBranch = false
             if ($this->loginAllowedInBranch_mode === 'all') {
-                // Clear out user and group:
                 $this->fe_user->hideActiveLogin();
-                $userGroups = [0, -1];
-            } else {
-                $userGroups = [0, -2];
             }
-            $this->context->setAspect('frontend.user', GeneralUtility::makeInstance(UserAspect::class, $this->fe_user, $userGroups));
-            // Fetching the id again, now with the preview settings reset.
+            // Fetching the id again, now with the preview settings reset and respecting $this->loginAllowedInBranch = false
             $this->fetch_the_id($request);
         }
         // Final cleaning.
diff --git a/typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php b/typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php
index 99e92e9efdfad6d5ac65617bbf2f4beb8a2d4057..63025b4f93121242316ef4d6e0a5c8d21862b169 100644
--- a/typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php
+++ b/typo3/sysext/frontend/Classes/Middleware/FrontendUserAuthenticator.php
@@ -21,9 +21,7 @@ use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
-use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
 use TYPO3\CMS\Core\Context\Context;
-use TYPO3\CMS\Core\Context\UserAspect;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
 
@@ -69,9 +67,13 @@ class FrontendUserAuthenticator implements MiddlewareInterface
         // Authenticate now
         $frontendUser->start();
         $frontendUser->unpack_uc();
+        // no matter if we have an active user we try to fetch matching groups which can
+        // be set without an user (simulation for instance!)
+        $frontendUser->fetchGroupData();
 
-        // Register the frontend user as aspect and within the session
-        $this->setFrontendUserAspect($frontendUser);
+        // Register the frontend user as aspect and within the request
+        $userAspect = $frontendUser->createUserAspect();
+        $this->context->setAspect('frontend.user', $userAspect);
         $request = $request->withAttribute('frontend.user', $frontendUser);
 
         $response = $handler->handle($request);
@@ -120,14 +122,4 @@ class FrontendUserAuthenticator implements MiddlewareInterface
         }
         return $request;
     }
-
-    /**
-     * Register the frontend user as aspect
-     *
-     * @param AbstractUserAuthentication $user
-     */
-    protected function setFrontendUserAspect(AbstractUserAuthentication $user)
-    {
-        $this->context->setAspect('frontend.user', GeneralUtility::makeInstance(UserAspect::class, $user));
-    }
 }