tftd
tftd

Reputation: 17032

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?

Upvotes: 6

Views: 11416

Answers (2)

tftd
tftd

Reputation: 17032

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.

Upvotes: 6

lxg
lxg

Reputation: 13107

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.

Upvotes: 7

Related Questions