cleanup + improve code

This commit is contained in:
Daniel Seifert 2022-10-31 00:11:06 +01:00
parent 048816012c
commit 7088042cab
Signed by: DanielS
GPG Key ID: 6A513E13AEE66170
27 changed files with 286 additions and 739 deletions

View File

@ -19,6 +19,7 @@ use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList;
use D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\WebauthnErrors;
use D3\Webauthn\Application\Model\WebauthnException;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Exception;
use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
@ -94,16 +95,21 @@ class d3user_webauthn extends AdminDetailsController
public function setAuthnRegister()
{
$authn = oxNew(Webauthn::class);
try {
$authn = oxNew(Webauthn::class);
$user = $this->getUserObject();
$user->load($this->getEditObjectId());
$publicKeyCredentialCreationOptions = $authn->getCreationOptions($user);
$user = $this->getUserObject();
$user->load($this->getEditObjectId());
$publicKeyCredentialCreationOptions = $authn->getCreationOptions($user);
$this->addTplParam(
'webauthn_publickey_create',
$publicKeyCredentialCreationOptions
);
} catch (WebauthnException $e) {
// ToDo: log exc message
}
$this->addTplParam(
'webauthn_publickey_create',
$publicKeyCredentialCreationOptions
);
$this->addTplParam('isAdmin', isAdmin());
$this->addTplParam('keyname', Registry::getRequest()->getRequestEscapedParameter('credenialname'));
}

View File

@ -15,9 +15,11 @@
namespace D3\Webauthn\Application\Controller\Admin;
use Assert\AssertionFailedException;
use D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\WebauthnConf;
use D3\Webauthn\Application\Model\WebauthnErrors;
use D3\Webauthn\Application\Model\WebauthnException;
use D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Exception;
@ -67,12 +69,17 @@ class d3webauthnadminlogin extends AdminController
public function generateCredentialRequest()
{
/** @var Webauthn $webauthn */
$webauthn = oxNew(Webauthn::class);
$publicKeyCredentialRequestOptions = $webauthn->getRequestOptions();
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_LOGIN_OBJECT, $publicKeyCredentialRequestOptions);
$this->addTplParam('webauthn_publickey_login', $publicKeyCredentialRequestOptions);
$this->addTplParam('isAdmin', isAdmin());
try {
/** @var Webauthn $webauthn */
$webauthn = oxNew(Webauthn::class);
$userId = Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER);
$publicKeyCredentialRequestOptions = $webauthn->getRequestOptions($userId);
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_LOGIN_OBJECT, $publicKeyCredentialRequestOptions);
$this->addTplParam('webauthn_publickey_login', $publicKeyCredentialRequestOptions);
$this->addTplParam('isAdmin', isAdmin());
} catch (WebauthnException $e) {
// ToDo write exc message to display and log
}
}
public function assertAuthn()
@ -92,15 +99,16 @@ class d3webauthnadminlogin extends AdminController
if (strlen(Registry::getRequest()->getRequestEscapedParameter('credential'))) {
$credential = Registry::getRequest()->getRequestEscapedParameter('credential');
$webAuthn = oxNew(Webauthn::class);
$userId = Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER);
$webAuthn->assertAuthn($credential);
$user->load(Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER));
$user->load($userId);
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH, true);
/** @var d3_webauthn_UserComponent $userCmp */
$loginController = oxNew(LoginController::class);
return $loginController->checklogin();
}
} catch (Exception $e) {
} catch (AssertionFailedException|WebauthnException $e) {
Registry::getUtilsView()->addErrorToDisplay($e->getMessage());
$user->logout();

View File

@ -19,6 +19,7 @@ use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList;
use D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\WebauthnErrors;
use D3\Webauthn\Application\Model\WebauthnException;
use OxidEsales\Eshop\Application\Controller\AccountController;
use OxidEsales\Eshop\Core\Registry;
@ -69,13 +70,18 @@ class d3_account_webauthn extends AccountController
public function setAuthnRegister()
{
$authn = oxNew(Webauthn::class);
$publicKeyCredentialCreationOptions = $authn->getCreationOptions($this->getUser());
try {
$authn = oxNew(Webauthn::class);
$publicKeyCredentialCreationOptions = $authn->getCreationOptions($this->getUser());
$this->addTplParam(
'webauthn_publickey_create',
$publicKeyCredentialCreationOptions
);
} catch (WebauthnException $e) {
// ToDo: add exc msg to display and log
}
$this->addTplParam(
'webauthn_publickey_create',
$publicKeyCredentialCreationOptions
);
$this->addTplParam('isAdmin', isAdmin());
$this->addTplParam('keyname', Registry::getRequest()->getRequestEscapedParameter('credenialname'));
}

View File

@ -15,12 +15,14 @@
namespace D3\Webauthn\Application\Controller;
use Assert\AssertionFailedException;
use D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\WebauthnConf;
use D3\Webauthn\Application\Model\WebauthnErrors;
use D3\Webauthn\Application\Model\WebauthnException;
use D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Exception;
use Doctrine\DBAL\Exception as DoctrineException;
use OxidEsales\Eshop\Application\Controller\FrontendController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
@ -28,6 +30,8 @@ use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Utils;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class d3webauthnlogin extends FrontendController
{
@ -58,12 +62,25 @@ class d3webauthnlogin extends FrontendController
return parent::render();
}
/**
* @return void
* @throws \Doctrine\DBAL\Driver\Exception
* @throws DoctrineException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function generateCredentialRequest()
{
/** @var Webauthn $webauthn */
$webauthn = oxNew(Webauthn::class);
$publicKeyCredentialRequestOptions = $webauthn->getRequestOptions();
$this->addTplParam('webauthn_publickey_login', $publicKeyCredentialRequestOptions);
try {
/** @var Webauthn $webauthn */
$webauthn = oxNew(Webauthn::class);
$userId = Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER);
$publicKeyCredentialRequestOptions = $webauthn->getRequestOptions($userId);
$this->addTplParam('webauthn_publickey_login', $publicKeyCredentialRequestOptions);
} catch (WebauthnException $e) {
// ToDo: write exc msg to display and log
}
$this->addTplParam('isAdmin', isAdmin());
}
@ -92,7 +109,7 @@ class d3webauthnlogin extends FrontendController
$userCmp->d3WebauthnRelogin($user, $credential);
}
} catch (Exception $e) {
} catch (AssertionFailedException|WebauthnException $e) {
Registry::getUtilsView()->addErrorToDisplay($e->getMessage());
$user->logout();

View File

@ -18,11 +18,16 @@
namespace D3\Webauthn\Application\Model\Credential;
use DateTime;
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
use Doctrine\DBAL\Exception as DoctrineException;
use Doctrine\DBAL\Query\QueryBuilder;
use Exception;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Webauthn\PublicKeyCredentialSource;
class PublicKeyCredential extends BaseModel
@ -84,11 +89,18 @@ class PublicKeyCredential extends BaseModel
/**
* @param PublicKeyCredentialSource $publicKeyCredentialSource
* @param string|null $keyName
* @return void
* @throws \Exception
* @throws Exception
*/
public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $keyName = null): void
{
if ((oxNew(PublicKeyCredentialList::class))
->findOneByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId())
) {
return;
}
// will save on every successfully assertion, set id to prevent duplicated database entries
$id = $this->getIdByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId());
@ -104,6 +116,14 @@ class PublicKeyCredential extends BaseModel
$this->save();
}
/**
* @param string $publicKeyCredentialId
* @return string|null
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getIdByCredentialId(string $publicKeyCredentialId): ?string
{
/** @var QueryBuilder $qb */

