Examples
In this section some common situations are described:
Send visitors to login page and redirect to original page
A common situation is that visitors who go to a page with access restrictions should go to a login page first and after logging in should be send back to the page they originally requested.
Assume we have a login page with id 2
.
Using TypoScript we can still display links to access restricted pages and send visitors to the login page:
config {
typolinkLinkAccessRestrictedPages = 2
typolinkLinkAccessRestrictedPages_addParams = &return_url=###RETURN_URL###
}
On the login page the login form must be configured to redirect to the original page:
plugin.tx_felogin_login.settings.redirectMode = getpost
(This option can also be set in the flexform configuration of the felogin content element)
If visitors will directly enter the URL of an access restricted page they will be sent to the first page in the rootline to which they have access. Sending those direct visits to a login page is not a job of the felogin plugin, but requires a custom page-not-found handler. In this sense, we refer to Custom error handler implementation for 403 redirects.
Login link visible when not logged in and logout link visible when logged in
Again TypoScript will help you out. The page with the login form has id=2:
10 = TEXT
10 {
value = Login
typolink.parameter = 2
}
[frontend.user.isLoggedIn]
10.value = Logout
10.typolink.additionalParams = &logintype=logout
[end]
Of course there can be solutions with HMENU
items, etc.
Custom error handler implementation for 403 redirects
This section explains how to utilize a custom error handler to catch 403 restricted page errors and allow to forward to a login form, and then redirect back to the originating page after successful login.
-
You need the following site settings in the error handling
There you add the custom 403 error handler and configure the error handler, you create in the following steps.
See also
-
Look up the page ID where a login form (like with EXT:felogin) is placed
This page ID is needed in the following step, so that the error handler will know, where to forward an unauthenticated user to, so that a login can be performed.
Ideally, this should be done by configuring a page ID via the site settings, and referring back to a named ID. See PHP API: accessing site configuration for more information. For reduced complexity, this example uses a hard-coded page ID.
-
Create a new error handler
Redirect
Login Error Handler. php Create a PHP error handler class like the following in a custom extension, like your own sitepackage:
<?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 MyVendor\MySitePackage\Error\PageErrorHandler; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Controller\ErrorPageController; use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface; use TYPO3\CMS\Core\Http\HtmlResponse; use TYPO3\CMS\Core\Http\RedirectResponse; use TYPO3\CMS\Core\LinkHandling\LinkService; use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons; /** * An error handler that redirects to a configured page, where the login * process is handled. Passes a configurable URL parameter (`return_url` or * `redirect_url`) to the target page. */ final class RedirectLoginErrorHandler implements PageErrorHandlerInterface { private const PAGE_ID_LOGIN_FORM = 656; private readonly int $loginRedirectPid; private readonly string $loginRedirectParameter; private readonly Context $context; private readonly LinkService $linkService; private readonly ErrorPageController $errorPageController; public function __construct(private readonly int $statusCode) { $configuration = [ // TODO: Replace with $siteSettings[...] or something else 'loginRedirectTarget' => 't3://page?uid=' . self::PAGE_ID_LOGIN_FORM, 'loginRedirectParameter' => 'return_url', ]; $this->context = GeneralUtility::makeInstance(Context::class); $this->linkService = GeneralUtility::makeInstance(LinkService::class); $this->errorPageController = GeneralUtility::makeInstance(ErrorPageController::class); $urlParams = $this->linkService->resolve($configuration['loginRedirectTarget']); $this->loginRedirectPid = (int)($urlParams['pageuid'] ?? 0); $this->loginRedirectParameter = $configuration['loginRedirectParameter']; } public function handlePageError( ServerRequestInterface $request, string $message, array $reasons = [] ): ResponseInterface { $this->checkHandlerConfiguration(); if ($this->shouldHandleRequest($reasons)) { return $this->handleLoginRedirect($request); } // Show general error message with a 403 HTTP status code return $this->getGenericAccessDeniedResponse($message); } private function getGenericAccessDeniedResponse(string $reason): ResponseInterface { $reason = $reason ? ' Reason: ' . $reason : ''; $content = $this->errorPageController->errorAction( 'Page Not Found', sprintf('The page did not exist or was inaccessible.%s', $reason), 0, $this->statusCode, ); return new HtmlResponse($content, $this->statusCode); } private function handleLoginRedirect(ServerRequestInterface $request): ResponseInterface { if ($this->isLoggedIn()) { return $this->getGenericAccessDeniedResponse( 'The requested page was not accessible with the provided credentials' ); } /** @var Site $site */ $site = $request->getAttribute('site'); $language = $request->getAttribute('language'); $loginUrl = $site->getRouter()->generateUri( $this->loginRedirectPid, [ '_language' => $language, $this->loginRedirectParameter => (string)$request->getUri(), ] ); return new RedirectResponse($loginUrl); } private function shouldHandleRequest(array $reasons): bool { if (!isset($reasons['code'])) { return false; } $accessDeniedReasons = [ PageAccessFailureReasons::ACCESS_DENIED_PAGE_NOT_RESOLVED, PageAccessFailureReasons::ACCESS_DENIED_SUBSECTION_NOT_RESOLVED, ]; $isAccessDenied = in_array($reasons['code'], $accessDeniedReasons, true); return $isAccessDenied || $this->isSimulatedBackendGroup(); } private function isLoggedIn(): bool { if ($this->context->getPropertyFromAspect('frontend.user', 'isLoggedIn')) { return true; } return $this->isSimulatedBackendGroup(); } protected function isSimulatedBackendGroup(): bool { if (!$this->context->getPropertyFromAspect('backend.user', 'isLoggedIn')) { return false; } // look for special "any group" $groups = $this->context->getPropertyFromAspect('frontend.user', 'groupIds'); return $groups[1] === -2; } private function checkHandlerConfiguration(): void { if ($this->loginRedirectPid === 0) { throw new \RuntimeException('No loginRedirectTarget configured for LoginRedirect errorhandler', 1700813537); } if ($this->statusCode !== 403) { throw new \RuntimeException(sprintf('Invalid HTTP status code %d for LoginRedirect errorhandler', $this->statusCode), 1700813545); } } }
Adapt the constant
PAGE_
to match the page ID from the previous step. Since there is no proper way how to do it otherwise, we put in the page ID of the login form hard-coded into the fileID_ LOGIN_ FORM Redirect
and define a constantLogin Error Handler. php PAGE_
for it. In the example above, this is set toID_ LOGIN_ FORM 656
.Hint
This example code uses PHP 8.1 syntax. Depending on the PHP version you use in your project, you may need to adapt language features like
readonly
to match your used PHP version. -
In your EXT:felogin plugin, make sure you selected "Defined by GET/POST Parameters" as first redirect mode
You need to configure the login form that receives your redirect in a way, that allows to evaluate submitted URL parameters. In
EXT:
, this is achieved via this Redirect Mode (which can also be set through TypoScript configuration, see redirectMode.felogin Your login form will probably also need to define a specific target page for normal logins (independent from the error handler redirect), so you should also add a
redirect
likeMode login
to your list, and set a target page in redirectPageLogin. -
Testing the custom error handler
Clear the caches, for example via the backend module Admin Tools > Maintenance.
Then open any access-restricted page in an incognito browser window to be sure that you are not logged in yet. Here we will use the example URL
https://
.example. org/ restricted/ page When everything is configured correctly and if you are not logged in yet, then you should be redirected to your login page like
https://
(example page IDexample. org/ login 656
).After entering proper frontend user credentials, you should be redirected back to
https://
, the page where you wanted to get to initially.example. org/ restricted/ page Hint
When you have multiple site configurations, be sure to access the correct one. This means where both the login form is located, and the custom error handler is configured for.
Hint
Do not copy the generated link from the address URL after you clicked View webpage from the backend, and then just paste it into the URL bar of the incognito window. The reason is that when being logged in to the backend, a possibly simulated frontend user login can affect your tests.
Hint
Do not get confused when the URL
https://
will be forwarded to a URL likeexample. org/ restricted/ page https://
example. org/ login?return_ url=https%3A%2F%2Fexample. org%3A8443%2Frestricted%2Fpage&c Hash=d0e92f9f9f7b3ca98a2e5e688ad22de9 when you want to access the restricted page in the first place. These are the
getpost
redirect parameters that are evaluated byEXT:
. Now type in the user credentials of the already created frontend user and you should get redirected to the desired pagefelogin https://
.example. org/ restricted/ page
This example was taken from [FEATURE] Introduce ErrorHandler for 403 errors with redirect option which works in TYPO3 v11 and v12, and has been integrated to TYPO3 v13, where it can be used without a custom implementation.