All Articles

[Symfony]: Empty token storage when injecting service in listener

Another Symfony mistery

You might have been struggling fetching the logged in user from a listener that includes a UserService class which contains the logic of fetching the logged in user.

Let’s take the below code as an example:

//UserService.php

<?php

namespace App\Service;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;


class UserService {

  private $tokenStorage;

  private $currentUser;

  /**
  * @required
  */
  public function setSecurityContext(TokenStorageInterface $tokenStorage) {
    $token = $tokenStorage->getToken();
    if ($token) {
      $this->currentUser = $token->getUser();
    }
  }

  public function getCurrentUser() {
    return $this->currentUser;
  }
}

?>
//MyListner

<?php
namespace App\Listener;
use Doctrine\Persistence\Event\LifecycleEventArgs;

class MyListener {

  private $userService;

  public function __construct(UserService $userService) {
    $this->userService = $userService;
  }

  public function onFlush(LifecycleEventArgs $args) {

    $currentUser = $this->userService->getCurrentUser();

    var_dump($currentUser); // prints NULL
  }

}
?>

Why the currentUser is NULL ?

The answer: Doctrine listeners are called whenever corresponding event occurs. Initialization of them may occur before security context.

You have two options here:

The first one : inject yout TokenStorage directly in your Listener and retrieve the token inside your event handler.

//MyListner

<?php
namespace App\Listener;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;


class MyListener {

  private $tokenStorage;

  public function __construct(TokenStorageInterface $tokenStorage) {
    $this->tokenStorage = $tokenStorage;
  }

  public function onFlush(LifecycleEventArgs $args) {

    $currentUser = $this->tokenStorage->getToken()->getUser();

    var_dump($currentUser); // prints a UserInterface object
  }

}
?>

The second one: Fetch the token inside the getCurrentUser method.

//UserService.php

<?php

namespace App\Service;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class UserService {

  private $tokenStorage;

  private $currentUser;

  public function __construct(TokenStorageInterface $tokenStorage) {
    $this->tokenStorage = $tokenStorage;
  }


  public function getCurrentUser(TokenStorageInterface $tokenStorage) {
    $token = $tokenStorage->getToken();
    if ($token && is_null($this->currentUser)) {
      $this->currentUser = $token->getUser();
    }

    return $this->currentUser;
  }

}

?>

The hidden trick is that you have to call the getToken method in order to get a fresh token.

That was it, i hope this blog post was helpful.

Cheers