<?php
namespace App\Controller;
use App\Security\LoginFormAuthenticator;
use App\Services\TwoFactorService;
use LogicException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* @Route("/login", name="app_login")
* @param AuthenticationUtils $authenticationUtils
* @param Request $request
* @param TwoFactorService $twoFactorService
* @return Response
*/
public function login(
AuthenticationUtils $authenticationUtils,
Request $request,
TwoFactorService $twoFactorService
): Response {
if ($this->getUser()) {
return $this->redirectToRoute("app_index");
}
$session = $request->getSession();
// Кнопка «Вернуться ко входу» на форме кода: гасим challenge и
// показываем обычную форму логина и пароля (#4049).
if ($request->query->getBoolean('restart')) {
$session->remove(LoginFormAuthenticator::SESSION_CHALLENGE);
return $this->redirectToRoute('app_login');
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
// Живой challenge в сессии означает, что пароль уже принят и ждём код.
// Проверяем его именно здесь, а не по одному факту наличия ключа:
// токен мог истечь или исчерпать попытки, пока страница была открыта, —
// тогда форму кода рисовать нельзя, человека надо вернуть к паролю.
$challengeToken = (string)$session->get(LoginFormAuthenticator::SESSION_CHALLENGE, '');
if ($challengeToken !== '' && $twoFactorService->loadValidChallenge($challengeToken) !== null) {
// Приглашение ввести код приезжает сюда тем же механизмом, что и
// ошибки (иначе из guard-цепочки на страницу логина не вернуться),
// но ошибкой не является — красный блок на первом же шаге пугал бы
// зря. Отличаем по служебному ключу, шаблон покажет нейтральный текст.
if ($error !== null && $error->getMessageKey() === LoginFormAuthenticator::PROMPT_ENTER_CODE) {
$error = null;
}
return $this->render('security/login_2fa.html.twig', [
'error' => $error,
'attempts_left' => $twoFactorService->attemptsLeft($challengeToken),
'digits' => \App\Services\TotpService::digits(),
]);
}
$session->remove(LoginFormAuthenticator::SESSION_CHALLENGE);
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.');
}
}