How can one force logout a user in Symfony?
Asked Answered
T

2

6

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?

Tilt answered 17/9, 2014 at 23:30 Comment(0)
I
7

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.

Incardinate answered 17/9, 2014 at 23:43 Comment(3)
symfony 3.4: $this->get('security.token_storage')->setToken(null); $this->get('session')->invalidate();Mote
In Symfony 3.4 you should not inject the container, but use auto-wiring instead.Incardinate
Could explain what invalidate() function gonna do behind the sceneTrigraph
T
6

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.

Tilt answered 18/9, 2014 at 0:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.