├── LICENSE ├── Model └── BackupCodeInterface.php ├── README.md ├── Security ├── Http │ └── EventListener │ │ └── CheckBackupCodeListener.php └── TwoFactor │ ├── Backup │ ├── BackupCodeManager.php │ ├── BackupCodeManagerInterface.php │ └── NullBackupCodeManager.php │ └── Event │ └── BackupCodeEvents.php └── composer.json /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Christian Scheb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Model/BackupCodeInterface.php: -------------------------------------------------------------------------------- 1 | eventDispatcher->dispatch($event, BackupCodeEvents::CHECK); 35 | 36 | if ($this->backupCodeManager->isBackupCode($user, $code)) { 37 | $this->eventDispatcher->dispatch($event, BackupCodeEvents::VALID); 38 | $this->backupCodeManager->invalidateBackupCode($user, $code); 39 | 40 | return true; 41 | } 42 | 43 | $this->eventDispatcher->dispatch($event, BackupCodeEvents::INVALID); 44 | 45 | return false; 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | public static function getSubscribedEvents(): array 52 | { 53 | return [CheckPassportEvent::class => ['checkPassport', self::LISTENER_PRIORITY]]; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Security/TwoFactor/Backup/BackupCodeManager.php: -------------------------------------------------------------------------------- 1 | isBackupCode($code); 23 | } 24 | 25 | return false; 26 | } 27 | 28 | public function invalidateBackupCode(object $user, string $code): void 29 | { 30 | if (!($user instanceof BackupCodeInterface)) { 31 | return; 32 | } 33 | 34 | $user->invalidateBackupCode($code); 35 | $this->persister->persist($user); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Security/TwoFactor/Backup/BackupCodeManagerInterface.php: -------------------------------------------------------------------------------- 1 |