I´m working in a symfony project. It almost ready, the authentication process flow fine... but after an user logout he can back to the system by pressing the back button in the browser.
The user cant use the system, if he click in some link o refresh the page the login page appear. But he can see the information that last page of the application.
It is a way to prevent that behaivour with symfony? BR...
security.yaml
security:
encoders:
App\Entity\Usuario:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\Usuario
property: usuario
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: app_user_provider
form_login:
login_path: sac_login
check_path: sac_login
csrf_token_generator: security.csrf.token_manager
default_target_path: sac_login
always_use_default_target_path: true
logout:
path: sac_logout
guard:
authenticators:
- App\Security\LoginFormAuthenticator
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/sala, roles: ROLE_SALA }
- { path: ^/estacionario, roles: ROLE_ESTACIONARIO }
- { path: ^/registro, roles: ROLE_REGISTRO }
- { path: ^/direccion, roles: ROLE_DIRECTIVO }
- { path: ^/transcripcion, roles: ROLE_TRANSCRIPCION }
- { path: ^/reprografia, roles: ROLE_REPROGRAFIA }
PD1: after the Bossman's answer the the user can logout and click on the browser back button, the log in page stay, but if another user with diferent role make login an error 403 appear because the app redirect to the url that we suposed to avoid.
PD2: the app have no unsecured area, all users need to login to access their zone of work. I accomplish that by saving in the user table the route of that zone an load it in the moment when the symfony authentication system arrive to the onAuthenticationSuccess using the following code:
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
$user = $this->entityManager->getRepository(Usuario::class)->findOneBy(['usuario' => $request->request->get('_username')]);
$ruta = $user->getRuta();
return new RedirectResponse($this->urlGenerator->generate($ruta));
}
CodePudding user response:
This is normal behaviour by browsers, but if you really need to control this, then you can achieve this with an event listener and setting the response headers so those pages won't be cached by the browser.
This listener will only set the headers depending on the controller, so you can set it for only admin pages, like App\Controller\AdminController
for example...
Place file in src/EventListener/ResponseHeaderListener.php
.
// src/EventListener/ResponseHeaderListener.php
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use App\Controller\AdminController;
class ResponseHeaderListener implements EventSubscriberInterface
{
private $controller;
public static function getSubscribedEvents()
{
return array(
'kernel.controller' => 'onKernelController',
'kernel.response' => 'onKernelResponse'
);
}
public function onKernelController(ControllerEvent $event)
{
$this->controller = $event->getController();
}
public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMasterRequest() || !is_array($this->controller)) {
return;
}
if ($this->controller[0] instanceof AdminController) {
$response = $event->getResponse();
// Set response headers
$response->headers->add(array(
'Cache-Control' => 'nocache, no-store, max-age=0, must-revalidate',
'Pragma' => 'no-cache'
));
}
}
}
Tweak and modify to your needs.
CodePudding user response:
To accomplish what I want, first I need to create de ResponseHeaderListener class according to Bossman's answer but with an small change because my system has no unsecured area, all the controllers need to pass by the listener:
<?php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
class ResponseHeaderListener implements EventSubscriberInterface
{
private $controller;
public static function getSubscribedEvents()
{
return array(
'kernel.controller' => 'onKernelController',
'kernel.response' => 'onKernelResponse'
);
}
public function onKernelController(ControllerEvent $event)
{
$this->controller = $event->getController();
}
public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMasterRequest() || !is_array($this->controller)) {
return;
}
$response = $event->getResponse();
$response->headers->add(array(
'Cache-Control' => 'nocache, no-store, max-age=0, must-revalidate',
'Pragma' => 'no-cache'
));
}
}
Second, update the security.yaml file with the following keys under the main firewall:
form_login:
login_path: sac_login
check_path: sac_login
always_use_default_target_path: true
where sac_login is the symfony route to the login page.
Third and final step, comment or delete the following code:
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
in the onAuthenticationSuccess function that belongs to the loginFormAuthenticator class.
Hope this answer can help others with similiar doubt. Really I read about this problem, not only in symfony, and for days I struggle with a solution. Thanks to @Bossman for all the patience and of course, his help!!!