View File

@ -17,13 +17,16 @@
namespace D3\Webauthn\Application\Model\Credential;
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
use Doctrine\DBAL\Exception as DoctrineException;
use Doctrine\DBAL\Query\QueryBuilder;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Model\ListModel;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
use phpDocumentor\Reflection\Types\This;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialSourceRepository;
use Webauthn\PublicKeyCredentialUserEntity;
@ -37,6 +40,14 @@ class PublicKeyCredentialList extends ListModel implements PublicKeyCredentialSo
parent::__construct(PublicKeyCredential::class);
}
/**
* @param string $publicKeyCredentialId
* @return PublicKeyCredentialSource|null
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource
{
/** @var QueryBuilder $qb */
@ -66,6 +77,14 @@ class PublicKeyCredentialList extends ListModel implements PublicKeyCredentialSo
return $credential instanceof PublicKeyCredentialSource ? $credential : null;
}
/**
* @param PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity
* @return array|PublicKeyCredentialSource[]
* @throws ContainerExceptionInterface
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws NotFoundExceptionInterface
*/
public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array
{
/** @var QueryBuilder $qb */
@ -91,7 +110,15 @@ class PublicKeyCredentialList extends ListModel implements PublicKeyCredentialSo
}, $qb->execute()->fetchAllAssociative());
}
public function getAllFromUser(User $user)
/**
* @param User $user
* @return $this
* @throws ContainerExceptionInterface
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws NotFoundExceptionInterface
*/
public function getAllFromUser(User $user): PublicKeyCredentialList
{
if (!$user->isLoaded()) {
return $this;

View File

@ -17,18 +17,18 @@
declare(strict_types=1);
namespace D3\Webauthn\Application\Model\Webauthn;
namespace D3\Webauthn\Application\Model;
use OxidEsales\Eshop\Application\Model\Shop;
use OxidEsales\Eshop\Core\Registry;
use Webauthn\PublicKeyCredentialRpEntity;
class d3PublicKeyCredentialRpEntity extends PublicKeyCredentialRpEntity
class RelyingPartyEntity extends PublicKeyCredentialRpEntity
{
public function __construct(Shop $shop)
public function __construct()
{
parent::__construct(
$shop->getFieldData('oxname'),
$_SERVER['HTTP_HOST']
Registry::getConfig()->getActiveShop()->getFieldData('oxname'),
preg_replace('/(^www\.)(.*)/mi', '$2', $_SERVER['HTTP_HOST'])
);
}
}

View File

@ -15,19 +15,27 @@
* @link http://www.oxidmodule.com
*/
namespace D3\Webauthn\Application\Model\Webauthn;
namespace D3\Webauthn\Application\Model;
use OxidEsales\Eshop\Application\Model\User;
use Webauthn\PublicKeyCredentialUserEntity;
class d3PublicKeyCredentialUserEntity extends publicKeyCredentialUserEntity
class UserEntity extends publicKeyCredentialUserEntity
{
/**
* @param User $user
* @throws WebauthnException
*/
public function __construct(User $user)
{
if (!$user->isLoaded() || !$user->getId()) {
throw oxNew(WebauthnException::class, 'can not create webauthn user entity from not loaded user');
}
parent::__construct(
strtolower($user->getFieldData('oxfname').'.'.$user->getFieldData('oxlname')),
strtolower($user->getFieldData('oxusername')),
$user->getId(),
$user->getFieldData('oxfname').', '.$user->getFieldData('oxlname')
$user->getFieldData('oxfname') . ' ' . $user->getFieldData('oxlname')
);
}
}

View File

@ -4,16 +4,21 @@ declare(strict_types=1);
namespace D3\Webauthn\Application\Model;
use Assert\AssertionFailedException;
use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
use Doctrine\DBAL\Exception as DoctrineException;
use Exception;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7Server\ServerRequestCreator;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Registry;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialRequestOptions;
use Webauthn\PublicKeyCredentialRpEntity;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\Server;
@ -22,7 +27,7 @@ class Webauthn
public const SESSION_CREATIONS_OPTIONS = 'd3WebAuthnCreationOptions';
public const SESSION_ASSERTION_OPTIONS = 'd3WebAuthnAssertionOptions';
public function isAvailable()
public function isAvailable(): bool
{
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' || // is HTTPS
!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ||
@ -40,12 +45,16 @@ class Webauthn
}
/**
* @param User $user
* @return false|string
* @throws ContainerExceptionInterface
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws NotFoundExceptionInterface
*/
public function getCreationOptions(User $user)
{
/** @var d3_User_Webauthn $user */
$userEntity = $user->d3GetWebauthnUserEntity();
$userEntity = oxNew(UserEntity::class, $user);
/** @var PublicKeyCredentialList $credentialSourceRepository */
$credentialSourceRepository = oxNew(PublicKeyCredentialList::class);
@ -66,12 +75,19 @@ class Webauthn
return json_encode($publicKeyCredentialCreationOptions,JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
public function getRequestOptions()
/**
* @return false|string
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function getRequestOptions(string $userId)
{
/** @var d3_User_Webauthn $user */
$user = oxNew(User::class);
$user->load('oxdefaultadmin');
$userEntity = $user->d3GetWebauthnUserEntity();
$user->load($userId);
$userEntity = oxNew(UserEntity::class, $user);
// Get the list of authenticators associated to the user
$credentialList = oxNew(PublicKeyCredentialList::class);
@ -98,14 +114,10 @@ class Webauthn
/**
* @return Server
*/
public function getServer()
public function getServer(): Server
{
$rpEntity = new PublicKeyCredentialRpEntity(
Registry::getConfig()->getActiveShop()->getFieldData('oxname'),
preg_replace('/(^www\.)(.*)/mi', '$2', $_SERVER['HTTP_HOST'])
);
return new Server($rpEntity, oxNew(PublicKeyCredentialList::class));
$rpEntity = oxNew(RelyingPartyEntity::class);
return oxNew(Server::class, $rpEntity, oxNew(PublicKeyCredentialList::class));
}
public function saveAuthn(string $credential, string $keyName = null)
@ -128,15 +140,18 @@ class Webauthn
$pkCredential = oxNew(PublicKeyCredential::class);
$pkCredential->saveCredentialSource($publicKeyCredentialSource, $keyName);
} catch (\Exception $e) {
dumpvar($e->getMessage());
dumpvar($e);
die();
} catch (Exception $e) {
// ToDo: write exc msg to display and log
}
}
public function assertAuthn(string $response)
/**
* @param string $response
* @return bool
* @throws AssertionFailedException
* @throws WebauthnException
*/
public function assertAuthn(string $response): bool
{
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
@ -147,10 +162,9 @@ class Webauthn
);
$serverRequest = $creator->fromGlobals();
/** @var d3_User_Webauthn $user */
$user = oxNew(User::class);
$user->load('oxdefaultadmin');
$userEntity = $user->d3GetWebauthnUserEntity();
$user->load(Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER));
$userEntity = oxNew(UserEntity::class, $user);
$this->getServer()->loadAndCheckAssertionResponse(
html_entity_decode($response),
@ -165,6 +179,10 @@ class Webauthn
/**
* @param $userId
* @return bool
* @throws ContainerExceptionInterface
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws NotFoundExceptionInterface
*/
public function isActive($userId): bool
{
@ -175,15 +193,19 @@ class Webauthn
/**
* @param $userId
* @return bool
* @throws ContainerExceptionInterface
* @throws DoctrineDriverException
* @throws DoctrineException
* @throws NotFoundExceptionInterface
*/
public function UserUseWebauthn($userId): bool
{
/** @var d3_User_Webauthn $user */
$user = oxNew(User::class);
$user->load($userId);
$entity = $user->d3GetWebauthnUserEntity();
$credentionList = oxNew(PublicKeyCredentialList::class);
$list = $credentionList->findAllForUserEntity($entity);
$entity = oxNew(UserEntity::class, $user);
$credentialList = oxNew(PublicKeyCredentialList::class);
$list = $credentialList->findAllForUserEntity($entity);
return is_array($list) && count($list);
}

View File

@ -1,76 +0,0 @@
<?php
/**
* This Software is the property of Data Development and is protected
* by copyright law - it is NOT Freeware.
*
* Any unauthorized use of this software without a valid license
* is a violation of the license agreement and will be prosecuted by
* civil and criminal law.
*
* http://www.shopmodule.com
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
* @link http://www.oxidmodule.com
*/
namespace D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\Credential\publicKeyCredential;
use Webauthn\PublicKeyCredentialSource;
/** @deprecated */
class d3PublicKeyCredentialSource extends PublicKeyCredentialSource
{
/**
* @throws \Exception
*/
public function saveCredential()
{
$credential = oxNew(publicKeyCredential::class);
$credential->d3SetName(date('Y-m-d H:i:s'));
$credential->d3SetCredentialId($this->getPublicKeyCredentialId());
$credential->d3SetType($this->getType());
$credential->d3SetTransports($this->getTransports());
$credential->d3SetAttestationType($this->getAttestationType());
$credential->d3SetTrustPath($this->getTrustPath());
$credential->d3SetAaguid($this->getAaguid());
$credential->d3SetPublicKey($this->getCredentialPublicKey());
$credential->d3SetUserHandle($this->getUserHandle());
$credential->d3SetCounter($this->getCounter());
$credential->save();
}
public static function createFromd3PublicKeyCredential(publicKeyCredential $publicKeyCredential): self
{
return new self(
$publicKeyCredential->d3GetCredentialId(),
$publicKeyCredential->d3GetType(),
$publicKeyCredential->d3GetTransports(),
$publicKeyCredential->d3GetAttestationType(),
$publicKeyCredential->d3GetTrustPath(),
$publicKeyCredential->d3GetAaguid(),
$publicKeyCredential->d3GetPublicKey(),
$publicKeyCredential->d3GetUserHandle(),
$publicKeyCredential->d3GetCounter()
);
}
public static function createFromPublicKeyCredentialSource(publicKeyCredentialSource $publicKeyCredential): self
{
return new self(
$publicKeyCredential->getPublicKeyCredentialId(),
$publicKeyCredential->getType(),
$publicKeyCredential->getTransports(),
$publicKeyCredential->getAttestationType(),
$publicKeyCredential->getTrustPath(),
$publicKeyCredential->getAaguid(),
$publicKeyCredential->getCredentialPublicKey(),
$publicKeyCredential->getUserHandle(),
$publicKeyCredential->getCounter()
);
}
}

View File

@ -1,92 +0,0 @@
<?php
/**
* This Software is the property of Data Development and is protected
* by copyright law - it is NOT Freeware.
*
* Any unauthorized use of this software without a valid license
* is a violation of the license agreement and will be prosecuted by
* civil and criminal law.
*
* http://www.shopmodule.com
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
* @link http://www.oxidmodule.com
*/
/**
* @deprecated
*/
declare(strict_types=1);
namespace D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\Credential\PublicKeyCredential;
use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList;
use Exception;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialSourceRepository;
use Webauthn\PublicKeyCredentialUserEntity;
class d3PublicKeyCredentialSourceRepository implements PublicKeyCredentialSourceRepository
{
/**
* @param string $publicKeyCredentialId
* @return PublicKeyCredentialSource|null
* @throws DatabaseConnectionException
*/
public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource
{
if (Registry::getRequest()->getRequestEscapedParameter('fnc') == 'checkregister') {
return null;
}
$credential = oxNew(publicKeyCredential::class);
$credential->loadByCredentialId($publicKeyCredentialId);
return $credential->getId() ?
d3PublicKeyCredentialSource::createFromd3PublicKeyCredential($credential) :
null;
}
/**
* @param PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity
* @return array
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array
{
$sourceList = [];
$credentialList = oxNew(PublicKeyCredentialList::class);
$credentialList->loadAllForUserEntity($publicKeyCredentialUserEntity);
/** @var publicKeyCredential $credential */
foreach ($credentialList->getArray() as $credential) {
$sourceList[$credential->getId()] = d3PublicKeyCredentialSource::createFromd3PublicKeyCredential($credential);
};
return $sourceList;
}
/**
* @param PublicKeyCredentialSource $publicKeyCredentialSource
* @throws Exception
*/
public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource): void
{
$publicKeyCredentialSource = d3PublicKeyCredentialSource::createFromPublicKeyCredentialSource($publicKeyCredentialSource);
if ($this->findOneByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId())) {
// increase counter
} else {
$publicKeyCredentialSource->saveCredential();
}
}
}

