webappAIO/backend/app/Services/TwoFactorService.php
2026-08-04 11:26:25 +03:00

72 lines
1.8 KiB
PHP

<?php
namespace App\Services;
use App\Models\TwoFactorRecoveryCode;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use PragmaRX\Google2FA\Google2FA;
class TwoFactorService
{
public function __construct(private readonly Google2FA $google2fa) {}
public function generateSecret(): string
{
return $this->google2fa->generateSecretKey();
}
public function otpAuthUrl(User $user, string $secret): string
{
return $this->google2fa->getQRCodeUrl(
config('app.name'),
$user->email,
$secret,
);
}
public function verifyCode(string $secret, string $code): bool
{
return $this->google2fa->verifyKey($secret, $code, window: 1);
}
/**
* @return list<string> the plaintext recovery codes (shown once, never stored plaintext)
*/
public function generateRecoveryCodes(User $user): array
{
$user->twoFactorRecoveryCodes()->delete();
$plaintextCodes = [];
foreach (range(1, 10) as $_) {
$code = Str::random(10);
$plaintextCodes[] = $code;
TwoFactorRecoveryCode::create([
'user_id' => $user->id,
'code_hash' => Hash::make($code),
]);
}
return $plaintextCodes;
}
public function redeemRecoveryCode(User $user, string $code): bool
{
$recoveryCode = $user->twoFactorRecoveryCodes()
->whereNull('used_at')
->get()
->first(fn (TwoFactorRecoveryCode $recoveryCode) => Hash::check($code, $recoveryCode->code_hash));
if (! $recoveryCode) {
return false;
}
$recoveryCode->update(['used_at' => now()]);
return true;
}
}