update to php 8.5, backup codes, etc.

modified:   Dockerfile
	modified:   composer.json
	modified:   composer.lock
	modified:   config/packages/twig.yaml
	modified:   config/services.yaml
	modified:   docs/Caddyfile
	modified:   docs/compose.yaml
	renamed:    docs/env.example -> docs/example.env
	modified:   public/index.php
	modified:   readme.md
	modified:   src/Command/GenerateBackupCodesCommand.php
	modified:   src/ConfigBag.php
	modified:   src/Data/Payload.php
	modified:   src/Enum/Scope.php
	modified:   src/Listener/AcceptListener.php
	modified:   src/Listener/AllowListener.php
	modified:   src/Listener/InterceptListener.php
	modified:   src/Listener/LoginListener.php
	modified:   src/MonitorCacheKeys.php
	modified:   src/PersistCache.php
	modified:   src/Service/BackupCodeManager.php
	modified:   src/Service/DomainManager.php
	new file:   src/Service/LoginManager.php
	modified:   src/Trait/CookieNameTrait.php
	modified:   src/Trait/GetTotpTrait.php
	modified:   src/Trait/MakeNonceTrait.php
	modified:   src/Trait/StringTrait.php
	modified:   src/Utilities.php
	modified:   templates/_script.html.twig
	modified:   templates/_style.html.twig
	modified:   templates/base.html.twig
	modified:   templates/login.html.twig
This commit is contained in:
2026-05-29 21:56:42 -04:00
parent 43e9b7136e
commit 9114cfd96f
32 changed files with 682 additions and 603 deletions
+14 -20
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Service;
use App\MonitorCacheKeys;
use App\Trait\HasLoggerTrait;
use App\Trait\StringTrait;
use DateTimeImmutable;
use Exception;
@@ -11,20 +12,17 @@ use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException;
use App\Trait\GetTotpTrait;
/**
* Manages generation and validation of singleuse backup codes.
*
* Backup codes are caseinsensitive alphanumeric strings whose length is
* the length of the TOTP code plus two characters. They are stored in the
* cache. Each code is marked as used after a successful authentication.
/** backup-codes are caseinsensitive alphanumeric strings
* they are single-use and marked as used after successful authentication
*/
final class BackupCodeManager {
final readonly class BackupCodeManager {
use GetTotpTrait;
use HasLoggerTrait;
use StringTrait;
private const DEFAULT_COUNT = 10;
private const int DEFAULT_COUNT = 10;
/* php base_convert() will break if given too long of an input */
const MAX_LENGTH = 64;
const int MAX_LENGTH = 64;
private CacheItemPoolInterface $sessionCache;
@@ -33,13 +31,10 @@ final class BackupCodeManager {
$this->sessionCache = new MonitorCacheKeys($sessionCache);
}
/**
* Generate a set of backup codes for a given user identifier.
*
/** generate a set of backup-codes and return them
* @param int $count Number of codes to generate
* @return list<string> Generated backup codes
* @throws InvalidArgumentException|Exception
*/
* @return string[] Generated backup codes
* @throws InvalidArgumentException|Exception */
public function generate(int $count = self::DEFAULT_COUNT): array {
$length = min($this->getTotp()->getDigits() + 2, self::MAX_LENGTH);
$codes = [];
@@ -50,6 +45,7 @@ final class BackupCodeManager {
$length, '0', STR_PAD_LEFT);
}
$this->saveCodes($codes);
$this->logger->info("generated {$count} backup codes}");
return $codes;
}
@@ -66,16 +62,14 @@ final class BackupCodeManager {
}
}
/**
* Verify a backup code and, if valid, mark it as used.
*
/** check if backup-code is valid and mark it as used
* @param string $code Code supplied by the client
* @return bool true if the code is valid and unused
* @throws InvalidArgumentException
*/
* @throws InvalidArgumentException */
public function verifyAndConsume(string $code): bool {
$backupItem = $this->sessionCache->getItem($this->makeCacheKey(strtolower("backup_$code")));
if ($backupItem->isHit() && $backupItem->get()) {
$this->logger->debug("valid backup code");
/* mark backup code as spent */
$backupItem->set(false); /* used */
/* per PSR6, if no expiration is set, implementation may set a default,