<?php
declare(strict_types=1);
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
use Endroid\QrCode\RoundBlockSizeMode\RoundBlockSizeModeMargin;
use Endroid\QrCode\Writer\PngWriter;
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Totp\TotpAuthenticatorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
#[Route('/login', name: 'app_login')]
public function index(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('login/index.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
#[Route('/logout', name: 'app_logout', methods: ['GET'])]
public function logout()
{
}
#[Route('/authentication/2fa/enable', name: 'app_2fa_enable')]
#[IsGranted('ROLE_ADMIN')]
public function enable2fa(TotpAuthenticatorInterface $totpAuthenticator)
{
$user = $this->getUser();
if (null === $user) {
throw new AuthenticationException('Access denied.');
}
if (!$user->isTotpAuthenticationEnabled()) {
$totpSecret = $totpAuthenticator->generateSecret();
$user->setTotpSecret($totpSecret);
}
return $this->render('security/2fa_form.html.twig', [
'qrContent' => base64_encode($totpAuthenticator->getQRContent($user)),
'secret' => base64_encode($totpSecret),
]);
}
#[Route('/authentication/2fa/save', name: 'app_2fa_save', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
public function save2fa(
Request $request,
TotpAuthenticatorInterface $totpAuthenticator,
EntityManagerInterface $entityManager,
RouterInterface $router,
SessionInterface $session,
) {
$user = $this->getUser();
if (null === $user) {
return new RedirectResponse($router->generate('app_login'));
}
$code = $request->request->get('code');
$secret = base64_decode($request->request->get('totp-secret'));
$user->setTotpSecret($secret);
if ($totpAuthenticator->checkCode($user, $code)) {
$entityManager->flush();
$session->getFlashBag()->add('success', '2FA Authorization Enabled');
return new RedirectResponse($router->generate('app_login'));
}
return new RedirectResponse($router->generate('app_2fa_enable'));
}
#[Route('/authentication/2fa/generate', name: 'app_2fa_generate')]
#[IsGranted('ROLE_ADMIN')]
public function generate2fa(Request $request)
{
$result = Builder::create()
->writer(new PngWriter())
->writerOptions([])
->data(base64_decode($request->get('qrContent')))
->encoding(new Encoding('UTF-8'))
->errorCorrectionLevel(new ErrorCorrectionLevelHigh())
->size(300)
->margin(0)
->roundBlockSizeMode(new RoundBlockSizeModeMargin())
->build();
return new Response($result->getString(), 200, ['Content-Type' => 'image/png']);
}
}