twoFactorSession = $session->getSection('2fa_auth'); // $secret = $this->twoFactorService->generateSecret(); // bdump($secret); } protected function createComponentSignInForm(): Form { $form = new Form(); $form->addText('username', 'Uživatelské jméno:') ->setRequired('Prosím vyplňte své uživatelské jméno.') ->setValue(@$_COOKIE['user']); $form->addPassword('password', 'Heslo:'); // setRequired nepoužívat!! kvůli soft logoutu!!! //->setRequired('Prosím vyplňte své heslo.'); $form->addSubmit('send', 'Přihlásit'); //$form->addProtection(); //ochrana pomocí TOKENu $form->onSuccess[] = $this->signInFormSucceeded(...); return $form; } private function signInFormSucceeded(Form $form, LoginFormData $data): void //moje vlastní třída LoginFormData { try { // 1. Ověříme heslo přes autentifikátor (manuálně bez volání $this->getUser()->login()) $identity = $this->autentifikator->authenticate($data->username, $data->password); // 2. Zjistíme, zda má uživatel aktivní 2FA (např. má v DB uložený secret) $twoFactor = $identity->twoFactor ?? false; if ($twoFactor) { // Uživatel MÁ 2FA -> uložíme do session a jdeme na druhý krok $this->twoFactorSession->identity = $identity; $this->twoFactorSession->setExpiration('5 minutes'); $this->redirect('twoFactor'); } else { // Uživatel NEMÁ 2FA -> přihlásíme ho hned $this->getUser()->login($identity); $this->restoreRequest($this->backlink); $this->redirect(':Home:'); } } catch (Nette\Security\AuthenticationException $e) { $form->addError('Neplatné jméno nebo heslo.'); } // try { // $this->getUser()->setAuthenticator($this->autentifikator); // musíme ji registrovat! // // validace přihlášení je v třídě MujAutentifikator->authenticate(...) // $this->getUser()->login($data->username, $data->password); // // kam potom? // $this->restoreRequest($this->backlink); // vrátit se na požadovanou stránku // $this->redirect(':Home:'); // jinak přejdi sem // } catch (Nette\Database\ConnectionException) { // error_log("Obvody - neplatne prihlaseni.", 0); //log je ve var/log/apache2/error.log (fail2ban) // $form->addError('Neplatné přihlášení.'); // } catch (Nette\Security\AuthenticationException $e) { // error_log("Obvody - neplatne prihlaseni.", 0); //log je ve var/log/apache2/error.log (fail2ban) // $form->addError($e->getMessage()); // } } protected function createComponentTwoFactorForm(): Form { $form = new Form(); $form->addText('code', 'Ověřovací kód (OTP):') ->setRequired() ->addRule($form::Pattern, 'Kód musí mít 6 číslic', '\d{6}') ->setHtmlAttribute('placeholder', '000 000') ->setHtmlAttribute('autocomplete', 'one-time-code'); $form->addSubmit('verify', 'Ověřit a přihlásit'); $form->onSuccess[] = $this->twoFactorFormSucceeded(...); return $form; } private function twoFactorFormSucceeded(Form $form, \stdClass $data): void { /** * Uživatelská identita = přihlášený user. * @var UserIdentity */ $identity = $this->twoFactorSession->identity; if (!$identity) { $this->redirect('in'); } bdump($identity); // Předpokládáme, že secret je uložen v identitě (z DB) $secret = $identity->totpSecret ?? null; if ($this->twoFactorService->verifyCode($secret, $data->code)) { // KÓD JE SPRÁVNÝ -> Přihlásíme uživatele do Nette nativně $this->getUser()->login($identity); // Vyčistíme dočasnou session $this->twoFactorSession->remove(); $this->restoreRequest($this->backlink); $this->redirect(':Home:'); } else { $form->addError('Neplatný ověřovací kód.'); } } public function renderTwoFactor(): void { if (!$this->twoFactorSession->identity) { $this->redirect('in'); } } public function renderIn(): void { } /** * Normalní logout. * @return void */ public function actionOut(): void { $this->getUser()->logout(); $this->flashMessage('Odhlášení bylo úspěšné.'); $this->redirect('Sign:in'); } }