How to Customize Access Denied Responses
Warning: You are browsing the documentation for Symfony 4.x, which is no longer maintained.
Read the updated version of this page for Symfony 7.1 (the current stable version).
In Symfony, you can throw an AccessDeniedException to disallow access to the user. Symfony will handle this exception and generates a response based on the authentication state:
- If the user is not authenticated (or authenticated anonymously), an authentication entry point is used to generate a response (typically a redirect to the login page or an 401 Unauthorized response);
- If the user is authenticated, but does not have the required permissions, a 403 Forbidden response is generated.
Customize the Unauthorized Response
You need to create a class that implements
AuthenticationEntryPointInterface.
This interface has one method (start()
) that is called whenever an
unauthenticated user tries to access a protected resource:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// src/Security/AuthenticationEntryPoint.php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class AuthenticationEntryPoint implements AuthenticationEntryPointInterface
{
private $urlGenerator;
private $session;
public function __construct(UrlGeneratorInterface $urlGenerator, SessionInterface $session)
{
$this->urlGenerator = $urlGenerator;
$this->session = $session;
}
public function start(Request $request, AuthenticationException $authException = null): RedirectResponse
{
// add a custom flash message and redirect to the login page
$this->session->getFlashBag()->add('note', 'You have to login in order to access this page.');
return new RedirectResponse($this->urlGenerator->generate('security_login'));
}
}
That's it if you're using the default services.yaml configuration. Otherwise, you have to register this service in the container.
Now, configure this service ID as the entry point for the firewall:
1 2 3 4 5 6 7
# config/packages/security.yaml
firewalls:
# ...
main:
# ...
entry_point: App\Security\AuthenticationEntryPoint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!-- config/packages/security.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<firewall name="main"
entry-point="App\Security\AuthenticationEntryPoint"
>
<!-- ... -->
</firewall>
</config>
</srv:container>
1 2 3 4 5 6 7 8 9 10 11
// config/packages/security.php
use App\Security\AuthenticationEntryPoint;
$container->loadFromExtension('security', [
'firewalls' => [
'main' => [
// ...
'entry_point' => AuthenticationEntryPoint::class,
],
],
]);
Customize the Forbidden Response
Create a class that implements
AccessDeniedHandlerInterface.
This interface defines one method called handle()
where you can
implement whatever logic that should execute when access is denied for the
current user (e.g. send a mail, log a message, or generally return a custom
response):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// src/Security/AccessDeniedHandler.php
namespace App\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
class AccessDeniedHandler implements AccessDeniedHandlerInterface
{
public function handle(Request $request, AccessDeniedException $accessDeniedException): ?Response
{
// ...
return new Response($content, 403);
}
}
If you're using the default services.yaml configuration, you're done! Symfony will automatically know about your new service. You can then configure it under your firewall:
1 2 3 4 5 6 7
# config/packages/security.yaml
firewalls:
# ...
main:
# ...
access_denied_handler: App\Security\AccessDeniedHandler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!-- config/packages/security.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<firewall name="main"
access-denied-handler="App\Security\AccessDeniedHandler"
>
<!-- ... -->
</firewall>
</config>
</srv:container>
1 2 3 4 5 6 7 8 9 10 11
// config/packages/security.php
use App\Security\AccessDeniedHandler;
$container->loadFromExtension('security', [
'firewalls' => [
'main' => [
// ...
'access_denied_handler' => AccessDeniedHandler::class,
],
],
]);
Customizing All Access Denied Responses
In some cases, you might want to customize both responses or do a specific
action (e.g. logging) for each AccessDeniedException
. In this case,
configure a kernel.exception listener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// src/EventListener/AccessDeniedListener.php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class AccessDeniedListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
// the priority must be greater than the Security HTTP
// ExceptionListener, to make sure it's called before
// the default exception listener
KernelEvents::EXCEPTION => ['onKernelException', 2],
];
}
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
if (!$exception instanceof AccessDeniedException) {
return;
}
// ... perform some action (e.g. logging)
// optionally set the custom response
$event->setResponse(new Response(null, 403));
// or stop propagation (prevents the next exception listeners from being called)
//$event->stopPropagation();
}
}