I configured a firewall to protect part of my application (/manager). I also setup a login form to authenticate, but i loop on login form.
I'm sure password is the right one, and after some investigation i found that the authentication worked well (i was authenticated) but right after it, when symfony refresh the user, it log me out cause the user is not the same. I found that was caused by password mismatch. The problem is that when i'm logged in by authenticator, User is serialised (with TokenInterface i guess), but the field 'password' is set to null in TokenInterface (not in database). I'm using JMSSerialiserBundle in my application, i don't know if the problem comes to it. Can you help me ?
My user class
/**
* @ORM\Entity(repositoryClass="App\Repository\UtilisateurRepository")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({"eleve" = "Eleve", "gestionnaire" = "Gestionnaire"})
*/
abstract class Utilisateur implements UserInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Serializer\Groups({"id"})
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Groups({"Utilisateur"})
*/
private $nom;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Groups({"Utilisateur"})
*/
private $prenom;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Groups({"Utilisateur"})
*/
private $username;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Exclude()
*/
private $password;
/**
* @ORM\Column(type="string", length=1024, nullable=true)
* @Serializer\Groups({"Login"})
*/
private $token;
My Authenticator class
class ManagerAuthenticator 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 'app_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 = $userProvider->loadUserByUsername($credentials['username']);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Username could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password'], $user->getSalt());
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
//On redirige sur la page précédente sauf si on vient du login
$targetPath = $this->getTargetPath($request->getSession(), $providerKey);
$loginRoute = $this->urlGenerator->generate('app_login');
if ($targetPath && strpos($loginRoute, $targetPath) === false) {
return new RedirectResponse($targetPath);
}
//Rediriger vers l'index
return new RedirectResponse($this->urlGenerator->generate('accueil_index'));
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate('app_login');
}
}
Even without @Serializer\Exclude() it doesn't work. I don't know if i'm supposed to override some function or implement interface.
Check Symfony\Component\Security\Core\Authentication\Token\AbstractToken::hasUserChanged()
My advice create new method on your user class with name isEqualTo and your class should implement EquatableInterface.
/**
* @param UserInterface $user
* @return bool
*/
public function isEqualTo(UserInterface $user): bool
{
return $this->username === $user->getUsername() && $this->id === $user->getId();
}
Also you may need to add this two methods.
/**
* @see \Serializable::serialize()
*/
public function serialize(): string
{
return serialize([$this->id, $this->username,]);
}
/**
* @see \Serializable::unserialize()
* @param string $serialized
* @return User
*/
public function unserialize($serialized): self
{
[$this->id, $this->username] = unserialize($serialized);
return $this;
}