Working with Symfony 4, and making an User environment (using SymfonyCast tutorials) I wrote a LoginFormAuthenticator :
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request)
{
return 'login' === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'username' => $request->request->get('username'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['username']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User::class)->findOneBy(['username' => $credentials['username']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('L\'username ne peut pas être trouvé.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate('core_home'));
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate('login');
}
}
`
But the issue is in the csrf protection. (after many searches, reducing the area of the issue) : I can't connect with my username, a loop give me always the form, with last username...
From the log :
[2019-11-16 15:27:17] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\Security\LoginFormAuthenticator"} [] [2019-11-16 15:27:17] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"App\Security\LoginFormAuthenticator"} []
Trying to better understand, I put a dd() in the getUser function :
$token = new CsrfToken('authenticate', $credentials['csrf_token']); dd($token);
and the result is :
LoginFormAuthenticator.php on line 63: CsrfToken^ {#345 ▼ -id: "authenticate" -value: "A11CgM6FtyduixvNmOotNkjqbakFCfYPD-TSXv8_PGE" }
We have a Csrf token. Searching more, I put in CsrfTokenManager (from Symfony/Security/Csrf) two dump() an a dd() in the istokenValid function :
public function isTokenValid(CsrfToken $token)
{
dump($token);
$namespacedId = $this->getNamespace().$token->getId();
dump($namespacedId);
if (!$this->storage->hasToken($namespacedId)) {
dd(!$this->storage->hasToken($namespacedId));
return false;
}
return hash_equals($this->storage->getToken($namespacedId), $token->getValue());
}
and the results are :
CsrfTokenManager.php on line 109:
CsrfToken^ {#345 ▼ -id: "authenticate" -value: "YPZIzpw_8eU5Uy3YvV6PwySz4qz8FIz0EzkIwkBd-OQ" }
CsrfTokenManager.php on line 111:
"authenticate"
CsrfTokenManager.php on line 113:
true
So, if I understand well, we have a cookie, and the function return 'this cookie is not valid'...
What's wrong ?