How to get the current logged User in a service [duplicate]
Asked Answered
S

8

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.

Scene answered 26/4, 2016 at 16:8 Comment(0)
I
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

Idalia answered 26/4, 2016 at 17:12 Comment(2)
I did this, but when I request the token, I get null. Is there something I missed to set?Welltodo
"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." symfony.com/doc/3.4/…Geotaxis
G
69

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

Geotaxis answered 23/9, 2018 at 10:16 Comment(7)
Works for Symfony 4Heap
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.Conductor
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: tomasvotruba.cz/blog/2019/03/28/…Geotaxis
@Conductor From what I can tell from here it's because the user may not be logged in yet when the service constructor runs. symfony.com/doc/current/…Exaltation
@Liiva: I've checked Security class source (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.Projector
@Projector 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: github.com/symfony/security-core/commit/…Conductor
Symfony\Component\Security\Core\Security is depredated since 6.2 use use Symfony\Bundle\SecurityBundle\Security insteadShorts
F
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);
    }
}
Frierson answered 25/9, 2017 at 20:47 Comment(6)
Why do you say it's only for Symfony 3.3+ ? What part of your code doesn't work in older versions of Symfony?Beachhead
For some reason, I thought constructor dependency injection wasn't supported earlier, but that's not true. Thank you for your comment.Frierson
When I call $tokenStorage->getToken() I receive null. What could be the reason for this? The user is logged in ...Welltodo
If you get $tokenStorage->getToken() equal null, try storing the whole $tokenStorage as a class property, i.e. in __construct(...): $this->tokenStorage = $tokenStorage.Loper
Related to the comment of @iloo: It is important when you use the token storage. As stated here, 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+.Projector
+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!Frigging
Y
25

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

Your answered 29/3, 2019 at 12:31 Comment(1)
Good answer, works for me on Symfony 6Omnirange
B
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)
{...}
Bilateral answered 30/12, 2017 at 21:14 Comment(0)
B
4

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)
    {
        // ...
    }
}
Betteann answered 4/1, 2022 at 23:12 Comment(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.Interviewer
E
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.

Ether answered 19/10, 2017 at 23:42 Comment(2)
replace security.token_storage by what?Felten
I'd recommend this answer now https://mcmap.net/q/352006/-how-to-get-the-current-logged-user-in-a-service-duplicateEther
U
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

Ulane answered 26/4, 2016 at 17:57 Comment(1)
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.Scene

© 2022 - 2024 — McMap. All rights reserved.