How to Customize Access Denied Responses ======================================== In Symfony, you can throw an :class:`Symfony\\Component\\Security\\Core\\Exception\\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. .. _security-entry-point: Customize the Unauthorized Response ----------------------------------- You need to create a class that implements :class:`Symfony\\Component\\Security\\Http\\EntryPoint\\AuthenticationEntryPointInterface`. This interface has one method (``start()``) that is called whenever an unauthenticated user tries to access a protected resource:: // src/Security/AuthenticationEntryPoint.php namespace App\Security; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; class AuthenticationEntryPoint implements AuthenticationEntryPointInterface { public function __construct( private UrlGeneratorInterface $urlGenerator, ) { } public function start(Request $request, ?AuthenticationException $authException = null): RedirectResponse { // add a custom flash message and redirect to the login page $request->getSession()->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 :ref:`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: .. configuration-block:: .. code-block:: yaml # config/packages/security.yaml firewalls: # ... main: # ... entry_point: App\Security\AuthenticationEntryPoint .. code-block:: xml .. code-block:: php // config/packages/security.php use App\Security\AuthenticationEntryPoint; use Symfony\Config\SecurityConfig; return static function (SecurityConfig $security): void { $security->firewall('main') // .... ->entryPoint(AuthenticationEntryPoint::class) ; }; Customize the Forbidden Response -------------------------------- Create a class that implements :class:`Symfony\\Component\\Security\\Http\\Authorization\\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):: // 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 :ref:`default services.yaml configuration `, you're done! Symfony will automatically know about your new service. You can then configure it under your firewall: .. configuration-block:: .. code-block:: yaml # config/packages/security.yaml firewalls: # ... main: # ... access_denied_handler: App\Security\AccessDeniedHandler .. code-block:: xml .. code-block:: php // config/packages/security.php use App\Security\AccessDeniedHandler; use Symfony\Config\SecurityConfig; return static function (SecurityConfig $security): void { $security->firewall('main') // .... ->accessDeniedHandler(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 :ref:`kernel.exception listener `:: // 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(); } }