diff --git a/typo3/sysext/backend/Classes/FrontendBackendUserAuthentication.php b/typo3/sysext/backend/Classes/FrontendBackendUserAuthentication.php
index 0a92a42c144cafd6d5fb07ba8da9a9206e759ba1..89d76f8e59c18a92f4a4e3fb45e9c81da192498e 100644
--- a/typo3/sysext/backend/Classes/FrontendBackendUserAuthentication.php
+++ b/typo3/sysext/backend/Classes/FrontendBackendUserAuthentication.php
@@ -15,6 +15,7 @@
 
 namespace TYPO3\CMS\Backend;
 
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Context\Context;
@@ -71,7 +72,7 @@ class FrontendBackendUserAuthentication extends BackendUserAuthentication
      *
      * @return bool Returns TRUE if access is OK
      */
-    public function backendCheckLogin()
+    public function backendCheckLogin(ServerRequestInterface $request = null)
     {
         if (empty($this->user['uid'])) {
             return false;
diff --git a/typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php b/typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php
index 4d88ad4a7c496f1b56694b5c8c831af7011ffcc8..062f73e8a30c9d7d68ee75b5d4104cc5cb3f0b07 100644
--- a/typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php
+++ b/typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php
@@ -134,7 +134,7 @@ class BackendUserAuthenticator extends \TYPO3\CMS\Core\Middleware\BackendUserAut
             }
         }
         if ($this->context->getAspect('backend.user')->isLoggedIn()) {
-            $GLOBALS['BE_USER']->initializeBackendLogin();
+            $GLOBALS['BE_USER']->initializeBackendLogin($request);
             // Reset the limiter after successful login
             if ($rateLimiter) {
                 $rateLimiter->reset();
diff --git a/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php b/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php
index e31bd56c79d4cb65d8580d016a7baa95b7ecd084..0802faf6a09384e1a43bbb86b3ecaaa0ae66363d 100644
--- a/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php
+++ b/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php
@@ -24,6 +24,7 @@ use Symfony\Component\Mailer\Exception\TransportException;
 use Symfony\Component\Mime\Exception\RfcComplianceException;
 use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedInEvent;
 use TYPO3\CMS\Core\Http\ServerRequestFactory;
 use TYPO3\CMS\Core\Mail\FluidEmail;
 use TYPO3\CMS\Core\Mail\MailerInterface;
@@ -39,46 +40,41 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  *
  * @internal this is not part of TYPO3 API as this is an internal hook
  */
-class EmailLoginNotification implements LoggerAwareInterface
+final class EmailLoginNotification implements LoggerAwareInterface
 {
     use LoggerAwareTrait;
 
-    /**
-     * @var int
-     */
-    private $warningMode;
-
-    /**
-     * @var string
-     */
-    private $warningEmailRecipient;
+    private int $warningMode = 0;
+    private string $warningEmailRecipient = '';
 
     /**
      * @var ServerRequestInterface
      */
     private $request;
 
-    public function __construct()
-    {
+    public function __construct(
+        private readonly MailerInterface $mailer
+    ) {
         $this->warningMode = (int)($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode'] ?? 0);
         $this->warningEmailRecipient = $GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr'] ?? '';
     }
 
     /**
      * Sends an email notification to warning_email_address and/or the logged-in user's email address.
-     *
-     * @param array $parameters array data
-     * @param BackendUserAuthentication $currentUser the currently just-logged in user
      */
-    public function emailAtLogin(array $parameters, BackendUserAuthentication $currentUser): void
+    public function emailAtLogin(AfterUserLoggedInEvent $event): void
     {
-        $user = $parameters['user'];
+        if (!$event->getUser() instanceof BackendUserAuthentication) {
+            return;
+        }
+        $currentUser = $event->getUser();
+        $user = $currentUser->user;
         $genericLoginWarning = $this->warningMode > 0 && !empty($this->warningEmailRecipient);
         $userLoginNotification = ($currentUser->uc['emailMeAtLogin'] ?? null) && GeneralUtility::validEmail($user['email']);
         if (!$genericLoginWarning && !$userLoginNotification) {
             return;
         }
-        $this->request = $parameters['request'] ?? $GLOBALS['TYPO3_REQUEST'] ?? ServerRequestFactory::fromGlobals();
+        $this->request = $event->getRequest() ?? $GLOBALS['TYPO3_REQUEST'] ?? ServerRequestFactory::fromGlobals();
 
         if ($genericLoginWarning) {
             $prefix = $currentUser->isAdmin() ? '[AdminLoginWarning]' : '[LoginWarning]';
@@ -114,8 +110,7 @@ class EmailLoginNotification implements LoggerAwareInterface
                 'headline' => $headline,
             ]);
         try {
-            // TODO: DI should be used to inject the MailerInterface
-            GeneralUtility::makeInstance(MailerInterface::class)->send($email);
+            $this->mailer->send($email);
         } catch (TransportException $e) {
             $this->logger->warning('Could not send notification email to "{recipient}" due to mailer settings error', [
                 'recipient' => $recipient,
diff --git a/typo3/sysext/backend/Configuration/Services.yaml b/typo3/sysext/backend/Configuration/Services.yaml
index 48dc69a1268470fc62ce885895505562dd0a05b6..deb99a50d33458bdea608e6dda8c95ea04dccd9c 100644
--- a/typo3/sysext/backend/Configuration/Services.yaml
+++ b/typo3/sysext/backend/Configuration/Services.yaml
@@ -173,6 +173,12 @@ services:
       - name: event.listener
         identifier: 'typo3/cms-backend/failed-login-attempt-notification'
 
+  TYPO3\CMS\Backend\Security\EmailLoginNotification:
+    tags:
+      - name: event.listener
+        identifier: 'typo3/cms-backend/login-notification'
+        method: 'emailAtLogin'
+
   TYPO3\CMS\Backend\EventListener\SilentSiteLanguageFlagMigration:
     tags:
       - name: event.listener
diff --git a/typo3/sysext/backend/Tests/Unit/Security/EmailLoginNotificationTest.php b/typo3/sysext/backend/Tests/Unit/Security/EmailLoginNotificationTest.php
index 0a34601c0d39f5f9550ae744e0b0f44f05a89ee4..ce1f2a6da13329554240b5bdb5a61a40a6d01d28 100644
--- a/typo3/sysext/backend/Tests/Unit/Security/EmailLoginNotificationTest.php
+++ b/typo3/sysext/backend/Tests/Unit/Security/EmailLoginNotificationTest.php
@@ -20,6 +20,7 @@ namespace TYPO3\CMS\Backend\Tests\Unit\Security;
 use PHPUnit\Framework\MockObject\MockObject;
 use TYPO3\CMS\Backend\Security\EmailLoginNotification;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedInEvent;
 use TYPO3\CMS\Core\Mail\FluidEmail;
 use TYPO3\CMS\Core\Mail\MailerInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -39,18 +40,16 @@ class EmailLoginNotificationTest extends UnitTestCase
             ->disableOriginalConstructor()
             ->getMock();
         $backendUser->uc['emailMeAtLogin'] = 1;
-
-        $userData = [
+        $backendUser->user = [
             'email' => 'test@acme.com',
         ];
 
         $mailMessage = $this->setUpMailMessageMock();
         $mailerMock = $this->createMock(MailerInterface::class);
         $mailerMock->expects(self::once())->method('send')->with($mailMessage);
-        GeneralUtility::addInstance(MailerInterface::class, $mailerMock);
 
-        $subject = new EmailLoginNotification();
-        $subject->emailAtLogin(['user' => $userData], $backendUser);
+        $subject = new EmailLoginNotification($mailerMock);
+        $subject->emailAtLogin(new AfterUserLoggedInEvent($backendUser));
     }
 
     /**
@@ -65,14 +64,14 @@ class EmailLoginNotificationTest extends UnitTestCase
             ->disableOriginalConstructor()
             ->getMock();
         $backendUser->uc['emailMeAtLogin'] = 0;
-
-        $userData = [
+        $backendUser->user = [
             'username' => 'karl',
             'email' => 'test@acme.com',
         ];
+        $mailerMock = $this->createMock(MailerInterface::class);
 
-        $subject = new EmailLoginNotification();
-        $subject->emailAtLogin(['user' => $userData], $backendUser);
+        $subject = new EmailLoginNotification($mailerMock);
+        $subject->emailAtLogin(new AfterUserLoggedInEvent($backendUser));
 
         // no additional assertion here, as the test would fail due to missing mail mocking if it actually tried to send an email
     }
@@ -89,14 +88,14 @@ class EmailLoginNotificationTest extends UnitTestCase
             ->disableOriginalConstructor()
             ->getMock();
         $backendUser->uc['emailMeAtLogin'] = 1;
-
-        $userData = [
+        $backendUser->user = [
             'username' => 'karl',
             'email' => 'dot.com',
         ];
+        $mailerMock = $this->createMock(MailerInterface::class);
 
-        $subject = new EmailLoginNotification();
-        $subject->emailAtLogin(['user' => $userData], $backendUser);
+        $subject = new EmailLoginNotification($mailerMock);
+        $subject->emailAtLogin(new AfterUserLoggedInEvent($backendUser));
 
         // no additional assertion here, as the test would fail due to missing mail mocking if it actually tried to send an email
     }
@@ -115,18 +114,16 @@ class EmailLoginNotificationTest extends UnitTestCase
             ->disableOriginalConstructor()
             ->getMock();
         $backendUser->method('isAdmin')->willReturn(true);
-
-        $userData = [
+        $backendUser->user = [
             'username' => 'karl',
         ];
 
         $mailMessage = $this->setUpMailMessageMock('typo3-admin@acme.com');
         $mailerMock = $this->createMock(MailerInterface::class);
         $mailerMock->expects(self::once())->method('send')->with($mailMessage);
-        GeneralUtility::addInstance(MailerInterface::class, $mailerMock);
 
-        $subject = new EmailLoginNotification();
-        $subject->emailAtLogin(['user' => $userData], $backendUser);
+        $subject = new EmailLoginNotification($mailerMock);
+        $subject->emailAtLogin(new AfterUserLoggedInEvent($backendUser));
     }
 
     /**
@@ -143,18 +140,16 @@ class EmailLoginNotificationTest extends UnitTestCase
             ->disableOriginalConstructor()
             ->getMock();
         $backendUser->method('isAdmin')->willReturn(true);
-
-        $userData = [
+        $backendUser->user = [
             'username' => 'karl',
         ];
 
         $mailMessage = $this->setUpMailMessageMock('typo3-admin@acme.com');
         $mailerMock = $this->createMock(MailerInterface::class);
         $mailerMock->expects(self::once())->method('send')->with($mailMessage);
-        GeneralUtility::addInstance(MailerInterface::class, $mailerMock);
 
-        $subject = new EmailLoginNotification();
-        $subject->emailAtLogin(['user' => $userData], $backendUser);
+        $subject = new EmailLoginNotification($mailerMock);
+        $subject->emailAtLogin(new AfterUserLoggedInEvent($backendUser));
     }
 
     /**
@@ -171,18 +166,16 @@ class EmailLoginNotificationTest extends UnitTestCase
             ->disableOriginalConstructor()
             ->getMock();
         $backendUser->method('isAdmin')->willReturn(false);
-
-        $userData = [
+        $backendUser->user = [
             'username' => 'karl',
         ];
 
         $mailMessage = $this->setUpMailMessageMock('typo3-admin@acme.com');
         $mailerMock = $this->createMock(MailerInterface::class);
         $mailerMock->expects(self::once())->method('send')->with($mailMessage);
-        GeneralUtility::addInstance(MailerInterface::class, $mailerMock);
 
-        $subject = new EmailLoginNotification();
-        $subject->emailAtLogin(['user' => $userData], $backendUser);
+        $subject = new EmailLoginNotification($mailerMock);
+        $subject->emailAtLogin(new AfterUserLoggedInEvent($backendUser));
     }
 
     /**
@@ -199,13 +192,13 @@ class EmailLoginNotificationTest extends UnitTestCase
             ->disableOriginalConstructor()
             ->getMock();
         $backendUser->method('isAdmin')->willReturn(false);
-
-        $userData = [
+        $backendUser->user = [
             'username' => 'karl',
         ];
+        $mailerMock = $this->createMock(MailerInterface::class);
 
-        $subject = new EmailLoginNotification();
-        $subject->emailAtLogin(['user' => $userData], $backendUser);
+        $subject = new EmailLoginNotification($mailerMock);
+        $subject->emailAtLogin(new AfterUserLoggedInEvent($backendUser));
 
         // no additional assertion here as the test would fail due to not mocking the email API
     }
diff --git a/typo3/sysext/backend/ext_localconf.php b/typo3/sysext/backend/ext_localconf.php
index aac771a2ff9b9585abc72a031494964e9939c78a..f890754079526ee3c06ffb8a8eb04ea8c1eeee9b 100644
--- a/typo3/sysext/backend/ext_localconf.php
+++ b/typo3/sysext/backend/ext_localconf.php
@@ -4,7 +4,6 @@ declare(strict_types=1);
 
 use TYPO3\CMS\Backend\Backend\Avatar\DefaultAvatarProvider;
 use TYPO3\CMS\Backend\LoginProvider\UsernamePasswordLoginProvider;
-use TYPO3\CMS\Backend\Security\EmailLoginNotification;
 use TYPO3\CMS\Backend\View\BackendLayout\PageTsBackendLayoutDataProvider;
 
 defined('TYPO3') or die();
@@ -25,4 +24,3 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['livesearch']['page'] = 'pages';
 
 // Register BackendLayoutDataProvider for PageTs
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['BackendLayoutDataProvider']['pagets'] = PageTsBackendLayoutDataProvider::class;
-$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['backendUserLogin']['sendEmailOnLogin'] = EmailLoginNotification::class . '->emailAtLogin';
diff --git a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php
index 42a6fdeb3fc58e0a8fe3401a1fa6859cd7cb3b26..8bed9c67fbe53cf692165d8c2b21dddf3395e65d 100644
--- a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php
+++ b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php
@@ -21,7 +21,9 @@ use Psr\Http\Message\ServerRequestInterface;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerAwareTrait;
 use Symfony\Component\HttpFoundation\Cookie;
+use TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedOutEvent;
 use TYPO3\CMS\Core\Authentication\Event\BeforeRequestTokenProcessedEvent;
+use TYPO3\CMS\Core\Authentication\Event\BeforeUserLogoutEvent;
 use TYPO3\CMS\Core\Authentication\Event\LoginAttemptFailedEvent;
 use TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry;
 use TYPO3\CMS\Core\Authentication\Mfa\MfaRequiredException;
@@ -879,14 +881,36 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface
     {
         $this->logger->debug('logoff: ses_id = {session}', ['session' => sha1($this->userSession->getIdentifier())]);
 
-        $_params = [];
-        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] ?? [] as $_funcRef) {
-            if ($_funcRef) {
-                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
+        $dispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
+
+        $event = new BeforeUserLogoutEvent($this);
+        $event = $dispatcher->dispatch($event);
+
+        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] ?? null)) {
+            trigger_error(
+                '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'t3lib/class.t3lib_userauth.php\'][\'logoff_pre_processing\'] will be removed in TYPO3 v13.0. Use the PSR-14 "BeforeUserLogoutEvent" instead.',
+                E_USER_DEPRECATED
+            );
+        }
+
+        if ($event->shouldLogout()) {
+            $_params = [];
+            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] ?? [] as $_funcRef) {
+                if ($_funcRef) {
+                    GeneralUtility::callUserFunction($_funcRef, $_params, $this);
+                }
             }
+            $this->performLogoff();
         }
-        $this->performLogoff();
 
+        $dispatcher->dispatch(new AfterUserLoggedOutEvent($this));
+
+        if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_post_processing'] ?? null)) {
+            trigger_error(
+                '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'t3lib/class.t3lib_userauth.php\'][\'logoff_post_processing\'] will be removed in TYPO3 v13.0. Use the PSR-14 "BeforeUserLogoutEvent" instead.',
+                E_USER_DEPRECATED
+            );
+        }
         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_post_processing'] ?? [] as $_funcRef) {
             if ($_funcRef) {
                 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
diff --git a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
index d40f03e474f21e482dbc0bcdce44ca9dbacb5da0..2c44304cccb96c9a9e419e0bdf2cd91e117b30b7 100644
--- a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
+++ b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
@@ -15,8 +15,11 @@
 
 namespace TYPO3\CMS\Core\Authentication;
 
+use Psr\EventDispatcher\EventDispatcherInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Module\ModuleProvider;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedInEvent;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Database\Connection;
@@ -2066,7 +2069,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
      * @throws \RuntimeException
      * @todo deprecate
      */
-    public function backendCheckLogin()
+    public function backendCheckLogin(ServerRequestInterface $request = null)
     {
         if (empty($this->user['uid'])) {
             // @todo: throw a proper AccessDeniedException in TYPO3 v12.0. and handle this functionality in the calling code
@@ -2075,7 +2078,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
             throw new ImmediateResponseException(new RedirectResponse($url, 303), 1607271747);
         }
         if ($this->isUserAllowedToLogin()) {
-            $this->initializeBackendLogin();
+            $this->initializeBackendLogin($request);
         } else {
             // @todo: throw a proper AccessDeniedException in TYPO3 v12.0.
             throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1294585860);
@@ -2085,7 +2088,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
     /**
      * @internal
      */
-    public function initializeBackendLogin(): void
+    public function initializeBackendLogin(ServerRequestInterface $request = null): void
     {
         // The groups are fetched and ready for permission checking in this initialization.
         // Tables.php must be read before this because stuff like the modules has impact in this
@@ -2101,9 +2104,18 @@ class BackendUserAuthentication extends AbstractUserAuthentication
                     ->getConnectionForTable($this->user_table)
                     ->update($this->user_table, ['password_reset_token' => ''], ['uid' => $this->user['uid']]);
             }
+
+            $event = new AfterUserLoggedInEvent($this, $request);
+            GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch($event);
             // Process hooks
-            $hooks = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['backendUserLogin'];
-            foreach ($hooks ?? [] as $_funcRef) {
+            $hooks = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['backendUserLogin'] ?? [];
+            if (!empty($hooks)) {
+                trigger_error(
+                    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'t3lib/class.t3lib_userauthgroup.php\'][\'backendUserLogin\'] will be removed in TYPO3 v13.0. Use the PSR-14 "AfterUserLoggedInEvent" instead.',
+                    E_USER_DEPRECATED
+                );
+            }
+            foreach ($hooks as $_funcRef) {
                 $_params = ['user' => $this->user];
                 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
             }
diff --git a/typo3/sysext/core/Classes/Authentication/CommandLineUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/CommandLineUserAuthentication.php
index c9e8d4352690053fb11f5ac7a50e047f29d5d168..2d7d43c894576c8efa6a0c1d4f7bfc1435354aa0 100644
--- a/typo3/sysext/core/Classes/Authentication/CommandLineUserAuthentication.php
+++ b/typo3/sysext/core/Classes/Authentication/CommandLineUserAuthentication.php
@@ -113,7 +113,7 @@ class CommandLineUserAuthentication extends BackendUserAuthentication
     /**
      * Logs in the TYPO3 Backend user "_cli_"
      */
-    public function backendCheckLogin()
+    public function backendCheckLogin(ServerRequestInterface $request = null)
     {
         $this->authenticate();
     }
diff --git a/typo3/sysext/core/Classes/Authentication/Event/AfterUserLoggedInEvent.php b/typo3/sysext/core/Classes/Authentication/Event/AfterUserLoggedInEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..15c35c64762a505ee69f72a0f1f13906b926940f
--- /dev/null
+++ b/typo3/sysext/core/Classes/Authentication/Event/AfterUserLoggedInEvent.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Authentication\Event;
+
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
+
+/**
+ * Event fired after a user has been actively logged in (incl. possible MFA).
+ * Currently only working in BE context, but might get opened up in FE context
+ * as well in TYPO3 v13+.
+ */
+final class AfterUserLoggedInEvent
+{
+    public function __construct(
+        private readonly AbstractUserAuthentication $user,
+        private readonly ?ServerRequestInterface $request = null
+    ) {
+    }
+
+    public function getUser(): AbstractUserAuthentication
+    {
+        return $this->user;
+    }
+
+    public function getRequest(): ?ServerRequestInterface
+    {
+        return $this->request;
+    }
+}
diff --git a/typo3/sysext/core/Classes/Authentication/Event/AfterUserLoggedOutEvent.php b/typo3/sysext/core/Classes/Authentication/Event/AfterUserLoggedOutEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..599369b32ac81f42224ff0f03c22074780e6fd15
--- /dev/null
+++ b/typo3/sysext/core/Classes/Authentication/Event/AfterUserLoggedOutEvent.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Authentication\Event;
+
+use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
+
+/**
+ * Event fired after a user has been actively logged out.
+ */
+final class AfterUserLoggedOutEvent
+{
+    public function __construct(
+        private readonly AbstractUserAuthentication $user
+    ) {
+    }
+
+    public function getUser(): AbstractUserAuthentication
+    {
+        return $this->user;
+    }
+}
diff --git a/typo3/sysext/core/Classes/Authentication/Event/BeforeUserLogoutEvent.php b/typo3/sysext/core/Classes/Authentication/Event/BeforeUserLogoutEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..98246b51576961a3967d4883fcb1eec06292bcdc
--- /dev/null
+++ b/typo3/sysext/core/Classes/Authentication/Event/BeforeUserLogoutEvent.php
@@ -0,0 +1,55 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Authentication\Event;
+
+use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
+
+/**
+ * Event fired before a user is going to be actively logged out.
+ * An option to interrupt the regular logout flow from TYPO3 Core (so you can do this yourself)
+ * is also available.
+ */
+final class BeforeUserLogoutEvent
+{
+    private bool $shouldLogout = true;
+
+    public function __construct(
+        private readonly AbstractUserAuthentication $user
+    ) {
+    }
+
+    public function getUser(): AbstractUserAuthentication
+    {
+        return $this->user;
+    }
+
+    public function disableRegularLogoutProcess(): void
+    {
+        $this->shouldLogout = false;
+    }
+
+    public function enableRegularLogoutProcess(): void
+    {
+        $this->shouldLogout = true;
+    }
+
+    public function shouldLogout(): bool
+    {
+        return $this->shouldLogout;
+    }
+}
diff --git a/typo3/sysext/core/Documentation/Changelog/12.3/Deprecation-100307-VariousHooksRelatedToAuthenticationUsers.rst b/typo3/sysext/core/Documentation/Changelog/12.3/Deprecation-100307-VariousHooksRelatedToAuthenticationUsers.rst
new file mode 100644
index 0000000000000000000000000000000000000000..f6dc7656be9306277608f0701f34e2e71d0eb0b1
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.3/Deprecation-100307-VariousHooksRelatedToAuthenticationUsers.rst
@@ -0,0 +1,49 @@
+.. include:: /Includes.rst.txt
+
+.. _deprecation-100307-1679924603:
+
+====================================================================
+Deprecation: #100307 - Various hooks related to authentication users
+====================================================================
+
+See :issue:`100307`
+
+Description
+===========
+
+The following hooks have been marked as deprecated:
+
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_post_processing']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['backendUserLogin']`
+
+They can be used to add notifications or actions to a TYPO3 installation
+after a frontend user or a backend user has been actively logged
+in or logged out.
+
+Impact
+======
+
+If one of the hooks is registered in a TYPO3 installation,
+a PHP :php:`E_USER_DEPRECATED` error is triggered when a user logs
+in or logs out.
+
+
+Affected installations
+======================
+
+TYPO3 installations with custom extensions using one of these hooks.
+
+The extension scanner detects any usage of the hooks.
+
+
+Migration
+=========
+
+Migrate to the newly introduced PSR-14 events:
+
+* :php:`\TYPO3\CMS\Core\Authentication\Event\BeforeUserLogoutEvent`
+* :php:`\TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedOutEvent`
+* :php:`\TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedInEvent`
+
+.. index:: Backend, Frontend, PHP-API, FullyScanned, ext:core
diff --git a/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100307-PSR-14EventsForUserLoginLogout.rst b/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100307-PSR-14EventsForUserLoginLogout.rst
new file mode 100644
index 0000000000000000000000000000000000000000..048fd5ea18a3878ab3982ef570dbe9caeaf881e5
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100307-PSR-14EventsForUserLoginLogout.rst
@@ -0,0 +1,78 @@
+.. include:: /Includes.rst.txt
+
+.. _feature-100307-1679924551:
+
+========================================================
+Feature: #100307 - PSR-14 Events for User Login & Logout
+========================================================
+
+See :issue:`100307`
+
+Description
+===========
+
+Three new PSR-14 events have been added:
+
+-   :php:`\TYPO3\CMS\Core\Authentication\Event\BeforeUserLogoutEvent`
+-   :php:`\TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedOutEvent`
+-   :php:`\TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedInEvent`
+
+The purpose of these events is to trigger any kind of action when a user
+has been successfully logged in or logged out.
+
+TYPO3 Core itself uses :php:`AfterUserLoggedInEvent` in the TYPO3 Backend
+to send an email to a user if the has successfully logged in.
+
+The event features the following methods:
+
+-   :php:`getUser()`: Returns the :php:`\TYPO3\CMS\Core\Authentication\AbstractUserAuthentication` derivative in question
+
+The PSR-14 event :php:`BeforeUserLogoutEvent` on top has the possibility
+to bypass the regular logout process by TYPO3 (removing the cookie and
+the user session) by calling :php:`$event->disableRegularLogoutProcess()`
+in an Event Listener.
+
+The PSR-14 event :php:`AfterUserLoggedInEvent` contains the method
+:php:`getRequest()` to return PSR-7 Request object of the current request.
+
+Registration of the event in your extension's :file:`Services.yaml`:
+
+..  code-block:: yaml
+    :caption: EXT:my_extension/Configuration/Services.yaml
+
+    MyVendor\MyExtension\EventListener\ExampleEventListener:
+        tags:
+          - name: event.listener
+            identifier: 'exampleEventListener'
+
+The corresponding event listener class for :php:`AfterUserLoggedInEvent`:
+
+..  code-block:: php
+    :caption: EXT:my_extension/Classes/EventListener/ExampleEventListener.php
+
+    namespace MyVendor\MyExtension\EventListener;
+
+    use TYPO3\CMS\Core\Authentication\Event\AfterUserLoggedInEvent;
+
+    final class ExampleEventListener
+    {
+        public function __invoke(AfterUserLoggedInEvent $event): void
+        {
+            if (
+                $event->getUser() instanceof BackendUserAuthentication
+                && $event->getUser()->isAdmin()
+            )
+            {
+                // Do something like: Clear all caches after login
+            }
+        }
+    }
+
+
+Impact
+======
+
+It is now possible to modify and adapt user functionality based on successful
+login or active logout.
+
+.. index:: Backend, Frontend, PHP-API, ext:core
diff --git a/typo3/sysext/core/Tests/Unit/Authentication/BackendUserAuthenticationTest.php b/typo3/sysext/core/Tests/Unit/Authentication/BackendUserAuthenticationTest.php
index 7ad30df6f43bf04311ae3a062fad8e3d898b0fd0..0e3019ff6e0dfbcba58a950115a8d77ee06f7150 100644
--- a/typo3/sysext/core/Tests/Unit/Authentication/BackendUserAuthenticationTest.php
+++ b/typo3/sysext/core/Tests/Unit/Authentication/BackendUserAuthenticationTest.php
@@ -17,6 +17,8 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Core\Tests\Unit\Authentication;
 
+use Psr\EventDispatcher\EventDispatcherInterface;
+use Psr\EventDispatcher\ListenerProviderInterface;
 use Psr\Log\NullLogger;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Authentication\IpLocker;
@@ -26,6 +28,7 @@ use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
+use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
 use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
 use TYPO3\CMS\Core\Localization\LanguageService;
@@ -42,6 +45,8 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
 class BackendUserAuthenticationTest extends UnitTestCase
 {
+    protected bool $resetSingletonInstances = true;
+
     /**
      * @test
      */
@@ -68,6 +73,7 @@ class BackendUserAuthenticationTest extends UnitTestCase
         );
         GeneralUtility::addInstance(FormProtectionFactory::class, $formProtectionFactory);
         GeneralUtility::addInstance(BackendFormProtection::class, $formProtectionMock);
+        GeneralUtility::setSingletonInstance(EventDispatcherInterface::class, new EventDispatcher($this->createMock(ListenerProviderInterface::class)));
 
         $sessionBackendMock = $this->createMock(SessionBackendInterface::class);
         $sessionBackendMock->method('remove')->with(self::anything())->willReturn(true);
diff --git a/typo3/sysext/frontend/Classes/Middleware/BackendUserAuthenticator.php b/typo3/sysext/frontend/Classes/Middleware/BackendUserAuthenticator.php
index 95fea6fbba9a316ed5bbdc7bf3cf45270284b7bf..74f6573eea970880899cb95d6a5d93c33b179570 100644
--- a/typo3/sysext/frontend/Classes/Middleware/BackendUserAuthenticator.php
+++ b/typo3/sysext/frontend/Classes/Middleware/BackendUserAuthenticator.php
@@ -104,7 +104,7 @@ class BackendUserAuthenticator extends \TYPO3\CMS\Core\Middleware\BackendUserAut
             $backendUserObject->fetchGroupData();
         }
         // Unset the user initialization if any setting / restriction applies
-        if (!$this->isAuthenticated($backendUserObject, $request->getAttribute('normalizedParams'))) {
+        if (!$this->isAuthenticated($backendUserObject, $request, $request->getAttribute('normalizedParams'))) {
             $backendUserObject = null;
             $this->setBackendUserAspect(null);
         }
@@ -115,7 +115,7 @@ class BackendUserAuthenticator extends \TYPO3\CMS\Core\Middleware\BackendUserAut
      * Implementing the access checks that the TYPO3 CMS bootstrap script does before a user is ever logged in.
      * Returns TRUE if access is OK
      */
-    protected function isAuthenticated(FrontendBackendUserAuthentication $user, NormalizedParams $normalizedParams): bool
+    protected function isAuthenticated(FrontendBackendUserAuthentication $user, ServerRequestInterface $request, NormalizedParams $normalizedParams): bool
     {
         // Check IP
         $ipMask = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'] ?? '');
@@ -126,6 +126,6 @@ class BackendUserAuthenticator extends \TYPO3\CMS\Core\Middleware\BackendUserAut
         if ((bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] && !$normalizedParams->isHttps()) {
             return false;
         }
-        return $user->backendCheckLogin();
+        return $user->backendCheckLogin($request);
     }
 }
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
index 3ddc8f2bfcec49eb0b8cfebf9f75e04be2d28eb9..57fc5c720d4c6ab01b9797671f0d2cc318f24d36 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
@@ -998,4 +998,22 @@ return [
             'Feature-100278-PSR-14EventAfterFailedLoginsInBackendOrFrontendUsers.rst',
         ],
     ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'t3lib/class.t3lib_userauth.php\'][\'logoff_pre_processing\']' => [
+        'restFiles' => [
+            'Deprecation-100307-VariousHooksRelatedToAuthenticationUsers.rst',
+            'Feature-100307-PSR-14EventsForUserLoginLogout.rst',
+        ],
+    ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'t3lib/class.t3lib_userauth.php\'][\'logoff_post_processing\']' => [
+        'restFiles' => [
+            'Deprecation-100307-VariousHooksRelatedToAuthenticationUsers.rst',
+            'Feature-100307-PSR-14EventsForUserLoginLogout.rst',
+        ],
+    ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'t3lib/class.t3lib_userauthgroup.php\'][\'backendUserLogin\']' => [
+        'restFiles' => [
+            'Deprecation-100307-VariousHooksRelatedToAuthenticationUsers.rst',
+            'Feature-100307-PSR-14EventsForUserLoginLogout.rst',
+        ],
+    ],
 ];
diff --git a/typo3/sysext/reactions/Classes/Authentication/ReactionUserAuthentication.php b/typo3/sysext/reactions/Classes/Authentication/ReactionUserAuthentication.php
index 02122c1f8fb55817f3aef0e341d8f7c51a02acc3..2797be4b91cdfe8a18ecb655d3d3c267288c2670 100644
--- a/typo3/sysext/reactions/Classes/Authentication/ReactionUserAuthentication.php
+++ b/typo3/sysext/reactions/Classes/Authentication/ReactionUserAuthentication.php
@@ -66,7 +66,7 @@ class ReactionUserAuthentication extends BackendUserAuthentication
         return null;
     }
 
-    public function backendCheckLogin(): void
+    public function backendCheckLogin(ServerRequestInterface $request = null): void
     {
         // do nothing
     }
@@ -82,7 +82,7 @@ class ReactionUserAuthentication extends BackendUserAuthentication
         return (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] === 0;
     }
 
-    public function initializeBackendLogin(): void
+    public function initializeBackendLogin(ServerRequestInterface $request = null): void
     {
         throw new \RuntimeException('Login Error: No login possible for reaction.', 1669800914);
     }