Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can one force logout a user in Symfony?

I have a User entity, which has a boolean column isActivated. Depending on the value of the column for each user, he may or may not be able to login (i.e. he hasn't activated his account so no login). I've achieved that by assigning an simple_form.authenticator in the firewall which check upon every login.

I'm trying to figure out how force logout a user while he's still loged in.
Consider the following scenario:

  1. The user logs in while his account is still active.
  2. An administrator deactivates the user's account.
  3. The user is logged out due to the fact it's not active anymore.

Unfortunately step #3 doesn't happen. The reason may lay in the fact that the user has already received the token and is considered to be "tursted" by the Symfony 2.5's firewall (probably the token is cached in the security context?).

I'm wondering what would be the best way to overcome this issue? Should I write a kernel event listener or perhaps a Custom User Provider?

like image 300
tftd Avatar asked Dec 12 '22 03:12

tftd


2 Answers

You can terminate the user's session with the following two lines (if you have access to the container, otherwise you must inject security.context and session):

$container->get('security.context')->setToken(null);
$container->get('session')->invalidate();

After that, the user should be logged out.

If you have loaded the user entity before, you may want to unset that, too.

like image 169
lxg Avatar answered Dec 17 '22 13:12

lxg


Although @lxg answered my question, I decided to extend his answer so that other people with the same issue have a better idea of how to fix that issue.

Create the event listener

namespace Acme\MyBundle\Events;

use Acme\MyBundle\Entity\User;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
use Symfony\Component\Security\Core\SecurityContext;

class RequestEvent {

    /**
     * @var \Symfony\Component\Security\Core\SecurityContext
     */
    private $securityContext;

    public function __construct(SecurityContext $context){
        $this->securityContext = $context;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        // not sure if this is actually needed?
        if (!$event->isMasterRequest()) {
            // don't do anything if it's not the master request
            return;
        }

        // try to get security context and catch the exception in case no firewall was configured (i.e. for the dev tool bar)
        try{
            // trigger only for logged in users
            if($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')){
                $token = $this->securityContext->getToken();
                /**
                 * @var User $user
                 */
                $user = $token->getUser();
                if($user != null && !$user->isActive()){
                    $this->securityContext->setToken(null);
                }
            }
        } catch(AuthenticationCredentialsNotFoundException $e){
            // don't do anything here... or do whatever you want.
        }
    }
}

?>

Now in your service.yml add this:

services:
    kernel.listener.request_listener:
        class: Acme\MyBundle\Events\RequestEvent
        arguments: [ @security.context ]
        tags:
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

And once the user has been deactivated, he'll be force-redirected to the login page of your firewall. Hope this helps somebody.

like image 23
tftd Avatar answered Dec 17 '22 11:12

tftd