symfonyfosuserbundlesymfony3

Symfony FOS UserBundle - prevent authenticated users access login, register, reset


I am using Symfony with the FOS UserBundle, I would like to prevent authenticated users from accessing the login, register or password resetting pages. Any attempt to access those pages should result in a redirect to the homepage.

I've read that you can copy the controller to make those changes, but that means manually updating that copied code when there are updates and applying those changes again, not ideal.

There is also the possibility of using an event subscriber. Here is my implementation which works for preventing the registration form, however I can still access the resetting page and login page.

use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Bundle\FrameworkBundle\Routing\Router;

class FOSUserSubscriber implements EventSubscriberInterface
{

    /**
     * @var Router
     */
    protected $router;

    /**
     * @var TokenStorage
     */
    private $tokenStorage;

    public function __construct(Router $router, TokenStorage $tokenStorage)
    {
        $this->router = $router;
        $this->tokenStorage = $tokenStorage;
    }

    public static function getSubscribedEvents()
    {
        return array(
            FOSUserEvents::REGISTRATION_INITIALIZE => 'forwardToRouteIfUser',
            FOSUserEvents::RESETTING_RESET_REQUEST => 'forwardToRouteIfUser',
            FOSUserEvents::RESETTING_RESET_INITIALIZE => 'forwardToRouteIfUser', //['forwardToRouteIfUser',-100]
            FOSUserEvents::RESETTING_RESET_SUCCESS => 'forwardToRouteIfUser',
            FOSUserEvents::RESETTING_RESET_COMPLETED => 'forwardToRouteIfUser',
        );
    }

    public function forwardToRouteIfUser(GetResponseUserEvent $event)
    {
        if (!$this->tokenStorage->getToken()->isAuthenticated()) {
            return;
        }

        $url = $this->router->generate('home');

        $response = new RedirectResponse($url);

        $event->setResponse($response);
    }

}

Service.yml

Foo\BarBundle\EventListeners\FOSUserSubscriber:
    arguments: ['@router','@security.token_storage']
    tags:
        - { name: kernel.event_subscriber }

If using a Controller really is the only way to do it, please provide an example that doesn't involve copying large chunks of code.

Versions:

"friendsofsymfony/user-bundle": "^2.0",
"symfony/symfony": "3.4.*",

Solution

  • You can actually override FOSUserBundle controllers without having to manually update them when there is a new version of FOSUserBundle, by using return parent::loginAction($request);

    Here is an example with the SecurityController, to prevent already logged-in users to access the login page:

    // src/UserBundle/Controller/SecurityController.php
    
    namespace UserBundle\Controller;
    
    use FOS\UserBundle\Controller\SecurityController as BaseController;
    use Symfony\Component\HttpFoundation\RedirectResponse;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    
    /**
     * Controller managing the login (extends FOSUserBundle SecurityController)
     */
    class SecurityController extends BaseController
    {
        /**
         * Customize the login action, to redirect already logged in users
         * to the homepage
         * 
         * @param Request $request
         * @return Response
         */
        public function loginAction(Request $request)
        {
            /* If the user is already logged in, redirect him to the homepage */
            if ($this->get(Services::SECURITY_AUTHORIZATION_CHECKER)->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
    
                /* Redirect the user to the homepage */
                return new RedirectResponse($this->generateUrl('homepage'));
            }
    
            /* Call the parent method */
            return parent::loginAction($request);
        }
    }
    

    You can apply the same to the RegistrationController and ResettingController.