<?php
declare(strict_types=1);
namespace Scheb\TwoFactorBundle\Security\Http\EventListener;
use Scheb\TwoFactorBundle\Security\Http\Authenticator\Passport\Credentials\TwoFactorCodeCredentials;
use Scheb\TwoFactorBundle\Security\Http\Authenticator\Passport\TwoFactorPassport;
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\PreparationRecorderInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
/**
* @internal
*/
abstract class AbstractCheckCodeListener implements EventSubscriberInterface
{
/**
* @var PreparationRecorderInterface
*/
private $preparationRecorder;
public function __construct(PreparationRecorderInterface $preparationRecorder)
{
$this->preparationRecorder = $preparationRecorder;
}
public function checkPassport(CheckPassportEvent $event): void
{
$passport = $event->getPassport();
if (!($passport instanceof TwoFactorPassport && $passport->hasBadge(TwoFactorCodeCredentials::class))) {
return;
}
/** @var TwoFactorCodeCredentials $credentialsBadge */
$credentialsBadge = $passport->getBadge(TwoFactorCodeCredentials::class);
if ($credentialsBadge->isResolved()) {
return;
}
$token = $passport->getTwoFactorToken();
$providerName = $token->getCurrentTwoFactorProvider();
if (!$providerName) {
throw new AuthenticationException('There is no active two-factor provider.');
}
if (!$this->preparationRecorder->isTwoFactorProviderPrepared($token->getProviderKey(true), $providerName)) {
throw new AuthenticationException(sprintf('The two-factor provider "%s" has not been prepared.', $providerName));
}
$user = $token->getUser();
if (null === $user) {
throw new \RuntimeException('Security token must provide a user.');
}
if ($this->isValidCode($providerName, $user, $credentialsBadge->getCode())) {
$token->setTwoFactorProviderComplete($providerName);
$credentialsBadge->markResolved();
}
}
/**
* @param object|string $user
*/
abstract protected function isValidCode(string $providerName, $user, string $code): bool;
}