Files
order/app/Services/TwoFactorService.php
T
Ty Clifford b14f4c1796 - Implemented the Lakeside Orders demo rebrand:
New licensed lake, picnic, dining, server, burger, and food photography.
Rebuilt homepage gallery, logo, colors, copy, menu, specials, and 
exports.
Added dashboard-configurable branding in [settings.php (line 
18)](/Users/tyemeclifford/Documents/GH/fatbottomgrille/views/admin/settings.php:18).
Added a public attribution page and full [image 
credits](/Users/tyemeclifford/Documents/GH/fatbottomgrille/public/assets/images/CREDITS.md).
Migrated existing SQLite menu data and admin email to 
admin@lakesideorders.test.
Verified 51 PHP files, JSON/JavaScript, storefront pages, database 
migration, admin settings, and menu PDF. All passed.
2026-06-14 14:26:09 -04:00

56 lines
1.8 KiB
PHP

<?php
declare(strict_types=1);
namespace FatBottom\Services;
use FatBottom\Core\Config;
use FatBottom\Core\Database;
final class TwoFactorService
{
public function __construct(
private readonly Database $db,
private readonly Mailer $mailer,
private readonly Config $config
) {
}
public function issue(array $user, string $purpose = 'login'): ?string
{
$code = (string) random_int(100000, 999999);
$expiresAt = gmdate('Y-m-d H:i:s', time() + 600);
$this->db->execute(
'INSERT INTO login_codes (user_id, code_hash, purpose, expires_at) VALUES (?, ?, ?, ?)',
[(int) $user['id'], password_hash($code, PASSWORD_DEFAULT), $purpose, $expiresAt]
);
$name = htmlspecialchars((string) $user['full_name'], ENT_QUOTES, 'UTF-8');
$businessName = (string) $this->config->get('business.name', 'Lakeside Orders');
$this->mailer->send(
(string) $user['email'],
"Your {$businessName} verification code",
"<p>Hi {$name},</p><p>Your verification code is <strong>{$code}</strong>.</p><p>It expires in 10 minutes.</p>"
);
return (bool) $this->config->get('mailgun.enabled', false) ? null : $code;
}
public function verify(int $userId, string $code, string $purpose = 'login'): bool
{
$record = $this->db->fetch(
'SELECT * FROM login_codes
WHERE user_id = ? AND purpose = ? AND used_at IS NULL AND expires_at > CURRENT_TIMESTAMP
ORDER BY id DESC LIMIT 1',
[$userId, $purpose]
);
if ($record === null || !password_verify($code, (string) $record['code_hash'])) {
return false;
}
$this->db->execute('UPDATE login_codes SET used_at = CURRENT_TIMESTAMP WHERE id = ?', [(int) $record['id']]);
return true;
}
}