View File

@ -17,10 +17,10 @@ namespace D3\Webauthn\Application\Model;
class WebauthnConf
{
const WEBAUTHN_SESSION_AUTH = 'webauthn_auth'; // has valid webauthn, user is logged in completly
const WEBAUTHN_LOGIN_OBJECT = 'authnloginobject'; // webauthn register options, required for credential check
const WEBAUTHN_SESSION_CURRENTUSER = 'd3webauthnCurrentUser'; // oxid assigned to user from entered username
const WEBAUTHN_SESSION_LOGINUSER = 'd3webauthnLoginUser'; // username entered in login form
const WEBAUTHN_SESSION_CURRENTCLASS = 'd3webauthnCurrentClass'; // no usage
const WEBAUTHN_SESSION_NAVFORMPARAMS = 'd3webauthnNavFormParams'; // no usage
public const WEBAUTHN_SESSION_AUTH = 'webauthn_auth'; // has valid webauthn, user is logged in completly
public const WEBAUTHN_LOGIN_OBJECT = 'authnloginobject'; // webauthn register options, required for credential check
public const WEBAUTHN_SESSION_CURRENTUSER = 'd3webauthnCurrentUser'; // oxid assigned to user from entered username
public const WEBAUTHN_SESSION_LOGINUSER = 'd3webauthnLoginUser'; // username entered in login form
public const WEBAUTHN_SESSION_CURRENTCLASS = 'd3webauthnCurrentClass'; // no usage
public const WEBAUTHN_SESSION_NAVFORMPARAMS = 'd3webauthnNavFormParams'; // no usage
}

