48

In Symfony 2.8/3.0, with our fancy new security components, how do I get the currently logged User (i.e. FOSUser) object in a service without injecting the whole container?

Is it even possible in a non-hacky way?

PS: Let's not consider the "pass it to the service function as a parameter" for being trivially obvious. Also, dirty.

Cœur
  • 37,241
  • 25
  • 195
  • 267
xDaizu
  • 1,051
  • 1
  • 12
  • 29

8 Answers8

70

Inject security.token_storage service into your service, and then use:

$this->token_storage->getToken()->getUser();

as described here: http://symfony.com/doc/current/book/security.html#retrieving-the-user-object and here: http://symfony.com/doc/current/book/service_container.html#referencing-injecting-services

Miro
  • 1,879
  • 14
  • 12
  • 1
    I did this, but when I request the token, I get `null`. Is there something I missed to set? – Mathias Bader Jun 03 '18 at 19:30
  • "**Always Check if the User is Logged In** It's important to check if the user is authenticated first. If they're not, $user will either be null or the string anon.. Wait, what? Yes, this is a quirk. If you're not logged in, the user is technically the string anon., though the getUser() controller shortcut converts this to null for convenience." https://symfony.com/doc/3.4/security.html#always-check-if-the-user-is-logged-in – Kim Sep 23 '18 at 10:20
66

Works with Symfony 3.4, 4.x, 5.x & above. The Security utility class was introduced in Symfony 3.4.

use Symfony\Component\Security\Core\Security;

public function indexAction(Security $security)
{
    $user = $security->getUser();
}

https://symfony.com/doc/3.4/security.html#always-check-if-the-user-is-logged-in

Niket Pathak
  • 6,323
  • 1
  • 39
  • 51
Kim
  • 1,757
  • 1
  • 17
  • 32
  • 1
    Works for Symfony 4 – Darragh Enright Apr 20 '19 at 13:15
  • 2
    I don't fully understand why `Security` is recommended over `TokenStorageInterface` and/or `AuthorizationCheckerInterface` as the class is `final` which makes it impossible to mock which in turn makes unit testing services dependent on these services harder than it needs to be. – Liiva Jun 26 '19 at 14:39
  • I guess you should never change your code just because of how easy you can write (phpunit) tests for it. Having and using final classes makes much sense. You can mock final classes for example using hooks: https://www.tomasvotruba.cz/blog/2019/03/28/how-to-mock-final-classes-in-phpunit/ – Kim Jun 26 '19 at 21:36
  • @Liiva From what I can tell from here it's because the user may not be logged in yet when the service constructor runs. https://symfony.com/doc/current/security.html#b-fetching-the-user-from-a-service – Brett Thomas Apr 27 '20 at 17:16
  • 1
    @Liiva: I've checked Security class source (https://github.com/symfony/security-core/blob/5.2/Security.php) and it only has `@final` in doc and is not marked `final`. Mocking works fine, tested with Symfony 5.2.9. – k00ni May 28 '21 at 08:43
  • 1
    @k00ni You're correct but at the time of writing it was a final class and later moved to the docs as part of this commit: https://github.com/symfony/security-core/commit/a44d4cef38ce2c9b8e43d94e4847ebcb5f0e65ef#diff-9883341373efe75f508748760a2f2b8f2ab98e90db5b49dc6b7063c83ff32406 – Liiva May 28 '21 at 15:03
  • Symfony\Component\Security\Core\Security is depredated since 6.2 use use Symfony\Bundle\SecurityBundle\Security instead – Didier Corbière Feb 21 '23 at 06:50
47

Using constructor dependency injection, you can do it this way:

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

class A
{
    private $user;

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

    public function foo()
    {
        dump($this->user);
    }
}
Mateusz
  • 2,340
  • 25
  • 24
  • 2
    Why do you say it's only for Symfony 3.3+ ? What part of your code doesn't work in older versions of Symfony? – Radu Murzea Nov 06 '17 at 07:50
  • 1
    For some reason, I thought constructor dependency injection wasn't supported earlier, but that's not true. Thank you for your comment. – Mateusz Nov 07 '17 at 23:52
  • When I call `$tokenStorage->getToken()` I receive `null`. What could be the reason for this? The user is logged in ... – Mathias Bader Jun 03 '18 at 19:32
  • 1
    If you get `$tokenStorage->getToken()` equal `null`, try storing the whole `$tokenStorage` as a class property, i.e. in `__construct(...)`: `$this->tokenStorage = $tokenStorage`. – iloo Feb 13 '19 at 10:03
  • Related to the comment of @iloo: It is important **when** you use the token storage. As stated [here](https://symfony.com/doc/current/security.html#b-fetching-the-user-from-a-service), the `auth` process might not be complete, when the service-constructor gets called. Therefore store the reference `$tokenStorage->getToken()` or use `Symfony\Component\Security\Core\Security` instead. Can't tell the difference in behavior between those 2. `Security` seems to be common in Symfony 4+. – k00ni Nov 18 '19 at 10:58
  • +1 for putting the NS import, often overlooked but there are several and it's a pain figuring out which one goes with the answer! – James Apr 06 '20 at 09:08
20

In symfo 4 :

use Symfony\Component\Security\Core\Security;

class ExampleService
{
    private $security;

    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    public function someMethod()
    {
        $user = $this->security->getUser();
    }
}

See doc : https://symfony.com/doc/current/security.html#retrieving-the-user-object

William Rossier
  • 873
  • 9
  • 15
5

From Symfony 3.3, from a Controller only, according this blog post: https://symfony.com/blog/new-in-symfony-3-2-user-value-resolver-for-controllers

It's easy as:

use Symfony\Component\Security\Core\User\UserInterface

public function indexAction(UserInterface $user)
{...}
Thomas Decaux
  • 21,738
  • 2
  • 113
  • 124
3

With Symfony 5.2+ and PHP 8.0+ you can also get the logged user using the #[CurrentUser] attribute

namespace App\Controller;

use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Http\Attribute\CurrentUser;

class FooController extends AbstractController
{
    public function index(#[CurrentUser] ?User $user)
    {
        // ...
    }
}
Fabien Sa
  • 9,135
  • 4
  • 37
  • 44
  • 1
    This is the best practice now. Note that if your controller is secured with the `IsGranted` attribute, you can make the user type not nullable `#[CurrentUser] User $user`. – COil Mar 20 '23 at 13:33
2

Symfony does this in Symfony\Bundle\FrameworkBundle\ControllerControllerTrait

protected function getUser()
{
    if (!$this->container->has('security.token_storage')) {
        throw new \LogicException('The SecurityBundle is not registered in your application.');
    }

    if (null === $token = $this->container->get('security.token_storage')->getToken()) {
        return;
    }

    if (!is_object($user = $token->getUser())) {
        // e.g. anonymous authentication
        return;
    }

    return $user;
}

So if you simply inject and replace security.token_storage, you're good to go.

simPod
  • 11,498
  • 17
  • 86
  • 139
0

if you class extend of Controller

$this->get('security.context')->getToken()->getUser();

Or, if you has access to container element..

$container = $this->configurationPool->getContainer();
$user = $container->get('security.context')->getToken()->getUser();

http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements

Maxi Schvindt
  • 1,432
  • 1
  • 11
  • 20
  • 2
    The class is a service, not a controller (technically, it *can* be both, but it's rare and ugly). Also, I explicitly asked to not inject the controller. Thus, your answer, though *kinda* correct, violates both pre-conditions so it's technically invalid. Thanks, though. – xDaizu Apr 27 '16 at 07:39