Skip to content

Commit

Permalink
Creation of BackupCodeUsedEvent
Browse files Browse the repository at this point in the history
  • Loading branch information
chaours committed Oct 6, 2024
1 parent 5d255b5 commit 3ae1b3c
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 1 deletion.
7 changes: 7 additions & 0 deletions doc/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ Constant: ``Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticati

Is dispatched when two-factor authentication is attempted, right before checking the code.

``scheb_two_factor.authentication.backup_code_used``
-------------------------------------------

Constant: ``Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvents::BACKUP_CODE_USED``

Is dispatched when two-factor authentication was successful with a backup-code.

``scheb_two_factor.authentication.success``
-------------------------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@

namespace Scheb\TwoFactorBundle\Security\Http\EventListener;

use Scheb\TwoFactorBundle\Security\Authentication\Token\TwoFactorTokenInterface;
use Scheb\TwoFactorBundle\Security\TwoFactor\Backup\BackupCodeManagerInterface;
use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvent;
use Scheb\TwoFactorBundle\Security\TwoFactor\Event\TwoFactorAuthenticationEvents;
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\PreparationRecorderInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
* @final
Expand All @@ -20,6 +26,9 @@ class CheckBackupCodeListener extends AbstractCheckCodeListener
public function __construct(
PreparationRecorderInterface $preparationRecorder,
private readonly BackupCodeManagerInterface $backupCodeManager,
private readonly TokenStorageInterface $tokenStorage,
private readonly RequestStack $requestStack,
private readonly EventDispatcherInterface $eventDispatcher,
) {
parent::__construct($preparationRecorder);
}
Expand All @@ -28,6 +37,16 @@ protected function isValidCode(string $providerName, object $user, string $code)
{
if ($this->backupCodeManager->isBackupCode($user, $code)) {
$this->backupCodeManager->invalidateBackupCode($user, $code);
$token = $this->tokenStorage->getToken();
if (!($token instanceof TwoFactorTokenInterface)) {
return false;
}

$request = $this->requestStack->getCurrentRequest();
if ($request) {
$event = new TwoFactorAuthenticationEvent($request, $token);
$this->eventDispatcher->dispatch($event, TwoFactorAuthenticationEvents::BACKUP_CODE_USED);
}

return true;
}
Expand Down
3 changes: 3 additions & 0 deletions src/bundle/Resources/config/backup_codes.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,8 @@
->args([
service('scheb_two_factor.provider_preparation_recorder'),
service('scheb_two_factor.backup_code_manager'),
service('security.token_storage'),
service('request_stack'),
service('event_dispatcher'),
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ class TwoFactorAuthenticationEvents
*/
public const ATTEMPT = 'scheb_two_factor.authentication.attempt';

/**
* When a backup-code is used.
*/
public const BACKUP_CODE_USED = 'scheb_two_factor.authentication.backup_code_used';

/**
* When two-factor authentication was successful (code was valid) for a single provider.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,29 @@
use PHPUnit\Framework\MockObject\MockObject;
use Scheb\TwoFactorBundle\Security\Http\EventListener\CheckBackupCodeListener;
use Scheb\TwoFactorBundle\Security\TwoFactor\Backup\BackupCodeManagerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
* @property CheckBackupCodeListener $listener
*/
class CheckBackupCodeListenerTest extends AbstractCheckCodeListenerTestSetup
{
private MockObject|BackupCodeManagerInterface $backupCodeManager;
private MockObject|TokenStorageInterface $tokenStorage;
private MockObject|RequestStack $requestStack;
private MockObject|EventDispatcherInterface $eventDispatcher;

protected function setUp(): void
{
parent::setUp();

$this->backupCodeManager = $this->createMock(BackupCodeManagerInterface::class);
$this->listener = new CheckBackupCodeListener($this->preparationRecorder, $this->backupCodeManager);
$this->tokenStorage = $this->createMock(TokenStorageInterface::class);
$this->requestStack = $this->createMock(RequestStack::class);
$this->eventDispatecher = $this->createMock(EventDispatcherInterface::class);
$this->listener = new CheckBackupCodeListener($this->preparationRecorder, $this->backupCodeManager, $this->tokenStorage, $this->requestStack, $this->eventDispatecher);
}

protected function expectDoNothing(): void
Expand Down

0 comments on commit 3ae1b3c

Please sign in to comment.