Symfony2: Check user authentication based on path

in Symfony2, is it possible to check if user is authenticated to access the URl he requested.
What I want to do is, i dont want to allow a logged in user to go back to registration or login or recover password pages.

here is my security.yml:

    access_control:
    - { path: ^/signup/, roles: IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICATED_FULLY}
    - { path: ^/register/, roles: IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICATED_FULLY}
    - { path: ^/recover/, roles: IS_AUTHENTICATED_ANONYMOUSLY && !IS_AUTHENTICATED_FULLY}

but this is showing, access denied page to current user. So i think it would be nice if I can redirect the user to home page on such request, by checking if he is not allowed. Can I check by providing path that user is authenticated or not in listener?

    public function onKernelResponse(FilterResponseEvent $event)
    {
     $request = $event->getRequest();
     $path = $request->getPathInfo();

     if($this->container->get('security.context')->getToken() != null) {
       // To check if user is authenticated or anonymous
       if( ($this->container->get('security.context')->getToken() instanceof UsernamePasswordToken) &&
        ($this->container->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY') == true) ) {
         // HOW TO CHECK PATH ?
        // set response to redirect to home page
      }
    }
  }

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

The security.access_map service

The configuration of security.access_control is processed by …

SecurityBundle\DependencyInjection\SecurityExtension

… which creates RequestMatchers for the routes (path,hosts,ip,…) and then invokes the service’s add() method with the matcher, the allowed roles and the channel (i.e. https ).

The service is usually used by i.e. the AccessListener.

You can use the security.access_map service to access the
security.access_control parameters in your application.

The class used for the security.access_map service is defined by the parameter security.access_map.class and defaults to

Symfony\Component\Security\Http\AccessMap ( implements
AccessMapInterface )

You can use the parameter security.access_map.class to override the service with a custom class (must implement AccessMapInterface):

# i.e. app/config/config.yml

parameters:
    security.access_map.class: My\Custom\AccessMap

How to access the service

The security.access_map service is a private service as you can see by it’s definition here.

This means you can’t request it from the container directly like this:

$this->container->get('security.access_map')

You will have to inject it into another service (i.e. a listener service) explicitly to be able to access it.


A listener example

services:
    my_listener:
        class: My\Bundle\MyListenerBundle\EventListener\ForbiddenRouteListener
        arguments: [ @security.access_map ]
        tags:      
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

Then you can call the getPatterns() method to obtain the RequestMatchers, allowed roles and required channel from there.

namespace My\Bundle\MyListenerBundle\EventListener;

use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;

class ForbiddenRouteListener
{

    protected $accessMap;

    public function __construct(AccessMapInterface $access_map)
    {
        $this->accessMap = $access_map;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        $request = $event->getRequest();
        $patterns = $this->accessMap->getPatterns($request);

        // ...

Solution 2

Maybe this will help someone. I just catch route name and check if they are in array. If yes just redirect. This is event listener.

services.yml

project.loggedin_listener:
      class: Project\FrontBundle\EventListener\LoggedInListener
      arguments: [ "@router", "@service_container" ]
      tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

listener:

namespace Project\FrontBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;

class LoggedInListener {
    private $router;
    private $container;

    public function __construct($router, $container)
    {
        $this->router = $router;
        $this->container = $container;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        $container = $this->container;
        $accountRouteName = "_homepage";

        if( $container->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY') ){

            $routeName = $container->get('request')->get('_route');
            $routes = array("admin_login","fos_user_security_login","fos_user_registration_register","fos_user_resetting_request");
            if(in_array($routeName, $routes)){
                $url = $this->router->generate($accountRouteName);
                $event->setResponse(new RedirectResponse($url));
            }

        }

    }
}

Solution 3

You can do not only inside security.yml options, but also via controller, like this:

if($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
      return $this->redirect($this->generateUrl('homepage'));
}

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply