mailwolt/app/Services/TotpService.php

84 lines
2.3 KiB
PHP

<?php
namespace App\Services;
use App\Models\TwoFactorMethod;
use App\Models\TwoFactorRecoveryCode;
use App\Models\User;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use PragmaRX\Google2FA\Google2FA;
class TotpService
{
public function __construct(private readonly Google2FA $g2fa = new Google2FA()) {}
public function generateSecret(): string
{
return $this->g2fa->generateSecretKey(32);
}
public function verify(string $secret, string $code): bool
{
return (bool) $this->g2fa->verifyKey($secret, $code, 1);
}
public function qrCodeSvg(User $user, string $secret): string
{
$otpauth = $this->g2fa->getQRCodeUrl(
config('app.name', 'Mailwolt'),
$user->email,
$secret
);
$renderer = new ImageRenderer(
new RendererStyle(200, 2),
new SvgImageBackEnd()
);
return (new Writer($renderer))->writeString($otpauth);
}
public function enable(User $user, string $secret): array
{
TwoFactorMethod::updateOrCreate(
['user_id' => $user->id, 'method' => 'totp'],
['secret' => encrypt($secret), 'enabled' => true, 'confirmed_at' => now()]
);
$user->update(['two_factor_enabled' => true]);
return TwoFactorRecoveryCode::generateNewSet($user->id);
}
public function disable(User $user): void
{
TwoFactorMethod::where('user_id', $user->id)->where('method', 'totp')->delete();
TwoFactorRecoveryCode::where('user_id', $user->id)->delete();
if (!$user->twoFactorMethods()->where('enabled', true)->exists()) {
$user->update(['two_factor_enabled' => false]);
}
}
public function getSecret(User $user): ?string
{
$m = TwoFactorMethod::where('user_id', $user->id)
->where('method', 'totp')
->where('enabled', true)
->first();
return $m ? decrypt($m->secret) : null;
}
public function isEnabled(User $user): bool
{
return TwoFactorMethod::where('user_id', $user->id)
->where('method', 'totp')
->where('enabled', true)
->exists();
}
}