Bonjour les amis,
Je viens vers vous je butte sur un problème depuis maintenant plus de 3 jours.
J'essaye de comprendre comment fonctionne les Events dans symfony pour pouvoir l'utiliser avec Google Authentificator et je vous avouerai que je ne comprend pas encore grand choses.
Finalement après 3 jours d'efforts, je me suis décidé à prendre l'exemple que j'ai trouvé sur le web et a tanter de le modifier.
Problème quoi que je fasse, ça ne fonctionne.
Je souhaite que lorsque je me connecte à mon application, tous se passe comme il le faut.
Mais quand je demande l'url lié à la la route admin, Google Autentificator me demande de m'authentifier seulement et seulement si j'ai le rôle SUPER_ADMIN si je n'ai pas le role SUPER_ADMIN ça me renvoie une erreur 403
Actuellement, que je sois, SUPER_ADMIN ou USER à la connexion google me demande m'authentifier. De plus cela même si je n'ai pas demandé l'url lié à la route admin
Voici mon code pour le Subscriber
class TwoFactorAuthenticationSubscriber implements EventSubscriberInterface
{
const ROLE_2FA_SUCCEED = "2FA_SUCCEED";
const FIREWALL_NAME = "main";
const ROUTE_FOR_2FA = "two-factor";
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
* @var RouterInterface
*/
private $router;
/**
* TwoFactorAuthenticationSubscriber constructor.
* @param TokenStorageInterface $tokenStorage
* @param RouterInterface $router
*/
public function __construct(TokenStorageInterface $tokenStorage, RouterInterface $router)
{
$this->tokenStorage = $tokenStorage;
$this->router = $router;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if (in_array($event->getRequest()->attributes->get('_route'), ["admin", self::ROUTE_FOR_2FA])) {
return;
}
if (($currentToken = $this->tokenStorage->getToken()) && $currentToken instanceof PostAuthenticationGuardToken) {
if ($currentToken->getProviderKey() === self::FIREWALL_NAME) {
if (!$this->hasRole($currentToken, self::ROLE_2FA_SUCCEED)) {
$response = new RedirectResponse($this->router->generate(self::ROUTE_FOR_2FA));
$event->setResponse($response);
}
}
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => ['onKernelRequest', -10],
];
}
private function hasRole(TokenInterface $token, string $role): bool
{
foreach ($token->getRoles() as $userRole) {
if ($userRole->getRole() === $role) {
return true;
}
}
return false;
}
}
Et voici mon code sur controller
class TwoFactorAuthenticationController extends AbstractController
{
/**
* @Route("/two-factor", name="two-factor")
* @param Request $request
* @param TokenStorageInterface $tokenStorage
* @param SessionInterface $session
* @param EntityManagerInterface $entityManager
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
*/
public function twoFactorAction(Request $request, TokenStorageInterface $tokenStorage, SessionInterface $session,
EntityManagerInterface $entityManager)
{
$form = $this->createForm(GoogleAuthenticatorType::class);
$form->handleRequest($request);
$google2fa = new Google2FA();
$svg = null;
// Ici je met la condition de vérification du rôle de l'utilisateur
$userCurrent = $this->getUser();
/** @var User $user */
$user = $this->getUser();
if (!$user->getGoogleAuthenticatorSecret()) {
if ($session->get('2fa_secret')) {
$secret = $session->get('2fa_secret');
} else {
$secret = $google2fa->generateSecretKey();
$request->getSession()->set('2fa_secret', $secret);
}
$svg = $this->generateSvgForUser($google2fa, $user, $secret);
} else {
$secret = $user->getGoogleAuthenticatorSecret();
}
if ($form->isSubmitted() && $form->isValid()) {
$code = $form->getData()["code"];
$codeIsValid = $google2fa->verifyKey($secret, $code, 4);
if ($codeIsValid) {
if (!$user->getGoogleAuthenticatorSecret()) {
$user->setGoogleAuthenticatorSecret($secret);
$entityManager->persist($user);
$entityManager->flush();
}
$this->addRoleTwoFA($tokenStorage, $session);
return $this->redirectToRoute("admin");
}
$this->addFlash("error", "Invalid verification code");
}
return $this->render("security/two-factor.html.twig", [
"svg" => $svg,
"form" => $form->createView(),
]);
}
private function generateSvgForUser(Google2FA $google2FA, User $user, string $secret): string
{
$g2faUrl = $google2FA->getQRCodeUrl(
"Uwandzani.com",
$user->getUsername(),
$secret
);
$writer = new Writer(
new ImageRenderer(
new RendererStyle(400),
new SvgImageBackEnd() // can also user new ImagickImageBackEnd() in order to generate png
)
);
return $writer->writeString($g2faUrl);
}
private function addRoleTwoFA(TokenStorageInterface $tokenStorage, SessionInterface $session): void
{
/** @var PostAuthenticationGuardToken $currentToken */
$currentToken = $tokenStorage->getToken();
$roles = array_merge($currentToken->getRoles(), [TwoFactorAuthenticationSubscriber::ROLE_2FA_SUCCEED]);
$newToken = new PostAuthenticationGuardToken($currentToken->getUser(), $currentToken->getProviderKey(), $roles);
$tokenStorage->setToken($newToken);
$session->set('_security_' . $currentToken->getProviderKey(), serialize($newToken));
}
}