View File

@ -0,0 +1,10 @@
<?php
namespace D3\Webauthn\Application\Model;
use OxidEsales\Eshop\Core\Exception\StandardException;
class WebauthnException extends StandardException
{
}

View File

@ -17,6 +17,12 @@ $aLang = array(
'WEBAUTHN_INPUT_HELP' => 'Bitte mit Hardwareschlüssel authentisieren.',
'WEBAUTHN_CANCEL_LOGIN' => 'Anmeldung abbrechen',
'D3_WEBAUTHN_BREADCRUMB' => 'Passwortloses Anmelden',
'D3_WEBAUTHN_CONF_BROWSER_REQUEST' => 'Bitte die Anfrage des Browsers bestätigen:',
'D3_WEBAUTHN_CANCEL' => 'Abbrechen',
'D3_WEBAUTHN_DELETE' => 'Löschen',
'D3_WEBAUTHN_DELETE_CONFIRM' => 'Soll der Schlüssel wirklich gelöscht werden?',
'D3_WEBAUTHN_KEYNAME' => 'Name des Schlüssels',
'D3_WEBAUTHN_NOKEYREGISTERED' => 'kein Schlüssel registriert',
'D3_WEBAUTHN_ACCOUNT_TYPE0' => 'nur Passwort',
'D3_WEBAUTHN_ACCOUNT_TYPE1' => 'nur Auth-Stick',

View File

@ -1,85 +0,0 @@
[{if $request_webauthn}]
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="checklogin">
<input type="hidden" name="cl" value="login">
<input type="hidden" id="keyauth" name="keyauth" value="">
[{if $Errors.default|@count}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
<div class="d3webauthn_icon">
<div class="svg-container">
[{include file=$oViewConf->getModulePath('d3webauthn', 'out/img/fingerprint.svg')}]
</div>
<div class="message">[{oxmultilang ident="WEBAUTHN_INPUT_HELP"}]</div>
</div>
[{* prevent cancel button (1st button) action when form is sent via Enter key *}]
<input type="submit" style="display:none !important;">
<input class="btn btn_cancel" value="[{oxmultilang ident="WEBAUTHN_CANCEL_LOGIN"}]" type="submit"
onclick="document.getElementById('login').fnc.value='d3WebauthnCancelLogin'; document.getElementById('login').submit();"
>
[{capture name="webauthn_login"}]
function arrayToBase64String(a) {
return btoa(String.fromCharCode(...a));
}
function base64url2base64(input) {
input = input
.replace(/=/g, "")
.replace(/-/g, '+')
.replace(/_/g, '/');
const pad = input.length % 4;
if(pad) {
if(pad === 1) {
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
}
input += new Array(5-pad).join('=');
}
return input;
}
let publicKey = [{$webauthn_publickey_login}];
publicKey.challenge = Uint8Array.from(window.atob(base64url2base64(publicKey.challenge)), function(c){return c.charCodeAt(0);});
if (publicKey.allowCredentials) {
publicKey.allowCredentials = publicKey.allowCredentials.map(function(data) {
data.id = Uint8Array.from(window.atob(base64url2base64(data.id)), function(c){return c.charCodeAt(0);});
return data;
});
}
navigator.credentials.get({ 'publicKey': publicKey }).then(function(data){
let publicKeyCredential = {
id: data.id,
type: data.type,
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
response: {
authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
signature: arrayToBase64String(new Uint8Array(data.response.signature)),
userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
}
};
document.getElementById('keyauth').value = btoa(JSON.stringify(publicKeyCredential));
document.getElementById('login').submit();
})
.catch(function(error){
// alert('Open your browser console!');
console.log('FAIL', error);
});
[{/capture}]
[{oxscript add=$smarty.capture.webauthn_login}]
[{oxscript}]
[{oxstyle include=$oViewConf->getModuleUrl('d3webauthn', 'out/admin/src/css/d3webauthnlogin.css')}]
[{oxstyle}]
[{else}]
[{$smarty.block.parent}]
[{/if}]

View File

@ -24,11 +24,17 @@ $aLang = [
'D3_WEBAUTHN_ERROR_MISSINGPKC' => 'Keine prüfbaren Anfrageoptionen gespeichert. Bitte führen Sie die Anmeldung noch einmal durch bzw. wenden sich an den Betreiber.',
'WEBAUTHN_INPUT_HELP' => 'Bitte mit Hardwareschlüssel authentisieren.',
'WEBAUTHN_CANCEL_LOGIN' => 'Anmeldung abbrechen',
'D3WEBAUTHN_CONF_BROWSER_REQUEST' => 'Bitte die Anfrage des Browsers bestätigen:',
'D3WEBAUTHN_CANCEL' => 'Abbrechen',
'D3WEBAUTHN_DELETE' => 'Löschen',
'D3WEBAUTHN_DELETE_CONFIRM' => 'Soll der Schlüssel wirklich gelöscht werden?',
'D3WEBAUTHN_CANCELNOKEYREGISTERED' => 'kein Schlüssel registriert',
'd3mxuser_webauthn' => 'Hardwareschlüssel',
'D3_WEBAUTHN_REGISTERNEW' => 'neue Registrierung erstellen',
'D3_WEBAUTHN_ADDKEY' => 'Sicherheitsschlüssel hinzufügen',
'D3_WEBAUTHN_KEYNAME' => 'Name des Schlüssels',
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registrierte Schlüssel',

View File

@ -24,11 +24,17 @@ $aLang = [
'D3_WEBAUTHN_ERROR_MISSINGPKC' => 'No verifiable request options saved. Please perform the registration again or contact the operator.',
'WEBAUTHN_INPUT_HELP' => 'Please authenticate with hardware key.',
'WEBAUTHN_CANCEL_LOGIN' => 'Cancel login',
'D3WEBAUTHN_CONF_BROWSER_REQUEST' => 'Please confirm the browser request:',
'D3WEBAUTHN_CANCEL' => 'Cancel',
'D3WEBAUTHN_DELETE' => 'Delete',
'D3WEBAUTHN_DELETE_CONFIRM' => 'Do you really want to delete the key?',
'D3WEBAUTHN_CANCELNOKEYREGISTERED' => 'No key registered',
'd3mxuser_webauthn' => 'hardware key',
'D3_WEBAUTHN_REGISTERNEW' => 'create new registration',
'D3_WEBAUTHN_ADDKEY' => 'add security key',
'D3_WEBAUTHN_KEYNAME' => 'Key name',
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registered keys',

View File

@ -38,7 +38,7 @@
[{capture name="javascripts"}]
function deleteItem(id) {
if (confirm('wirklich loeschen?') === true) {
if (confirm('[{oxmultilang ident="D3WEBAUTHN_DELETE_CONFIRM"}]') === true) {
document.getElementById('fncname').value = 'deleteKey';
document.getElementById('oxidvalue').value = id;
document.getElementById('myedit').submit();
@ -57,9 +57,9 @@
<div class="card">
<div class="card-body">
<p class="card-text">
Bitte die Anfrage Ihres Browsers bestätigen.
[{oxmultilang ident="D3WEBAUTHN_CONF_BROWSER_REQUEST"}]
</p>
<button onclick="document.getElementById('webauthn').submit();">Abbrechen</button>
<button onclick="document.getElementById('webauthn').submit();">[{oxmultilang ident="D3WEBAUTHN_CANCEL"}]</button>
</div>
</div>
@ -82,7 +82,9 @@
<table style="padding:0; border:0; width:98%;">
<tr>
<td></td>
<td class="errorbox">[{oxmultilang ident=$sSaveError}]</td>
<td class="errorbox">
[{oxmultilang ident=$sSaveError}]
</td>
</tr>
</table>
[{/if}]
@ -102,7 +104,9 @@
<input type="hidden" name="fnc" value="requestNewCredential">
<input type="hidden" name="oxid" value="[{$oxid}]">
[{block name="user_d3user_totp_registerform"}]
<label for="credentialname">Name des Schlüssels</label>
<label for="credentialname">
[{oxmultilang ident="D3_WEBAUTHN_KEYNAME"}]
</label>
<p class="card-text">
<input id="credentialname" type="text" name="credenialname" [{$readonly}]>
</p>
@ -132,14 +136,14 @@
[{$credential->getName()}]
<a onclick="deleteItem('[{$credential->getId()}]'); return false;" href="#" class="btn btn-danger btn-sm">
<span class="glyphicon glyphicon-pencil"></span>
delete
[{oxmultilang ident="D3WEBAUTHN_DELETE"}]
</a>
</li>
[{/foreach}]
</ul>
[{else}]
<div class="card-text">
kein Schluessel registriert
[{oxmultilang ident="D3WEBAUTHN_CANCELNOKEYREGISTERED"}]
</div>
[{/if}]
</div>

View File

@ -16,7 +16,7 @@
[{include file="js_login.tpl"}]
<form action="[{$oViewConf->getSelfLink()}]" target="_top" method="post" name="login" id="login">
<form action="[{$oViewConf->getSelfLink()}]" method="post" id="login">
[{block name="admin_login_form"}]
[{$oViewConf->getHiddenSid()}]
@ -50,7 +50,7 @@
</div>
[{oxscript}]
<script type="text/javascript">if (window != window.top) top.location.href = document.location.href;</script>
<script type="text/javascript">if (window !== window.top) top.location.href = document.location.href;</script>
</body>
</html>

View File

@ -1,95 +0,0 @@
[{if $webauthn_publickey_register}]
[{capture name="webauthn_register"}]
function arrayToBase64String(a) {
return btoa(String.fromCharCode(...a));
}
function base64url2base64(input) {
input = input
.replace(/=/g, "")
.replace(/-/g, '+')
.replace(/_/g, '/');
const pad = input.length % 4;
if(pad) {
if(pad === 1) {
throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
}
input += new Array(5-pad).join('=');
}
return input;
}
function authnregister() {
let publicKey = [{$webauthn_publickey_register}];
publicKey.challenge = Uint8Array.from(window.atob(base64url2base64(publicKey.challenge)), function(c){return c.charCodeAt(0);});
publicKey.user.id = Uint8Array.from(window.atob(publicKey.user.id), function(c){return c.charCodeAt(0);});
if (publicKey.excludeCredentials) {
publicKey.excludeCredentials = publicKey.excludeCredentials.map(function(data) {
data.id = Uint8Array.from(window.atob(base64url2base64(data.id)), function(c){return c.charCodeAt(0);});
return data;
});
}
navigator.credentials.create({ 'publicKey': publicKey })
.then(function(data){
let publicKeyCredential = {
id: data.id,
type: data.type,
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
response: {
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
attestationObject: arrayToBase64String(new Uint8Array(data.response.attestationObject))
}
};
window.location = 'index.php?cl=start&fnc=checkregister&authn='+btoa(JSON.stringify(publicKeyCredential));
}).catch(function(error){
//alert('Open your browser console!');
console.log('FAIL', error);
}
);
}
[{/capture}]
[{oxscript add=$smarty.capture.webauthn_register}]
<button onclick="authnregister();">Fido2 Register</button>
[{capture name="webauthn_login"}]
function authnlogin() {
let publicKey = [{$webauthn_publickey_login}];
publicKey.challenge = Uint8Array.from(window.atob(base64url2base64(publicKey.challenge)), function(c){return c.charCodeAt(0);});
if (publicKey.allowCredentials) {
publicKey.allowCredentials = publicKey.allowCredentials.map(function(data) {
data.id = Uint8Array.from(window.atob(base64url2base64(data.id)), function(c){return c.charCodeAt(0);});
return data;
});
}
navigator.credentials.get({ 'publicKey': publicKey })
.then(function(data){
let publicKeyCredential = {
id: data.id,
type: data.type,
rawId: arrayToBase64String(new Uint8Array(data.rawId)),
response: {
authenticatorData: arrayToBase64String(new Uint8Array(data.response.authenticatorData)),
clientDataJSON: arrayToBase64String(new Uint8Array(data.response.clientDataJSON)),
signature: arrayToBase64String(new Uint8Array(data.response.signature)),
userHandle: data.response.userHandle ? arrayToBase64String(new Uint8Array(data.response.userHandle)) : null
}
};
window.location = 'index.php?cl=start&fnc=checklogin&authn='+btoa(JSON.stringify(publicKeyCredential));
})
.catch(function(error){
// alert('Open your browser console!');
console.log('FAIL', error);
});
}
[{/capture}]
[{oxscript add=$smarty.capture.webauthn_login}]
<button onclick="authnlogin();">Fido2 Login</button>
[{/if}]
[{$smarty.block.parent}]

View File

@ -0,0 +1,4 @@
[{$smarty.block.parent}]
<li>
<a href="[{oxgetseourl ident=$oViewConf->getSslSelfLink()|cat:"cl=d3_account_webauthn"}]">[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT"}]</a>
</li>

View File

@ -2,7 +2,7 @@
[{capture name="javascripts"}]
function deleteItem(id) {
if (confirm('wirklich loeschen?') === true) {
if (confirm('[{oxmultilang ident="D3_WEBAUTHN_DELETE_CONFIRM"}]') === true) {
document.getElementById('fncname').value = 'deleteKey';
document.getElementById('oxidvalue').value = id;
document.getElementById('actionform').submit();
@ -34,9 +34,9 @@
<div class="card">
<div class="card-body">
<p class="card-text">
Bitte die Anfrage Ihres Browsers bestätigen.
[{oxmultilang ident="D3_WEBAUTHN_CONF_BROWSER_REQUEST"}]
</p>
<button onclick="document.getElementById('webauthn').submit();">Abbrechen</button>
<button onclick="document.getElementById('webauthn').submit();">[{oxmultilang ident="D3_WEBAUTHN_CANCEL"}]</button>
</div>
</div>
@ -72,7 +72,7 @@
<input type="hidden" name="fnc" value="requestNewCredential">
<input type="hidden" name="oxid" value="[{$oxid}]">
[{block name="user_d3user_totp_registerform"}]
<label for="credentialname">Name des Schlüssels</label>
<label for="credentialname">[{oxmultilang ident="D3_WEBAUTHN_KEYNAME"}]</label>
<p class="card-text">
<input id="credentialname" type="text" name="credenialname" [{$readonly}]>
</p>
@ -102,14 +102,14 @@
[{$credential->getName()}]
<a onclick="deleteItem('[{$credential->getId()}]'); return false;" href="#" class="btn btn-danger btn-sm">
<span class="glyphicon glyphicon-pencil"></span>
delete
[{oxmultilang ident="D3_WEBAUTHN_DELETE"}]
</a>
</li>
[{/foreach}]
</ul>
[{else}]
<div class="card-text">
kein Schluessel registriert
[{oxmultilang ident="D3_WEBAUTHN_NOKEYREGISTERED"}]
</div>
[{/if}]
</div>

View File

@ -15,16 +15,14 @@
namespace D3\Webauthn\Modules\Application\Component;
use Assert\AssertionFailedException;
use D3\Webauthn\Application\Model\WebauthnConf;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnMissingPublicKeyCredentialRequestOptions;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnWrongAuthException;
use D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Doctrine\DBAL\DBALException;
use D3\Webauthn\Application\Model\WebauthnException;
use Doctrine\DBAL\Driver\Exception as DoctrineDriverException;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Query\QueryBuilder;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\UtilsView;
@ -37,8 +35,10 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
{
/**
* @return string|void
* @throws DBALException
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws DoctrineDriverException
*/
public function login_noredirect()
{
@ -49,12 +49,15 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
$webauthn = $this->d3GetWebauthnObject();
if ($webauthn->isActive($userId)
&& false == Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)
&& !Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)
) {
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
$this->getParent()->getClassKey() != 'd3webauthnlogin' ? $this->getParent()->getClassKey() : 'start');
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $userId);
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER,
$userId
);
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS,
$this->getParent()->getViewConfig()->getNavFormParams()
@ -67,47 +70,18 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
}
parent::login_noredirect();
/** @var d3_User_Webauthn $user */
/*
$oUser = $this->getUser();
if ($oUser && $oUser->getId()) {
$webauthn = $this->d3GetWebauthnObject();
$webauthn->loadByUserId($oUser->getId());
if ($webauthn->isActive()
&& false == Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)
) {
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
$this->getParent()->getClassKey() != 'd3webauthnlogin' ? $this->getParent()->getClassKey() : 'start');
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $oUser->getId());
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS,
$this->getParent()->getViewConfig()->getNavFormParams()
);
$oUser->d3templogout();
return "d3webauthnlogin";
}
}
*/
}
/**
* @return Webauthn
*/
public function d3GetWebauthnObject()
public function d3GetWebauthnObject(): Webauthn
{
return oxNew(Webauthn::class);
}
/**
* @return bool|string
* @throws DatabaseConnectionException
* @throws d3webauthnMissingPublicKeyCredentialRequestOptions
*/
public function checkWebauthnlogin()
{
@ -119,15 +93,11 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
$webauthn = $this->d3GetWebauthnObject();
try {
if (false == $this->isNoWebauthnOrNoLogin($webauthn, $userId) && $this->hasValidWebauthn($sWebauth, $webauthn)) {
$this->d3WebauthnRelogin($oUser, $sWebauth);
$this->d3WebauthnClearSessionVariables();
if (!$this->isNoWebauthnOrNoLogin($webauthn, $userId) && $this->hasValidWebauthn($sWebauth, $webauthn)) {
$this->d3WebauthnRelogin($oUser, $sWebauth);
$this->d3WebauthnClearSessionVariables();
return false;
}
} catch (d3webauthnWrongAuthException $oEx) {
$this->d3GetUtilsView()->addErrorToDisplay($oEx, false, false, "", 'd3webauthnlogin');
return false;
}
return 'd3webauthnlogin';
@ -136,7 +106,7 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
/**
* @return UtilsView
*/
public function d3GetUtilsView()
public function d3GetUtilsView(): UtilsView
{
return Registry::getUtilsView();
}
@ -150,9 +120,14 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
/**
* @param Webauthn $webauthn
* @param $userId
* @return bool
* @throws ContainerExceptionInterface
* @throws DoctrineDriverException
* @throws Exception
* @throws NotFoundExceptionInterface
*/
public function isNoWebauthnOrNoLogin($webauthn, $userId)
public function isNoWebauthnOrNoLogin(Webauthn $webauthn, $userId): bool
{
return false == $this->d3GetSession()->getVariable("auth")
|| false == $webauthn->isActive($userId);
@ -162,17 +137,15 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
* @param string $sWebauth
* @param Webauthn $webauthn
* @return bool
* @throws d3webauthnMissingPublicKeyCredentialRequestOptions
* @throws d3webauthnWrongAuthException
*/
public function hasValidWebauthn($sWebauth, $webauthn): bool
public function hasValidWebauthn(string $sWebauth, Webauthn $webauthn): bool
{
try {
return Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH) ||
(
$sWebauth && $webauthn->assertAuthn($sWebauth)
);
} catch (\Exception $e) {
} catch (AssertionFailedException|WebauthnException $e) {
return false;
}
}
@ -195,19 +168,20 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
$this->d3GetSession()->deleteVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS);
$this->d3GetSession()->deleteVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER);
$this->d3GetSession()->deleteVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS);
$this->d3GetSession()->deleteVariable(WebauthnConf::WEBAUTHN_LOGIN_OBJECT);
}
/**
* @return Session
*/
public function d3GetSession()
public function d3GetSession(): Session
{
return Registry::getSession();
}
/**
* @return string|null
* @throws \Doctrine\DBAL\Driver\Exception
* @throws DoctrineDriverException
* @throws Exception
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface

View File

@ -33,8 +33,6 @@ use Psr\Container\NotFoundExceptionInterface;
class d3_LoginController_Webauthn extends d3_LoginController_Webauthn_parent
{
/**
* @return Webauthn
*/
@ -45,7 +43,10 @@ class d3_LoginController_Webauthn extends d3_LoginController_Webauthn_parent
/**
* @return mixed|string
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws DoctrineException
* @throws Exception
* @throws NotFoundExceptionInterface
*/
public function checklogin()
{
@ -56,13 +57,20 @@ class d3_LoginController_Webauthn extends d3_LoginController_Webauthn_parent
$webauthn = $this->d3GetWebauthnObject();
if ($webauthn->isActive($userId)
&& false == Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)
&& !Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)
) {
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
$this->getClassKey() != 'd3webauthnadminlogin' ? $this->getClassKey() : 'admin_start');
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $userId);
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_LOGINUSER, $lgn_user);
$this->getClassKey() != 'd3webauthnadminlogin' ? $this->getClassKey() : 'admin_start'
);
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER,
$userId
);
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_LOGINUSER,
$lgn_user
);
/*
Registry::getSession()->setVariable(

View File

@ -1,209 +0,0 @@
<?php
/**
* This Software is the property of Data Development and is protected
* by copyright law - it is NOT Freeware.
* Any unauthorized use of this software without a valid license
* is a violation of the license agreement and will be prosecuted by
* civil and criminal law.
* http://www.shopmodule.com
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
* @link http://www.oxidmodule.com
*/
namespace D3\Webauthn\Modules\Application\Controller;
use D3\Webauthn\Application\Model\Credential\d3MetadataStatementRepository;
use D3\Webauthn\Application\Model\Webauthn\d3PublicKeyCredentialRpEntity;
use D3\Webauthn\Application\Model\Webauthn\d3PublicKeyCredentialSourceRepository;
use D3\Webauthn\Application\Model\Webauthn\d3PublicKeyCredentialUserEntity;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7Server\ServerRequestCreator;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialRequestOptions;
use Webauthn\Server;
class d3_StartController_Webauthn extends d3_StartController_Webauthn_parent
{
/**
* @return string
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function ___render()
{
if (!Registry::getRequest()->getRequestEscapedParameter('authn')) {
/*** register ***/
$rpEntity = oxNew(d3PublicKeyCredentialRpEntity::class, Registry::getConfig()->getActiveShop());
$publicKeyCredentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
$server = new Server(
$rpEntity,
$publicKeyCredentialSourceRepository,
new d3MetadataStatementRepository()
);
$user = oxNew(User::class);
//$user->load('oxdefaultadmin');
$user->load('36944b76d6e583fe2.12734046');
$userEntity = new d3PublicKeyCredentialUserEntity($user);
$excludedCredentials = [];
$credentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
foreach ($credentialSourceRepository->findAllForUserEntity($userEntity) as $credentialSource) {
$excludedCredentials[] = $credentialSource->getPublicKeyCredentialDescriptor();
}
$publicKeyCredentialCreationOptions = $server->generatePublicKeyCredentialCreationOptions(
$userEntity,
PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE,
$excludedCredentials
);
$this->addTplParam(
'webauthn_publickey_register',
json_encode($publicKeyCredentialCreationOptions, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
);
if (!Registry::getSession()->isSessionStarted()) {
Registry::getSession()->start();
}
Registry::getSession()->setVariable('authnobject', $publicKeyCredentialCreationOptions);
/*** login ***/
$allowedCredentials = [];
$credentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
foreach ($credentialSourceRepository->findAllForUserEntity($userEntity) as $credentialSource) {
$allowedCredentials[] = $credentialSource->getPublicKeyCredentialDescriptor();
}
// We generate the set of options.
$publicKeyCredentialRequestOptions = $server->generatePublicKeyCredentialRequestOptions(
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_PREFERRED, // Default value
$allowedCredentials
);
$this->addTplParam(
'webauthn_publickey_login',
json_encode($publicKeyCredentialRequestOptions, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
);
Registry::getSession()->setVariable('authnloginobject', $publicKeyCredentialRequestOptions);
}
$return = parent::render();
return $return;
}
public function ____checkregister()
{
// Retrieve the PublicKeyCredentialCreationOptions object created earlier
/** @var PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions */
$publicKeyCredentialCreationOptions = Registry::getSession()->getVariable('authnobject');
// Retrieve de data sent by the device
$data = base64_decode(Registry::getRequest()->getRequestParameter('authn'), true);
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory, // ServerRequestFactory
$psr17Factory, // UriFactory
$psr17Factory, // UploadedFileFactory
$psr17Factory // StreamFactory
);
$serverRequest = $creator->fromGlobals();
/*** register ***/
$rpEntity = oxNew(d3PublicKeyCredentialRpEntity::class, Registry::getConfig()->getActiveShop());
$publicKeyCredentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
$server = new Server(
$rpEntity,
$publicKeyCredentialSourceRepository,
new d3MetadataStatementRepository()
);
try {
$publicKeyCredentialSource = $server->loadAndCheckAttestationResponse(
$data,
$publicKeyCredentialCreationOptions, // The options you stored during the previous step
$serverRequest // The PSR-7 request
);
// The user entity and the public key credential source can now be stored using their repository
// The Public Key Credential Source repository must implement Webauthn\PublicKeyCredentialSourceRepository
$publicKeyCredentialSourceRepository->saveCredentialSource($publicKeyCredentialSource);
} catch(\Exception $exception) {
dumpvar($exception);
}
dumpvar('registered');
}
public function _____checklogin()
{
// Retrieve the Options passed to the device
$publicKeyCredentialRequestOptions = Registry::getSession()->getVariable('authnloginobject');
if (!$publicKeyCredentialRequestOptions) {
return;
}
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory, // ServerRequestFactory
$psr17Factory, // UriFactory
$psr17Factory, // UploadedFileFactory
$psr17Factory // StreamFactory
);
$serverRequest = $creator->fromGlobals();
// Retrieve de data sent by the device
$data = base64_decode(Registry::getRequest()->getRequestParameter('authn'));
$publicKeyCredentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
$server = new Server(
new d3PublicKeyCredentialRpEntity(Registry::getConfig()->getActiveShop()),
$publicKeyCredentialSourceRepository,
new d3MetadataStatementRepository()
);
$user = oxNew(User::class);
//$user->load('oxdefaultadmin');
$user->load('36944b76d6e583fe2.12734046');
$userEntity = new d3PublicKeyCredentialUserEntity($user);
try {
$publicKeyCredentialSource = $server->loadAndCheckAssertionResponse(
$data,
$publicKeyCredentialRequestOptions, // The options you stored during the previous step
$userEntity, // The user entity
$serverRequest // The PSR-7 request
);
//If everything is fine, this means the user has correctly been authenticated using the
// authenticator defined in $publicKeyCredentialSource
} catch(\Throwable $exception) {
dumpvar(openssl_error_string());
dumpvar($exception);
}
dumpvar('logged in');
}
}

