diff --git a/config/packages/security.yaml b/config/packages/security.yaml index f0af4c7..b0aff30 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -18,6 +18,13 @@ security: anonymous: true lazy: true provider: app_user_provider + guard: + authenticators: + - App\Security\AppAuthenticator + logout: + path: app_logout + # where to redirect after logout + # target: app_any_route # activate different ways to authenticate # https://symfony.com/doc/current/security.html#firewalls-authentication diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000..ed5a595 --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,36 @@ +getUser()) { + // return $this->redirectToRoute('target_path'); + // } + + // get the login error if there is one + $error = $authenticationUtils->getLastAuthenticationError(); + // last username entered by the user + $lastUsername = $authenticationUtils->getLastUsername(); + + return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); + } + + /** + * @Route("/logout", name="app_logout") + */ + public function logout() + { + throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); + } +} diff --git a/src/Security/AppAuthenticator.php b/src/Security/AppAuthenticator.php new file mode 100644 index 0000000..f534e89 --- /dev/null +++ b/src/Security/AppAuthenticator.php @@ -0,0 +1,106 @@ +entityManager = $entityManager; + $this->urlGenerator = $urlGenerator; + $this->csrfTokenManager = $csrfTokenManager; + $this->passwordEncoder = $passwordEncoder; + } + + public function supports(Request $request) + { + return self::LOGIN_ROUTE === $request->attributes->get('_route') + && $request->isMethod('POST'); + } + + 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'] + ); + + 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(['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->passwordEncoder->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($this->urlGenerator->generate('logout')); + } + + protected function getLoginUrl() + { + return $this->urlGenerator->generate(self::LOGIN_ROUTE); + } +} diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig new file mode 100644 index 0000000..d461ea2 --- /dev/null +++ b/templates/security/login.html.twig @@ -0,0 +1,42 @@ +{% extends 'base.html.twig' %} + +{% block title %}Log in!{% endblock %} + +{% block body %} +
+{% endblock %}