Switch to new authentication system
This commit is contained in:
		
							parent
							
								
									d277d9c93e
								
							
						
					
					
						commit
						f2eed9d848
					
				
					 3 changed files with 38 additions and 76 deletions
				
			
		| 
						 | 
					@ -22,8 +22,7 @@ security:
 | 
				
			||||||
        main:
 | 
					        main:
 | 
				
			||||||
            lazy: true
 | 
					            lazy: true
 | 
				
			||||||
            provider: app_user_provider
 | 
					            provider: app_user_provider
 | 
				
			||||||
            guard:
 | 
					            custom_authenticators:
 | 
				
			||||||
                authenticators:
 | 
					 | 
				
			||||||
                - App\Security\AppAuthenticator
 | 
					                - App\Security\AppAuthenticator
 | 
				
			||||||
            logout:
 | 
					            logout:
 | 
				
			||||||
                path: app_logout
 | 
					                path: app_logout
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection;
 | 
				
			||||||
use Doctrine\Common\Collections\Collection;
 | 
					use Doctrine\Common\Collections\Collection;
 | 
				
			||||||
use Doctrine\ORM\Mapping as ORM;
 | 
					use Doctrine\ORM\Mapping as ORM;
 | 
				
			||||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
 | 
					use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
 | 
				
			||||||
 | 
					use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
 | 
				
			||||||
use Symfony\Component\Security\Core\User\UserInterface;
 | 
					use Symfony\Component\Security\Core\User\UserInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -14,7 +15,7 @@ use Symfony\Component\Security\Core\User\UserInterface;
 | 
				
			||||||
 * @ORM\Table(name="`user`")
 | 
					 * @ORM\Table(name="`user`")
 | 
				
			||||||
 * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
 | 
					 * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class User implements UserInterface
 | 
					class User implements UserInterface, PasswordAuthenticatedUserInterface
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @ORM\Id
 | 
					     * @ORM\Id
 | 
				
			||||||
| 
						 | 
					@ -92,6 +93,14 @@ class User implements UserInterface
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @see UserInterface
 | 
					     * @see UserInterface
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    public function getUserIdentifier(): string
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return (string) $this->email;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @deprecated since Symfony 5.3, use getUserIdentifier instead
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public function getUsername(): string
 | 
					    public function getUsername(): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return (string) $this->username;
 | 
					        return (string) $this->username;
 | 
				
			||||||
| 
						 | 
					@ -117,11 +126,11 @@ class User implements UserInterface
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @see UserInterface
 | 
					     * @see PasswordAuthenticatedUserInterface
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public function getPassword(): string
 | 
					    public function getPassword(): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return (string) $this->password;
 | 
					        return $this->password;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function setPassword(string $password): self
 | 
					    public function setPassword(string $password): self
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,104 +2,58 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace App\Security;
 | 
					namespace App\Security;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Entity\User;
 | 
					 | 
				
			||||||
use Doctrine\ORM\EntityManagerInterface;
 | 
					 | 
				
			||||||
use Symfony\Component\HttpFoundation\RedirectResponse;
 | 
					use Symfony\Component\HttpFoundation\RedirectResponse;
 | 
				
			||||||
use Symfony\Component\HttpFoundation\Request;
 | 
					use Symfony\Component\HttpFoundation\Request;
 | 
				
			||||||
 | 
					use Symfony\Component\HttpFoundation\Response;
 | 
				
			||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 | 
					use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
 | 
				
			||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
 | 
					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\Security;
 | 
				
			||||||
use Symfony\Component\Security\Core\User\UserInterface;
 | 
					use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
 | 
				
			||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
 | 
					use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
 | 
				
			||||||
use Symfony\Component\Security\Csrf\CsrfToken;
 | 
					use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
 | 
				
			||||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
 | 
					use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
 | 
				
			||||||
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
 | 
					use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
 | 
				
			||||||
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
 | 
					use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
 | 
				
			||||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
 | 
					use Symfony\Component\Security\Http\Util\TargetPathTrait;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AppAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
 | 
					class AppAuthenticator extends AbstractLoginFormAuthenticator
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    use TargetPathTrait;
 | 
					    use TargetPathTrait;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public const LOGIN_ROUTE = 'app_login';
 | 
					    public const LOGIN_ROUTE = 'app_login';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private $entityManager;
 | 
					    private UrlGeneratorInterface $urlGenerator;
 | 
				
			||||||
    private $urlGenerator;
 | 
					 | 
				
			||||||
    private $csrfTokenManager;
 | 
					 | 
				
			||||||
    private $passwordHasher;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordHasherInterface $passwordHasher)
 | 
					    public function __construct(UrlGeneratorInterface $urlGenerator)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->entityManager = $entityManager;
 | 
					 | 
				
			||||||
        $this->urlGenerator = $urlGenerator;
 | 
					        $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')
 | 
					        $email = $request->request->get('email', '');
 | 
				
			||||||
            && $request->isMethod('POST');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getCredentials(Request $request)
 | 
					        $request->getSession()->set(Security::LAST_USERNAME, $email);
 | 
				
			||||||
    {
 | 
					
 | 
				
			||||||
        $credentials = [
 | 
					        return new Passport(
 | 
				
			||||||
            'email' => $request->request->get('email'),
 | 
					            new UserBadge($email),
 | 
				
			||||||
            'password' => $request->request->get('password'),
 | 
					            new PasswordCredentials($request->request->get('password', '')),
 | 
				
			||||||
            'csrf_token' => $request->request->get('_csrf_token'),
 | 
					            [
 | 
				
			||||||
        ];
 | 
					                new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
 | 
				
			||||||
        $request->getSession()->set(
 | 
					            ]
 | 
				
			||||||
            Security::LAST_USERNAME,
 | 
					 | 
				
			||||||
            $credentials['email']
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					 | 
				
			||||||
        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 ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
 | 
				
			||||||
        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)) {
 | 
					 | 
				
			||||||
            return new RedirectResponse($targetPath);
 | 
					            return new RedirectResponse($targetPath);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new RedirectResponse($this->urlGenerator->generate('user_page'));
 | 
					        return new RedirectResponse($this->urlGenerator->generate('user_page'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected function getLoginUrl()
 | 
					    protected function getLoginUrl(Request $request): string
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
 | 
					        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Reference in a new issue