View File

@ -16,10 +16,8 @@
namespace D3\Webauthn\Modules\Application\Model;
use D3\Webauthn\Application\Model\WebauthnConf;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
use ReflectionClass;
use Webauthn\PublicKeyCredentialUserEntity;
class d3_User_Webauthn extends d3_User_Webauthn_parent
{
@ -58,22 +56,6 @@ class d3_User_Webauthn extends d3_User_Webauthn_parent
return $return;
}
/**
* @return PublicKeyCredentialUserEntity
*/
public function d3GetWebauthnUserEntity(): PublicKeyCredentialUserEntity
{
if ($this->isLoaded()) {
return oxNew(PublicKeyCredentialUserEntity::class,
$this->getFieldData('oxusername'),
$this->getId(),
$this->getFieldData('oxfname') . ' ' . $this->getFieldData('oxlname')
);
}
throw oxNew(StandardException::class, 'can not create webauthn user entity from not loaded user');
}
public function login($userName, $password, $setSessionCookie = false)
{
if (Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)) {

View File

@ -25,19 +25,15 @@ use D3\Webauthn\Application\Controller\d3_account_webauthn;
use D3\Webauthn\Application\Controller\d3webauthnlogin;
use D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent;
use D3\Webauthn\Modules\Application\Controller\Admin\d3_LoginController_Webauthn;
use D3\Webauthn\Modules\Application\Controller\d3_StartController_Webauthn;
use D3\Webauthn\Modules\Application\Controller\d3_webauthn_OrderController;
use D3\Webauthn\Modules\Application\Controller\d3_webauthn_PaymentController;
use D3\Webauthn\Modules\Application\Controller\d3_webauthn_UserController;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use D3\Webauthn\Modules\Core\d3_webauthn_utils;
use D3\Webauthn\Setup as ModuleSetup;
use D3\ModCfg\Application\Model\d3utils;
use OxidEsales\Eshop\Application\Component\UserComponent;
use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
use OxidEsales\Eshop\Application\Controller\OrderController;
use OxidEsales\Eshop\Application\Controller\PaymentController;
use OxidEsales\Eshop\Application\Controller\StartController;
use OxidEsales\Eshop\Application\Controller\UserController;
use OxidEsales\Eshop\Core\Utils;
use OxidEsales\Eshop\Application\Model as OxidModel;
@ -69,7 +65,6 @@ $aModule = array(
PaymentController::class => d3_webauthn_PaymentController::class,
OrderController::class => d3_webauthn_OrderController::class,
OxidModel\User::class => d3_User_Webauthn::class,
StartController::class => d3_StartController_Webauthn::class,
LoginController::class => d3_LoginController_Webauthn::class,
Utils::class => d3_webauthn_utils::class,
UserComponent::class => d3_webauthn_UserComponent::class,
@ -99,15 +94,10 @@ $aModule = array(
'block' => 'account_menu',
'file' => 'Application/views/blocks/page/account/inc/account_menu.tpl',
],
[
'template' => 'page/shop/start.tpl',
'block' => 'start_welcome_text',
'file' => 'Application/views/blocks/page/shop/start_welcome_text.tpl',
],
[
'template' => 'login.tpl',
'block' => 'admin_login_form',
'file' => 'Application/views/admin/blocks/d3webauthn_login_admin_login_form.tpl',
'template' => 'widget/header/servicebox.tpl',
'block' => 'widget_header_servicebox_items',
'file' => 'Application/views/blocks/widget/header/widget_header_servicebox_items.tpl',
]
]
);