clusev/app/Livewire/Auth/TwoFactorSetup.php

76 lines
2.4 KiB
PHP

<?php
namespace App\Livewire\Auth;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
use PragmaRX\Google2FA\Exceptions\Google2FAException;
use PragmaRX\Google2FAQRCode\Google2FA;
#[Layout('layouts.auth')]
class TwoFactorSetup extends Component
{
public string $secret = '';
#[Validate('required|string')]
public string $code = '';
public function mount()
{
if (Auth::user()->hasTotp()) {
return $this->redirect(route('settings'), navigate: true);
}
$this->secret = (new Google2FA)->generateSecretKey();
}
public function confirm()
{
$this->validate();
try {
$matches = (new Google2FA)->verifyKey($this->secret, preg_replace('/\s+/', '', $this->code) ?? '');
} catch (Google2FAException $e) {
$matches = false;
}
if (! $matches) {
throw ValidationException::withMessages(['code' => __('auth.code_mismatch')]);
}
Auth::user()->forceFill([
'two_factor_secret' => $this->secret,
'two_factor_confirmed_at' => now(),
])->save();
// First factor enrolled and no codes yet → generate the single recovery set and
// ask Settings to pop the modal (the app layout hosts wire-elements/modal).
if (! Auth::user()->hasRecoveryCodes()) {
Auth::user()->replaceRecoveryCodes();
session()->flash('open_recovery_modal', true);
// put (not flash): must survive the redirect AND the later openModal Livewire
// request; the modal pulls it on mount to reveal the freshly generated codes once.
session()->put('2fa.codes_fresh', now()->timestamp);
// One-time download grant (a timestamp) — the download route requires + consumes
// it within a short window, so the fresh set can be downloaded once and a later
// direct hit 403s.
session()->put('2fa.download_grant', now()->timestamp);
}
return $this->redirect(route('settings'), navigate: true);
}
public function qrCode(): string
{
return (new Google2FA)->getQRCodeInline('Clusev', Auth::user()->email, $this->secret);
}
public function render()
{
return view('livewire.auth.two-factor-setup')->title(__('auth.title_setup'));
}
}