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); } 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')); } }