Hi Bastian,
thanks for your suggestion. In the meantime I came up with a solution using the beforeControllerInvocation signal. Since I have several pages unter /members-area/ and the login in page is configurable by backend users, I think this is the better fit for my case.
Since others might have the same problem, here is what I did:
Using the signal in the Package class:
class Package extends BasePackage
{
public function boot(Bootstrap $bootstrap)
{
$dispatcher = $bootstrap->getSignalSlotDispatcher();
$dispatcher->connect(Dispatcher::class, 'beforeControllerInvocation', AuthenticationListener::class, 'handleLoginRedirect');
}
}
The AuthenticationListener:
<?php
namespace My\Site\Namespace\Security;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Response;
use Neos\Flow\Log\SystemLoggerInterface;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Controller\ControllerInterface;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Flow\Mvc\RequestInterface;
use Neos\Flow\Mvc\ResponseInterface;
use Neos\Flow\Security\Context;
/**
* This listener redirects to the login page, if the user is not authenticated and tries to access a protected URI.
*/
class AuthenticationListener
{
/**
* @Flow\Inject()
* @var SystemLoggerInterface
*/
protected $logger;
/**
* @Flow\Inject()
* @var Context
*/
protected $securityContext;
/**
* @Flow\InjectConfiguration("authentication.protectedUris")
* @var array
*/
protected $protectedUris = [];
/**
* @Flow\InjectConfiguration("authentication.loginUri")
* @var string
*/
protected $loginUri = null;
public function handleLoginRedirect(RequestInterface $request, ResponseInterface $response, ControllerInterface $controller)
{
if (!$response instanceof Response || !$request instanceof ActionRequest) {
return;
}
if (!$this->securityContext->canBeInitialized()) {
return;
}
if ($this->loginUri === null) {
$this->logger->log("[AuthenticationListener] loginUri not configured. Please configure it using setting [My.Site.Namespace.authentication.loginUri]", LOG_WARNING);
return;
}
if (!is_array($this->protectedUris)) {
$this->logger->log("[AuthenticationListener] protectedUris not configured. Please configure it using setting [My.Site.Namespace.authentication.protectedUris]", LOG_WARNING);
return;
}
$path = $request->getHttpRequest()->getUri()->getPath();
$isSecuredArea = false;
foreach($this->protectedUris as $pattern) {
if (preg_match($pattern, $path)) {
$isSecuredArea = true;
break;
}
}
if (!$isSecuredArea) {
return;
}
$account = $this->securityContext->getAccount();
if ($account) {
$this->logger->log("[AuthenticationListener] We are authenticated with account [{$account->getAccountIdentifier()}]");
} else {
$this->logger->log("[AuthenticationListener] Not authenticated, redirecting to login page at [{$this->loginUri}]");
$loginUri = $this->loginUri.'?redirectAfterLogin='.urlencode($path);
$escapedUri = htmlentities($loginUri, ENT_QUOTES, 'utf-8');
$response->setContent('<html><head><meta http-equiv="refresh" content="' . intval(0) . ';url=' . $escapedUri . '"/></head></html>');
$response->setStatus(302);
$response->setHeader('Location', $loginUri);
$response->setHeader('X-Redirect-Reason', 'NotAuthenticated');
$request->setDispatched(true);
throw new StopActionException();
}
}
}
The AuthenticationController of the plugin uses the GET parameter redirectAfterLogin to redirect to the correct URI after logging in successfully.
The downside is that you cannot configure the protected URIs in the backend. This is done in the Settings.yaml:
My:
Site:
Namespace:
authentication:
loginUri: '/login'
protectedUris:
- '@^/members-area/?@'
- '@^/some-other-protected-page/?@'
I would like your opinion on this solution though!
Regards
Leif