Switch to new authentication system
This commit is contained in:
parent
d277d9c93e
commit
f2eed9d848
3 changed files with 38 additions and 76 deletions
|
@ -22,9 +22,8 @@ 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
|
||||||
# where to redirect after logout
|
# where to redirect after 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,105 +2,59 @@
|
||||||
|
|
||||||
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