diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 82916c9..ae530c4 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -22,9 +22,8 @@ security: main: lazy: true provider: app_user_provider - guard: - authenticators: - - App\Security\AppAuthenticator + custom_authenticators: + - App\Security\AppAuthenticator logout: path: app_logout # where to redirect after logout diff --git a/src/Entity/User.php b/src/Entity/User.php index 7207c3e..b31daf0 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -7,6 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; /** @@ -14,7 +15,7 @@ use Symfony\Component\Security\Core\User\UserInterface; * @ORM\Table(name="`user`") * @UniqueEntity(fields={"email"}, message="There is already an account with this email") */ -class User implements UserInterface +class User implements UserInterface, PasswordAuthenticatedUserInterface { /** * @ORM\Id @@ -92,6 +93,14 @@ class User implements UserInterface * * @see UserInterface */ + public function getUserIdentifier(): string + { + return (string) $this->email; + } + + /** + * @deprecated since Symfony 5.3, use getUserIdentifier instead + */ public function getUsername(): string { return (string) $this->username; @@ -117,11 +126,11 @@ class User implements UserInterface } /** - * @see UserInterface + * @see PasswordAuthenticatedUserInterface */ public function getPassword(): string { - return (string) $this->password; + return $this->password; } public function setPassword(string $password): self diff --git a/src/Security/AppAuthenticator.php b/src/Security/AppAuthenticator.php index 3ca8d6f..158686b 100644 --- a/src/Security/AppAuthenticator.php +++ b/src/Security/AppAuthenticator.php @@ -2,105 +2,59 @@ namespace App\Security; -use App\Entity\User; -use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; -use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; -use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException; use Symfony\Component\Security\Core\Security; -use Symfony\Component\Security\Core\User\UserInterface; -use Symfony\Component\Security\Core\User\UserProviderInterface; -use Symfony\Component\Security\Csrf\CsrfToken; -use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; -use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; -use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface; +use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; +use Symfony\Component\Security\Http\Authenticator\Passport\Passport; +use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; use Symfony\Component\Security\Http\Util\TargetPathTrait; -class AppAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface +class AppAuthenticator extends AbstractLoginFormAuthenticator { use TargetPathTrait; public const LOGIN_ROUTE = 'app_login'; - private $entityManager; - private $urlGenerator; - private $csrfTokenManager; - private $passwordHasher; + private UrlGeneratorInterface $urlGenerator; - public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordHasherInterface $passwordHasher) + public function __construct(UrlGeneratorInterface $urlGenerator) { - $this->entityManager = $entityManager; $this->urlGenerator = $urlGenerator; - $this->csrfTokenManager = $csrfTokenManager; - $this->passwordHasher = $passwordHasher; } - public function supports(Request $request) + public function authenticate(Request $request): PassportInterface { - return self::LOGIN_ROUTE === $request->attributes->get('_route') - && $request->isMethod('POST'); - } + $email = $request->request->get('email', ''); - public function getCredentials(Request $request) - { - $credentials = [ - 'email' => $request->request->get('email'), - 'password' => $request->request->get('password'), - 'csrf_token' => $request->request->get('_csrf_token'), - ]; - $request->getSession()->set( - Security::LAST_USERNAME, - $credentials['email'] + $request->getSession()->set(Security::LAST_USERNAME, $email); + + return new Passport( + new UserBadge($email), + new PasswordCredentials($request->request->get('password', '')), + [ + new CsrfTokenBadge('authenticate', $request->get('_csrf_token')), + ] ); - - return $credentials; } - public function getUser($credentials, UserProviderInterface $userProvider) + public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { - $token = new CsrfToken('authenticate', $credentials['csrf_token']); - if (!$this->csrfTokenManager->isTokenValid($token)) { - throw new InvalidCsrfTokenException(); - } - - $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]); - - if (!$user) { - // fail authentication with a custom error - throw new CustomUserMessageAuthenticationException('Email could not be found.'); - } - - return $user; - } - - public function checkCredentials($credentials, UserInterface $user) - { - return $this->passwordHasher->isPasswordValid($user, $credentials['password']); - } - - /** - * Used to upgrade (rehash) the user's password automatically over time. - */ - public function getPassword($credentials): ?string - { - return $credentials['password']; - } - - public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey) - { - if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { + if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) { return new RedirectResponse($targetPath); } return new RedirectResponse($this->urlGenerator->generate('user_page')); } - protected function getLoginUrl() + protected function getLoginUrl(Request $request): string { return $this->urlGenerator->generate(self::LOGIN_ROUTE); } -} +} \ No newline at end of file