In Symfony 4.4 I am attempting simulate authenticating a user so that I can write PHPUnit tests for a secured area of my application. I am using the ZenstruckFoundry to create a factory for my User that will be authenticated.
I have also followed the Symfony docs for Creating an Authentication Token and originally opened an issue in the zenstruck/foundry
repo. Since this doesn't actually seem to be an issue with Foundry I'm asking here on SO.
My test to an endpoint behind my main
firewall simply looks like this:
/**
* @test
*/
public function it_displays_the_dashboard()
{
$user = UserFactory::new()->createOne();
$this->auth($user);
$this->appClient->request('GET', '/'); // <- this requires an auth'd user
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('[data-test="user-profile"]', $user->getUsername());
}
/**
* Simulate authentication
*
* @param Proxy $user
*/
protected function auth(Proxy $user)
{
$session = self::$container->get('session');
$firewallName = 'main';
// **********************************************
// **********************************************
// $user = $user->object(); <-- If I uncomment to use the actual User model then auth doesn't work
// **********************************************
// **********************************************
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->appClient->getCookieJar()->set($cookie);
}
The issue I'm encountering is this: If I pass the Proxy
user to UsernamePasswordToken
then the token is created successfully and the user is authenticated. However, this causes Twig to not be able to use its magic methods to resolve getter attributes. For example, my Twig template has {{ user.username }}
, Behind the scenes Twig resolves this to the getUsername()
method on my User
instance. But since the Proxy
class doesn't have such a method, I get this error:
Error: Call to undefined method App\Model\User::username()
If I modify my template to be {{ user.getUsername }}
then my test passes.
But, if I pass the User
class directly to UsernamePasswordToken
using $user->object()
then the token is initially created, but then seemingly doesn't exist by the time authentication takes place and my test fails because I'm not authenticated.
If I look at the stacktrace I can see that the User is no longer the User that was used when creating the token and I see the following:
/Users/myuser/code/adminapp/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:84:
class Symfony\Component\Security\Core\Authentication\Token\AnonymousToken#847 (6) {
private $secret =>
string(7) "OxIzLMV"
private $user =>
string(5) "anon."
private $roles =>
array(0) {
}
private $roleNames =>
array(0) {
}
private $authenticated =>
bool(true)
private $attributes =>
array(0) {
}
}
I would like to be able to pass the actual User
class using $user->object()
so that I don't have to manipulate my Twig templates by using the getters directly.