diff --git a/src/Application/Controller/Admin/d3webauthnadminlogin.php b/src/Application/Controller/Admin/d3webauthnadminlogin.php
index bfaf420..105d7e4 100755
--- a/src/Application/Controller/Admin/d3webauthnadminlogin.php
+++ b/src/Application/Controller/Admin/d3webauthnadminlogin.php
@@ -18,24 +18,20 @@ namespace D3\Webauthn\Application\Controller\Admin;
use D3\TestingTools\Production\IsMockable;
use D3\Webauthn\Application\Controller\Traits\helpersTrait;
use D3\Webauthn\Application\Model\Exceptions\WebauthnGetException;
-use D3\Webauthn\Application\Model\Webauthn;
+use D3\Webauthn\Application\Model\WebauthnAfterLogin;
use D3\Webauthn\Application\Model\WebauthnConf;
use D3\Webauthn\Application\Model\Exceptions\WebauthnException;
-use D3\Webauthn\Modules\Application\Controller\Admin\d3_LoginController_Webauthn;
-use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
+use D3\Webauthn\Application\Model\WebauthnLogin;
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
use Doctrine\DBAL\Exception as DoctrineException;
use OxidEsales\Eshop\Application\Controller\Admin\AdminController;
-use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
use OxidEsales\Eshop\Application\Controller\FrontendController;
-use OxidEsales\Eshop\Core\Exception\ConnectionException;
-use OxidEsales\Eshop\Core\Exception\CookieException;
-use OxidEsales\Eshop\Core\Exception\UserException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Request;
use OxidEsales\Eshop\Core\SystemEventHandler;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\Eshop\Core\UtilsServer;
+use OxidEsales\Eshop\Core\UtilsView;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
@@ -69,17 +65,15 @@ class d3webauthnadminlogin extends AdminController
$this->getUtils()->redirect('index.php?cl=login');
}
- /** @var d3_LoginController_Webauthn $loginController */
- $loginController = $this->d3WebauthnGetLoginController();
- $loginController->d3WebauthnAfterLoginChangeLanguage();
-
$this->generateCredentialRequest();
$this->addTplParam('navFormParams', $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS));
$this->addTplParam('currentProfile', $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_ADMIN_PROFILE));
$this->d3GetSession()->deleteVariable(WebauthnConf::WEBAUTHN_ADMIN_PROFILE);
$this->addTplParam('currentChLanguage', $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_ADMIN_CHLANGUAGE));
- $this->d3GetSession()->deleteVariable(WebauthnConf::WEBAUTHN_ADMIN_CHLANGUAGE);
+
+ $afterLogin = $this->d3WebauthnGetAfterLogin();
+ $afterLogin->changeLanguage();
return $this->d3CallMockableParent('render');
}
@@ -95,7 +89,6 @@ class d3webauthnadminlogin extends AdminController
{
$userId = $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTUSER);
try {
- /** @var Webauthn $webauthn */
$webauthn = $this->d3GetWebauthnObject();
$publicKeyCredentialRequestOptions = $webauthn->getRequestOptions($userId);
$this->d3GetSession()->setVariable(WebauthnConf::WEBAUTHN_ADMIN_LOGIN_OBJECT, $publicKeyCredentialRequestOptions);
@@ -103,95 +96,41 @@ class d3webauthnadminlogin extends AdminController
$this->addTplParam('isAdmin', isAdmin());
} catch (WebauthnException $e) {
$this->d3GetSession()->setVariable(WebauthnConf::GLOBAL_SWITCH, true);
- Registry::getUtilsView()->addErrorToDisplay($e);
+ $this->d3GetUtilsViewObject()->addErrorToDisplay($e);
$this->d3GetLoggerObject()->error($e->getDetailedErrorMessage(), ['UserId' => $userId]);
$this->d3GetLoggerObject()->debug($e->getTraceAsString());
$this->getUtils()->redirect('index.php?cl=login');
}
}
+ /**
+ * @param string $credential
+ * @param string|null $error
+ * @throws WebauthnGetException
+ * @return WebauthnLogin
+ */
+ public function getWebauthnLoginObject(string $credential, ?string $error): WebauthnLogin
+ {
+ return oxNew(WebauthnLogin::class, $credential, $error);
+ }
+
/**
* @return string|null
*/
public function d3AssertAuthn(): ?string
{
- $myUtilsView = $this->d3GetUtilsViewObject();
- /** @var d3_User_Webauthn $user */
- $user = $this->d3GetUserObject();
- $userId = $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTUSER);
- $selectedProfile = $this->d3WebAuthnGetRequest()->getRequestEscapedParameter('profile');
-
try {
- $error = $this->d3WebAuthnGetRequest()->getRequestEscapedParameter('error');
- if (strlen((string) $error)) {
- /** @var WebauthnGetException $e */
- $e = oxNew(WebauthnGetException::class, $error);
- throw $e;
- }
-
- $credential = $this->d3WebAuthnGetRequest()->getRequestEscapedParameter('credential');
- if (!strlen((string) $credential)) {
- /** @var WebauthnGetException $e */
- $e = oxNew(WebauthnGetException::class, 'missing credential data');
- throw $e;
- }
-
- $webAuthn = $this->d3GetWebauthnObject();
- $webAuthn->assertAuthn($credential);
- $user->load($userId);
- $session = $this->d3GetSession();
- $adminProfiles = $session->getVariable("aAdminProfiles");
- $session->initNewSession();
- $session->setVariable("aAdminProfiles", $adminProfiles);
- $session->setVariable(WebauthnConf::OXID_ADMIN_AUTH, $userId);
-
- $cookie = $this->d3WebauthnGetUtilsServer()->getOxCookie();
- if ($cookie === null) {
- /** @var CookieException $exc */
- $exc = oxNew(CookieException::class, 'ERROR_MESSAGE_COOKIE_NOCOOKIE');
- throw $exc;
- }
-
- if ($user->getFieldData('oxrights') === 'user') {
- /** @var UserException $exc */
- $exc = oxNew(UserException::class, 'ERROR_MESSAGE_USER_NOVALIDLOGIN');
- throw $exc;
- }
- $iSubshop = (int) $user->getFieldData('oxrights');
-
- if ($iSubshop) {
- $session->setVariable("shp", $iSubshop);
- $session->setVariable('currentadminshop', $iSubshop);
- Registry::getConfig()->setShopId($iSubshop);
- }
-
- //execute onAdminLogin() event
- $oEvenHandler = $this->d3WebauthnGetEventHandler();
- $oEvenHandler->onAdminLogin(Registry::getConfig()->getShopId());
-
- /** @var d3_LoginController_Webauthn $loginController */
- $loginController = $this->d3WebauthnGetLoginController();
- $loginController->d3webauthnAfterLogin();
-
- return "admin_start";
- } catch (UserException $oEx) {
- $myUtilsView->addErrorToDisplay('LOGIN_ERROR');
- $oStr = getStr();
- $this->addTplParam('user', $oStr->htmlspecialchars($userId));
- $this->addTplParam('profile', $oStr->htmlspecialchars($selectedProfile));
- } catch (CookieException $oEx) {
- $myUtilsView->addErrorToDisplay('LOGIN_NO_COOKIE_SUPPORT');
- $oStr = getStr();
- $this->addTplParam('user', $oStr->htmlspecialchars($userId));
- $this->addTplParam('profile', $oStr->htmlspecialchars($selectedProfile));
- } catch (WebauthnException $e) {
- $myUtilsView->addErrorToDisplay($e);
- $this->d3GetLoggerObject()->error($e->getDetailedErrorMessage(), ['UserId' => $userId]);
- $this->d3GetLoggerObject()->debug($e->getTraceAsString());
- $user->logout();
+ $login = $this->getWebauthnLoginObject(
+ $this->d3WebAuthnGetRequest()->getRequestEscapedParameter('credential'),
+ $this->d3WebAuthnGetRequest()->getRequestEscapedParameter('error')
+ );
+ return $login->adminLogin(
+ $this->d3WebAuthnGetRequest()->getRequestEscapedParameter('profile')
+ );
+ } catch (WebauthnGetException $e) {
+ $this->d3GetUtilsViewObject()->addErrorToDisplay($e);
+ return 'login';
}
-
- return 'login';
}
/**
@@ -233,11 +172,11 @@ class d3webauthnadminlogin extends AdminController
}
/**
- * @return mixed|LoginController
+ * @return WebauthnAfterLogin
*/
- public function d3WebauthnGetLoginController()
+ public function d3WebauthnGetAfterLogin(): WebauthnAfterLogin
{
- return oxNew(LoginController::class);
+ return oxNew(WebauthnAfterLogin::class);
}
/**
diff --git a/src/Application/Model/Exceptions/WebauthnLoginErrorException.php b/src/Application/Model/Exceptions/WebauthnLoginErrorException.php
new file mode 100644
index 0000000..f97a1f2
--- /dev/null
+++ b/src/Application/Model/Exceptions/WebauthnLoginErrorException.php
@@ -0,0 +1,22 @@
+
+ * @link https://www.oxidmodule.com
+ */
+
+declare(strict_types=1);
+
+namespace D3\Webauthn\Application\Model\Exceptions;
+
+use OxidEsales\Eshop\Core\Exception\StandardException;
+
+class WebauthnLoginErrorException extends StandardException
+{
+}
\ No newline at end of file
diff --git a/src/Application/Model/WebauthnAfterLogin.php b/src/Application/Model/WebauthnAfterLogin.php
new file mode 100644
index 0000000..a346833
--- /dev/null
+++ b/src/Application/Model/WebauthnAfterLogin.php
@@ -0,0 +1,64 @@
+
+ * @link https://www.oxidmodule.com
+ */
+
+declare(strict_types=1);
+
+namespace D3\Webauthn\Application\Model;
+
+use OxidEsales\Eshop\Core\Registry;
+
+class WebauthnAfterLogin
+{
+ public function setDisplayProfile()
+ {
+ $sProfile = Registry::getRequest()->getRequestEscapedParameter('profile') ?:
+ Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_ADMIN_PROFILE);
+
+ Registry::getSession()->deleteVariable(WebauthnConf::WEBAUTHN_ADMIN_PROFILE);
+
+ $myUtilsServer = Registry::getUtilsServer();
+
+ if (isset($sProfile)) {
+ $aProfiles = Registry::getSession()->getVariable("aAdminProfiles");
+ if ($aProfiles && isset($aProfiles[$sProfile])) {
+ // setting cookie to store last locally used profile
+ $myUtilsServer->setOxCookie("oxidadminprofile", $sProfile . "@" . implode("@", $aProfiles[$sProfile]), time() + 31536000);
+ Registry::getSession()->setVariable("profile", $aProfiles[$sProfile]);
+ }
+ } else {
+ //deleting cookie info, as setting profile to default
+ $myUtilsServer->setOxCookie("oxidadminprofile", "", time() - 3600);
+ }
+ }
+
+ /**
+ * @return void
+ */
+ public function changeLanguage()
+ {
+ $myUtilsServer = Registry::getUtilsServer();
+ // languages
+ $iLang = Registry::getRequest()->getRequestEscapedParameter('chlanguage') ?:
+ Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_ADMIN_CHLANGUAGE);
+
+ Registry::getSession()->deleteVariable(WebauthnConf::WEBAUTHN_ADMIN_CHLANGUAGE);
+
+ $aLanguages = Registry::getLang()->getAdminTplLanguageArray();
+ if (!isset($aLanguages[$iLang])) {
+ $iLang = key($aLanguages);
+ }
+
+ $myUtilsServer->setOxCookie("oxidadminlanguage", $aLanguages[$iLang]->abbr, time() + 31536000);
+ Registry::getLang()->setTplLanguage($iLang);
+ }
+}
\ No newline at end of file
diff --git a/src/Application/Model/WebauthnLogin.php b/src/Application/Model/WebauthnLogin.php
new file mode 100644
index 0000000..43cc5ae
--- /dev/null
+++ b/src/Application/Model/WebauthnLogin.php
@@ -0,0 +1,395 @@
+
+ * @link https://www.oxidmodule.com
+ */
+
+declare(strict_types=1);
+
+namespace D3\Webauthn\Application\Model;
+
+use D3\Webauthn\Application\Controller\Traits\helpersTrait;
+use D3\Webauthn\Application\Model\Exceptions\WebauthnException;
+use D3\Webauthn\Application\Model\Exceptions\WebauthnGetException;
+use D3\Webauthn\Application\Model\Exceptions\WebauthnLoginErrorException;
+use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
+use OxidEsales\Eshop\Application\Model\User;
+use OxidEsales\Eshop\Core\Config;
+use OxidEsales\Eshop\Core\Exception\CookieException;
+use OxidEsales\Eshop\Core\Exception\UserException;
+use OxidEsales\Eshop\Core\Registry;
+use OxidEsales\Eshop\Core\Session;
+use OxidEsales\Eshop\Core\Str;
+use OxidEsales\Eshop\Core\SystemEventHandler;
+use OxidEsales\Eshop\Core\UtilsServer;
+use OxidEsales\EshopCommunity\Application\Component\UserComponent;
+
+class WebauthnLogin
+{
+ use helpersTrait;
+
+ public $credential;
+
+ public $errorMsg;
+
+ /**
+ * @param string $credential
+ * @param string|null $error
+ * @throws WebauthnGetException
+ */
+ public function __construct(string $credential, string $error = null)
+ {
+ $this->setCredential($credential);
+ $this->setErrorMsg($error);
+ }
+
+ /**
+ * @return string
+ * @throws WebauthnGetException
+ */
+ public function getCredential(): string
+ {
+ if (!strlen(trim((string) $this->credential))) {
+ /** @var WebauthnGetException $e */
+ $e = oxNew(WebauthnGetException::class, 'missing credential data');
+ throw $e;
+ }
+
+ return trim($this->credential);
+ }
+
+ /**
+ * @param string $credential
+ * @throws WebauthnGetException
+ */
+ public function setCredential(string $credential): void
+ {
+ if (!strlen(trim($credential))) {
+ /** @var WebauthnGetException $e */
+ $e = oxNew(WebauthnGetException::class, 'missing credential data');
+ throw $e;
+ }
+
+ $this->credential = trim($credential);
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getErrorMsg(): ?string
+ {
+ return $this->errorMsg;
+ }
+
+ /**
+ * @param string|null $errorMsg
+ */
+ public function setErrorMsg(?string $errorMsg): void
+ {
+ $this->errorMsg = $errorMsg;
+ }
+
+ /**
+ * @param UserComponent $usrCmp
+ * @param bool $setSessionCookie
+ * @return void
+ * @throws WebauthnLoginErrorException
+ */
+ public function frontendLogin(UserComponent $usrCmp, bool $setSessionCookie = false)
+ {
+ $myUtilsView = $this->d3GetUtilsViewObject();
+ /** @var d3_User_Webauthn $user */
+ $user = $this->d3GetUserObject();
+ $userId = $this->getUserId();
+
+ try {
+ $this->handleErrorMessage();
+
+ $user = $this->assertUser($userId);
+ $this->assertAuthn();
+
+ // relogin, don't extract from this try block
+ $usrCmp->setUser($this->d3GetUserObject());
+ $this->setFrontendSession($user);
+ $usrCmp->setLoginStatus(USER_LOGIN_SUCCESS);
+
+ if ($setSessionCookie) {
+ $this->setSessionCookie($user);
+ }
+
+ $this->regenerateSessionId();
+
+ $usrCmp->setUser($user);
+
+ return;
+ } catch (UserException $oEx) {
+ // for login component send exception text to a custom component (if defined)
+ $myUtilsView->addErrorToDisplay($oEx, false, true, '', false);
+
+ //return 'user';
+ } catch (\OxidEsales\Eshop\Core\Exception\CookieException $oEx) {
+ $myUtilsView->addErrorToDisplay($oEx);
+
+ //return 'user';
+ } catch (WebauthnException $e) {
+ $myUtilsView->addErrorToDisplay($e);
+ $this->d3GetLoggerObject()->error($e->getDetailedErrorMessage(), ['UserId' => $userId]);
+ $this->d3GetLoggerObject()->debug($e->getTraceAsString());
+ }
+
+ $user->logout();
+ $exc = oxNew(WebauthnLoginErrorException::class);
+ throw $exc;
+ }
+
+ /**
+ * @param string $selectedProfile
+ * @return string
+ */
+ public function adminLogin(string $selectedProfile): string
+ {
+ $myUtilsView = $this->d3GetUtilsViewObject();
+ /** @var d3_User_Webauthn $user */
+ $user = $this->d3GetUserObject();
+ $userId = $this->getUserId();
+
+ try {
+ $this->handleErrorMessage();
+ $this->assertUser($userId, true);
+ $this->handleBlockedUser($user);
+ $this->assertAuthn();
+ $session = $this->setAdminSession($userId);
+ $this->handleBackendCookie();
+ $this->handleBackendSubshopRights($user, $session);
+
+ $oEvenHandler = $this->d3WebauthnGetEventHandler();
+ $oEvenHandler->onAdminLogin();
+
+ $afterLogin = $this->getAfterLogin();
+ $afterLogin->setDisplayProfile();
+ $afterLogin->changeLanguage();
+
+ $this->regenerateSessionId();
+ $this->updateBasket();
+
+ return "admin_start";
+ } catch (UserException $oEx) {
+ $myUtilsView->addErrorToDisplay('LOGIN_ERROR');
+ } catch (CookieException $oEx) {
+ $myUtilsView->addErrorToDisplay('LOGIN_NO_COOKIE_SUPPORT');
+ } catch (WebauthnException $e) {
+ $myUtilsView->addErrorToDisplay($e);
+ $this->d3GetLoggerObject()->error($e->getDetailedErrorMessage(), ['UserId' => $userId]);
+ $this->d3GetLoggerObject()->debug($e->getTraceAsString());
+ }
+
+ $user->logout();
+ $oStr = Str::getStr();
+ $this->d3GetConfig()->getActiveView()->addTplParam('user', $oStr->htmlspecialchars($userId));
+ $this->d3GetConfig()->getActiveView()->addTplParam('profile', $oStr->htmlspecialchars($selectedProfile));
+
+ return 'login';
+ }
+
+ /**
+ * @throws WebauthnGetException
+ */
+ public function handleErrorMessage()
+ {
+ $error = $this->getErrorMsg();
+
+ if (strlen((string)$error)) {
+ /** @var WebauthnGetException $e */
+ $e = oxNew(WebauthnGetException::class, $error);
+ throw $e;
+ }
+ }
+
+ /**
+ * @throws WebauthnGetException|WebauthnException
+ */
+ public function assertAuthn(): void
+ {
+ $credential = $this->getCredential();
+ $webAuthn = $this->d3GetWebauthnObject();
+ $webAuthn->assertAuthn($credential);
+ }
+
+ /**
+ * @param $userId
+ * @return Session
+ */
+ public function setAdminSession($userId): Session
+ {
+ $session = $this->d3GetSession();
+ $adminProfiles = $session->getVariable("aAdminProfiles");
+ $session->initNewSession();
+ $session->setVariable("aAdminProfiles", $adminProfiles);
+ $session->setVariable(WebauthnConf::OXID_ADMIN_AUTH, $userId);
+ return $session;
+ }
+
+ /**
+ * @param User $user
+ * @return void
+ */
+ public function setSessionCookie(User $user)
+ {
+ if ($this->d3GetConfig()->getConfigParam('blShowRememberMe')) {
+ $this->getUtilsServer()->setUserCookie(
+ $user->getFieldData('oxusername'),
+ $user->getFieldData('oxpassword'),
+ $this->d3GetConfig()->getShopId()
+ );
+ }
+ }
+
+ /**
+ * @param $userId
+ * @param bool $isBackend
+ * @return User
+ * @throws UserException
+ */
+ public function assertUser($userId, bool $isBackend = false): User
+ {
+ $user = $this->d3GetUserObject();
+ $user->load($userId);
+ if (!$user->isLoaded() ||
+ ($isBackend && $user->getFieldData('oxrights') === 'user')
+ ) {
+ /** @var UserException $exc */
+ $exc = oxNew(UserException::class, 'ERROR_MESSAGE_USER_NOVALIDLOGIN');
+ throw $exc;
+ }
+
+ return $user;
+ }
+
+ /**
+ * @return void
+ * @throws CookieException
+ */
+ public function handleBackendCookie(): void
+ {
+ $cookie = $this->getUtilsServer()->getOxCookie();
+ if ($cookie === null) {
+ /** @var CookieException $exc */
+ $exc = oxNew(CookieException::class, 'ERROR_MESSAGE_COOKIE_NOCOOKIE');
+ throw $exc;
+ }
+ }
+
+ /**
+ * @param User $user
+ * @param Session $session
+ * @return void
+ */
+ public function handleBackendSubshopRights(User $user, Session $session): void
+ {
+ $iSubshop = (int)$user->getFieldData('oxrights');
+
+ if ($iSubshop) {
+ $session->setVariable("shp", $iSubshop);
+ $session->setVariable('currentadminshop', $iSubshop);
+ $this->d3GetConfig()->setShopId($iSubshop);
+ }
+ }
+
+ /**
+ * @return void
+ */
+ public function regenerateSessionId(): void
+ {
+ $oSession = $this->d3GetSession();
+ if ($oSession->isSessionStarted()) {
+ $oSession->regenerateSessionId();
+ }
+ }
+
+ public function handleBlockedUser(User $user)
+ {
+ // this user is blocked, deny him
+ if ($user->inGroup('oxidblocked')) {
+ $sUrl = $this->d3GetConfig()->getShopHomeUrl() . 'cl=content&tpl=user_blocked.tpl';
+ $this->d3GetUtilsObject()->redirect($sUrl, true, 302);
+ }
+ }
+
+ /**
+ * @return void
+ */
+ public function updateBasket(): void
+ {
+ if ($oBasket = $this->d3GetSession()->getBasket()) {
+ $oBasket->onUpdate();
+ }
+ }
+
+ /**
+ * @return SystemEventHandler
+ */
+ public function d3WebauthnGetEventHandler(): SystemEventHandler
+ {
+ return oxNew(SystemEventHandler::class);
+ }
+
+ /**
+ * @return WebauthnAfterLogin
+ */
+ public function getAfterLogin(): WebauthnAfterLogin
+ {
+ return oxNew(WebauthnAfterLogin::class);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isAdmin(): bool
+ {
+ return isAdmin();
+ }
+
+ /**
+ * @return string
+ */
+ public function getUserId(): string
+ {
+ return $this->isAdmin() ?
+ $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_ADMIN_SESSION_CURRENTUSER) :
+ $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER);
+ }
+
+ /**
+ * @return Config
+ */
+ public function d3GetConfig(): Config
+ {
+ return Registry::getConfig();
+ }
+
+ /**
+ * @return UtilsServer
+ */
+ public function getUtilsServer(): UtilsServer
+ {
+ return Registry::getUtilsServer();
+ }
+
+ /**
+ * @param User $user
+ * @return void
+ * @throws WebauthnGetException
+ */
+ public function setFrontendSession(User $user): void
+ {
+ $this->d3GetSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH, $this->getCredential());
+ $this->d3GetSession()->setVariable(WebauthnConf::OXID_FRONTEND_AUTH, $user->getId());
+ }
+}
\ No newline at end of file
diff --git a/src/Application/views/tpl/d3webauthnlogin.tpl b/src/Application/views/tpl/d3webauthnlogin.tpl
index 6f61453..563fecc 100755
--- a/src/Application/views/tpl/d3webauthnlogin.tpl
+++ b/src/Application/views/tpl/d3webauthnlogin.tpl
@@ -25,7 +25,7 @@
[{$oViewConf->getHiddenSid()}]
-
+
[{$navFormParams}]