8
0
Fork 0

add shop controllers

Dieser Commit ist enthalten in:
Daniel Seifert 2022-10-24 22:24:40 +02:00
Commit b5b3117fec
Signiert von: DanielS
GPG-Schlüssel-ID: 6A513E13AEE66170
42 geänderte Dateien mit 3663 neuen und 0 gelöschten Zeilen

46
composer.json Normale Datei
Datei anzeigen

@ -0,0 +1,46 @@
{
"name": "d3/oxwebauthn",
"description": "Webauthn / FIDO2 module for OXID eShop.",
"type": "oxideshop-module",
"keywords": [
"oxid",
"modules",
"eShop",
"d3",
"webauthn",
"FIDO2",
"CTAP",
"public key"
],
"authors": [
{
"name": "D3 Data Development (Inh. Thomas Dartsch)",
"email": "info@shopmodule.com",
"homepage": "https://www.d3data.de",
"role": "Owner"
}
],
"homepage": "https://www.oxidmodule.com/",
"license": [
"GPL-3.0-or-later"
],
"extra": {
"oxideshop": {
"source-directory": "/src",
"target-directory": "d3/oxwebauthn"
}
},
"require": {
"php": ">=5.6",
"oxid-esales/oxideshop-ce": "6.10 - 6.12",
"web-auth/webauthn-lib": "^3.3",
"ext-json": "*",
"ext-soap": "*",
"ext-PDO": "*"
},
"autoload": {
"psr-4": {
"D3\\Webauthn\\": "../../../source/modules/d3/oxwebauthn"
}
}
}

Datei anzeigen

@ -0,0 +1,174 @@
<?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\Controller\Admin;
use D3\Webauthn\Application\Model\Credential\d3PublicKeyCredentialList;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\Webauthn\d3PublicKeyCredentialUserEntity;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Exception;
use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\UtilsView;
class d3user_webauthn extends AdminDetailsController
{
protected $_sSaveError = null;
protected $_sThisTemplate = 'd3user_webauthn.tpl';
/**
* @return string
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function render()
{
parent::render();
$soxId = $this->getEditObjectId();
if (isset($soxId) && $soxId != "-1") {
/** @var d3_User_Webauthn $oUser */
$oUser = $this->getUserObject();
if ($oUser->load($soxId)) {
$this->addTplParam("oxid", $oUser->getId());
} else {
$this->addTplParam("oxid", '-1');
}
$this->addTplParam("edit", $oUser);
}
if ($this->_sSaveError) {
$this->addTplParam("sSaveError", $this->_sSaveError);
}
$this->setAuthnRegister();
return $this->_sThisTemplate;
}
/**
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function setAuthnRegister()
{
$publicKeyCredentialCreationOptions = $this->getWebauthnObject()->setAuthnRegister($this->getEditObjectId());
$this->addTplParam(
'webauthn_publickey_register',
json_encode($publicKeyCredentialCreationOptions, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
);
}
/**
* @param $userId
* @return d3PublicKeyCredentialList|object
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function getCredentialList($userId)
{
$credentialList = oxNew(d3PublicKeyCredentialList::class);
$oUser = $this->getUserObject();
$oUser->load($userId);
if ($oUser && $oUser->getId()) {
/** @var d3PublicKeyCredentialUserEntity $userEntity */
$userEntity = oxNew(d3PublicKeyCredentialUserEntity::class, $oUser);
$credentialList->loadAllForUserEntity($userEntity);
}
return $credentialList;
}
/**
* @return User
*/
public function getUserObject()
{
return oxNew(User::class);
}
/**
* @return d3webauthn
*/
public function getWebauthnObject()
{
return oxNew(d3webauthn::class);
}
public function registerNewKey()
{
$this->getWebauthnObject()->registerNewKey(Registry::getRequest()->getRequestParameter('authn'));
}
/**
* @throws Exception
*/
public function save()
{
parent::save();
$aParams = Registry::getRequest()->getRequestEscapedParameter("editval");
try {
/** @var d3webauthn $oWebauthn */
$oWebauthn = $this->getWebauthnObject();
/*
if ($oWebauthn->checkIfAlreadyExist($this->getEditObjectId())) {
$oException = oxNew(StandardException::class, 'D3_TOTP_ALREADY_EXIST');
throw $oException;
};
if ($aParams['d3totp__oxid']) {
$oWebauthn->load($aParams['d3totp__oxid']);
} else {
$aParams['d3totp__usetotp'] = 1;
$seed = Registry::getRequest()->getRequestEscapedParameter("secret");
$otp = Registry::getRequest()->getRequestEscapedParameter("otp");
$oWebauthn->saveSecret($seed);
$oWebauthn->assign($aParams);
$oWebauthn->verify($otp, $seed);
$oWebauthn->setId();
}
$oWebauthn->save();
*/
} catch (Exception $oExcp) {
$this->_sSaveError = $oExcp->getMessage();
}
}
/**
* @throws DatabaseConnectionException
*/
public function delete()
{
$aParams = Registry::getRequest()->getRequestEscapedParameter("editval");
/** @var d3webauthn $oWebauthn */
$oWebauthn = $this->getWebauthnObject();
if ($aParams['d3totp__oxid']) {
$oWebauthn->load($aParams['d3totp__oxid']);
$oWebauthn->delete();
Registry::get(UtilsView::class)->addErrorToDisplay('D3_TOTP_REGISTERDELETED');
}
}
}

Datei anzeigen

@ -0,0 +1,105 @@
<?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\Controller;
use D3\Webauthn\Application\Model\Credential\d3PublicKeyCredential;
use D3\Webauthn\Application\Model\Credential\d3PublicKeyCredentialList;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\Webauthn\d3PublicKeyCredentialUserEntity;
use OxidEsales\Eshop\Application\Controller\AccountController;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
class d3_account_webauthn extends AccountController
{
protected $_sThisTemplate = 'd3_account_webauthn.tpl';
/**
* @return string
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function render()
{
if (Registry::getRequest()->getRequestEscapedParameter('error')) {
dumpvar(Registry::getRequest()->getRequestEscapedParameter('error'));
Registry::getUtilsView()->addErrorToDisplay('error occured');
}
$sRet = parent::render();
// is logged in ?
$oUser = $this->getUser();
if (!$oUser) {
return $this->_sThisTemplate = $this->_sThisLoginTemplate;
}
$this->addTplParam('user', $this->getUser());
$this->setAuthnRegister();
return $sRet;
}
/**
* @return d3PublicKeyCredentialList|object
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function getCredentialList()
{
$credentialList = oxNew(d3PublicKeyCredentialList::class);
$oUser = $this->getUser();
if ($oUser) {
/** @var d3PublicKeyCredentialUserEntity $userEntity */
$userEntity = oxNew(d3PublicKeyCredentialUserEntity::class, $oUser);
$credentialList->loadAllForUserEntity($userEntity);
}
return $credentialList;
}
/**
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function setAuthnRegister()
{
$webauthn = oxNew(d3webauthn::class);
$publicKeyCredentialCreationOptions = $webauthn->setAuthnRegister('36944b76d6e583fe2.12734046');
$this->addTplParam(
'webauthn_publickey_register',
json_encode($publicKeyCredentialCreationOptions, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
);
}
public function registerNewKey()
{
$webauthn = oxNew(d3webauthn::class);
$webauthn->registerNewKey(Registry::getRequest()->getRequestParameter('authn'));
}
public function deleteKey()
{
if (Registry::getRequest()->getRequestEscapedParameter('oxid')) {
$credential = oxNew(d3PublicKeyCredential::class);
$credential->delete(Registry::getRequest()->getRequestEscapedParameter('oxid'));
}
}
}

Datei anzeigen

@ -0,0 +1,121 @@
<?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\Controller;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\d3webauthn_conf;
use OxidEsales\Eshop\Application\Controller\FrontendController;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Utils;
class d3webauthnlogin extends FrontendController
{
protected $_sThisTemplate = 'd3webauthnlogin.tpl';
/**
* @return null
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function render()
{
if (Registry::getSession()->hasVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH) ||
false == Registry::getSession()->hasVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER)
) {
$this->getUtils()->redirect('index.php?cl=start', true, 302);
if (false == defined('OXID_PHP_UNIT')) {
// @codeCoverageIgnoreStart
exit;
// @codeCoverageIgnoreEnd
}
}
$this->generateCredentialRequest();
$this->addTplParam('navFormParams', Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_NAVFORMPARAMS));
return parent::render();
}
/**
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function generateCredentialRequest()
{
$auth = Registry::getSession()->getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER);
$webauthn = oxNew(d3webauthn::class);
$publicKeyCredentialRequestOptions = $webauthn->getCredentialRequestOptions($auth);
$this->addTplParam(
'webauthn_publickey_login',
$publicKeyCredentialRequestOptions
);
}
/**
* @return Utils
*/
public function getUtils()
{
return Registry::getUtils();
}
public function getPreviousClass()
{
return Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS);
}
public function previousClassIsOrderStep()
{
$sClassKey = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS);
$resolvedClass = Registry::getControllerClassNameResolver()->getClassNameById($sClassKey);
$resolvedClass = $resolvedClass ? $resolvedClass : 'start';
/** @var FrontendController $oController */
$oController = oxNew($resolvedClass);
return $oController->getIsOrderStep();
}
/**
* @return bool
*/
public function getIsOrderStep()
{
return $this->previousClassIsOrderStep();
}
/**
* Returns Bread Crumb - you are here page1/page2/page3...
*
* @return array
*/
public function getBreadCrumb()
{
$aPaths = [];
$aPath = [];
$iBaseLanguage = Registry::getLang()->getBaseLanguage();
$aPath['title'] = Registry::getLang()->translateString('D3_WEBAUTHN_BREADCRUMB', $iBaseLanguage, false);
$aPath['link'] = $this->getLink();
$aPaths[] = $aPath;
return $aPaths;
}
}

Datei anzeigen

@ -0,0 +1,29 @@
<?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\Credential;
use Webauthn\MetadataService\MetadataStatement;
use Webauthn\MetadataService\MetadataStatementRepository;
class d3MetadataStatementRepository implements MetadataStatementRepository
{
public function findOneByAAGUID(string $aaguid): ?MetadataStatement
{
return new MetadataStatement();
}
}

Datei anzeigen

@ -0,0 +1,153 @@
<?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\Credential;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
class d3PublicKeyCredential extends BaseModel
{
protected $_sCoreTable = 'd3PublicKeyCredential';
public function __construct()
{
$this->init($this->getCoreTableName());
parent::__construct();
}
public function d3SetName($name)
{
$this->assign(['name' => $name]);
}
public function d3GetName()
{
return $this->getFieldData('name');
}
public function d3SetCredentialId($credentialId)
{
$this->assign(['credentialid' => $credentialId]);
}
public function d3GetCredentialId()
{
return $this->__get($this->_getFieldLongName('credentialid'))->rawValue;
}
public function d3SetType($type)
{
$this->assign(['Type' => $type]);
}
public function d3GetType()
{
return $this->getFieldData('Type');
}
public function d3SetTransports($transports)
{
$this->assign(['Transports' => base64_encode(serialize($transports))]);
}
public function d3GetTransports()
{
return unserialize(base64_decode($this->getFieldData('Transports')));
}
public function d3SetAttestationType($attestationType)
{
$this->assign(['AttestationType' => $attestationType]);
}
public function d3GetAttestationType()
{
return $this->getFieldData('AttestationType');
}
public function d3SetTrustPath($trustPath)
{
$this->assign(['TrustPath' => base64_encode(serialize($trustPath))]);
}
public function d3GetTrustPath()
{
return unserialize(base64_decode($this->getFieldData('TrustPath')));
}
public function d3SetAaguid($aaguid)
{
$this->assign(['Aaguid' => base64_encode(serialize($aaguid))]);
}
public function d3GetAaguid()
{
return unserialize(base64_decode($this->getFieldData('Aaguid')));
}
public function d3SetPublicKey($publicKey)
{
$this->assign(['PublicKey' => $publicKey]);
}
public function d3GetPublicKey()
{
return $this->__get($this->_getFieldLongName('PublicKey'))->rawValue;
}
public function d3SetUserHandle($userHandle)
{
$this->assign(['UserHandle' => $userHandle]);
}
public function d3GetUserHandle()
{
return $this->getFieldData('UserHandle');
}
public function d3SetCounter($count)
{
$this->assign(['Counter' => $count]);
}
public function d3GetCounter()
{
return $this->getFieldData('Counter');
}
/**
* @param string $publicKeyCredentialId
* @return |null
* @throws DatabaseConnectionException
*/
public function loadByCredentialId(string $publicKeyCredentialId)
{
if (Registry::getRequest()->getRequestEscapedParameter('fnc') == 'checkregister') {
return null;
}
$oDb = DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC);
$q = "SELECT oxid FROM ".$this->getViewName()." WHERE CredentialId = ".$oDb->quote($publicKeyCredentialId);
$id = $oDb->getOne($q);
$this->load($id);
}
}

Datei anzeigen

@ -0,0 +1,53 @@
<?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\Credential;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Model\ListModel;
use Webauthn\PublicKeyCredentialUserEntity;
class d3PublicKeyCredentialList extends ListModel
{
protected $_sObjectsInListName = d3PublicKeyCredential::class;
public function __construct()
{
parent::__construct(d3PublicKeyCredential::class);
}
/**
* @param PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function loadAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity)
{
$q = "SELECT oxid FROM ".$this->getBaseObject()->getViewName()." WHERE UserHandle = ".DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC)->quote($publicKeyCredentialUserEntity->getId());
$idList = DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC)->getAll($q);
if ($idList && is_iterable($idList)) {
foreach ($idList as $id) {
$credential = oxNew($this->_sObjectsInListName);
$credential->load($id['oxid']);
$this->offsetSet($credential->getId(), $credential);
}
}
}
}

Datei anzeigen

@ -0,0 +1,25 @@
<?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\Exceptions;
use OxidEsales\Eshop\Core\Exception\StandardException;
abstract class d3webauthnExceptionAbstract extends StandardException
{
}

Datei anzeigen

@ -0,0 +1,36 @@
<?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\Exceptions;
use Exception;
use OxidEsales\Eshop\Core\Exception\StandardException;
class d3webauthnMissingPublicKeyCredentialRequestOptions extends d3webauthnExceptionAbstract
{
/**
* Default constructor
*
* @param string $sMessage exception message
* @param integer $iCode exception code
* @param Exception|null $previous previous exception
*/
public function __construct($sMessage = "D3_WEBAUTHN_ERROR_MISSINGPKC", $iCode = 0, Exception $previous = null)
{
parent::__construct($sMessage, $iCode, $previous);
}
}

Datei anzeigen

@ -0,0 +1,35 @@
<?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\Exceptions;
use Exception;
class d3webauthnWrongAuthException extends d3webauthnExceptionAbstract
{
/**
* Default constructor
*
* @param string $sMessage exception message
* @param integer $iCode exception code
* @param Exception|null $previous previous exception
*/
public function __construct($sMessage = "D3_WEBAUTHN_ERROR_UNVALID", $iCode = 0, Exception $previous = null)
{
parent::__construct($sMessage, $iCode, $previous);
}
}

Datei anzeigen

@ -0,0 +1,34 @@
<?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
*/
declare(strict_types=1);
namespace D3\Webauthn\Application\Model\Webauthn;
use OxidEsales\Eshop\Application\Model\Shop;
use Webauthn\PublicKeyCredentialRpEntity;
class d3PublicKeyCredentialRpEntity extends PublicKeyCredentialRpEntity
{
public function __construct(Shop $shop)
{
parent::__construct(
$shop->getFieldData('oxname'),
$_SERVER['HTTP_HOST']
);
}
}

Datei anzeigen

@ -0,0 +1,74 @@
<?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\d3PublicKeyCredential;
use Webauthn\PublicKeyCredentialSource;
class d3PublicKeyCredentialSource extends PublicKeyCredentialSource
{
/**
* @throws \Exception
*/
public function saveCredential()
{
$credential = oxNew(d3PublicKeyCredential::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(d3PublicKeyCredential $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()
);
}
}

Datei anzeigen

@ -0,0 +1,88 @@
<?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
*/
declare(strict_types=1);
namespace D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\Credential\d3PublicKeyCredential;
use D3\Webauthn\Application\Model\Credential\d3PublicKeyCredentialList;
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(d3PublicKeyCredential::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(d3PublicKeyCredentialList::class);
$credentialList->loadAllForUserEntity($publicKeyCredentialUserEntity);
/** @var d3PublicKeyCredential $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();
}
}
}

Datei anzeigen

@ -0,0 +1,33 @@
<?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 OxidEsales\Eshop\Application\Model\User;
use Webauthn\PublicKeyCredentialUserEntity;
class d3PublicKeyCredentialUserEntity extends publicKeyCredentialUserEntity
{
public function __construct(User $user)
{
parent::__construct(
strtolower($user->getFieldData('oxfname').'.'.$user->getFieldData('oxlname')),
$user->getId(),
$user->getFieldData('oxfname').', '.$user->getFieldData('oxlname')
);
}
}

334
src/Application/Model/d3webauthn.php Ausführbare Datei
Datei anzeigen

@ -0,0 +1,334 @@
<?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;
use Assert\InvalidArgumentException;
use D3\Webauthn\Application\Model\Credential\d3MetadataStatementRepository;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnWrongAuthException;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnMissingPublicKeyCredentialRequestOptions;
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\Database\Adapter\DatabaseInterface;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
use Webauthn\PublicKeyCredentialCreationOptions;
use Webauthn\PublicKeyCredentialRequestOptions;
use Webauthn\Server;
class d3webauthn extends BaseModel
{
public $tableName = 'd3PublicKeyCredential';
protected $_sCoreTable = 'd3PublicKeyCredential';
public $userId;
/**
* d3webauthn constructor.
*/
public function __construct()
{
$this->init($this->tableName);
return parent::__construct();
}
/**
* @param $userId
* @throws DatabaseConnectionException
*/
public function loadByUserId($userId)
{
$this->userId = $userId;
$oDb = $this->d3GetDb();
if ($userId && $oDb->getOne("SHOW TABLES LIKE '".$this->tableName."'")) {
$query = "SELECT oxid FROM ".$this->getViewName().' WHERE UserHandle = '.$oDb->quote($userId).' LIMIT 1';
$this->load($oDb->getOne($query));
}
}
/**
* @return DatabaseInterface
* @throws DatabaseConnectionException
*/
public function d3GetDb()
{
return DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC);
}
/**
* @return User
*/
public function getUser()
{
$userId = $this->userId ? $this->userId : $this->getFieldData('UserHandle');
$user = $this->d3GetUser();
$user->load($userId);
return $user;
}
/**
* @return User
*/
public function d3GetUser()
{
return oxNew(User::class);
}
/**
* @return bool
*/
public function isActive()
{
return false == Registry::getConfig()->getConfigParam('blDisableWebauthnGlobally')
&& $this->UserUseWebauthn();
}
/**
* @return bool
*/
public function UserUseWebauthn()
{
return strlen($this->getId())
&& strlen($this->__get($this->_getFieldLongName('publickey'))->rawValue);
}
/**
* @param $auth
* @return false|string|null
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function getCredentialRequestOptions($auth)
{
$this->loadByUserId($auth);
$requestOptions = null;
if ($auth
&& $this->isActive()
&& false == Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH)
) {
/** @var d3PublicKeyCredentialRpEntity $rpEntity */
$rpEntity = oxNew(d3PublicKeyCredentialRpEntity::class, Registry::getConfig()->getActiveShop());
$publicKeyCredentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
$server = new Server(
$rpEntity,
$publicKeyCredentialSourceRepository,
new d3MetadataStatementRepository()
);
$user = $this->getUser();
$userEntity = new d3PublicKeyCredentialUserEntity($user);
dumpvar($userEntity);
$allowedCredentials = [];
$credentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
/** @var d3PublicKeyCredentialSource $credentialSource */
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
);
dumpvar($publicKeyCredentialRequestOptions);
$requestOptions = json_encode($publicKeyCredentialRequestOptions, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_LOGIN_OBJECT, $publicKeyCredentialRequestOptions);
// set auth as secured parameter;
Registry::getSession()->setVariable("auth", $auth);
}
return $requestOptions;
}
/**
* @param $webauth
* @return bool
* @throws d3webauthnWrongAuthException
* @throws d3webauthnMissingPublicKeyCredentialRequestOptions
*/
public function verify($webauth)
{
$blVerify = false;
// Retrieve the Options passed to the device
$publicKeyCredentialRequestOptions = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_LOGIN_OBJECT);
if (!$publicKeyCredentialRequestOptions) {
$oException = oxNew(d3webauthnMissingPublicKeyCredentialRequestOptions::class);
throw $oException;
}
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory, // ServerRequestFactory
$psr17Factory, // UriFactory
$psr17Factory, // UploadedFileFactory
$psr17Factory // StreamFactory
);
$serverRequest = $creator->fromGlobals();
$publicKeyCredentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
$server = new Server(
new d3PublicKeyCredentialRpEntity(Registry::getConfig()->getActiveShop()),
$publicKeyCredentialSourceRepository,
new d3MetadataStatementRepository()
);
$user = $this->getUser();
$userEntity = new d3PublicKeyCredentialUserEntity($user);
try {
$server->loadAndCheckAssertionResponse(
$webauth,
$publicKeyCredentialRequestOptions, // The options you stored during the previous step
$userEntity, // The user entity
$serverRequest // The PSR-7 request
);
$blVerify = true;
Registry::getSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_LOGIN_OBJECT);
//If everything is fine, this means the user has correctly been authenticated using the
// authenticator defined in $publicKeyCredentialSource
} catch(InvalidArgumentException $exception) {
// ToDo
$oException = oxNew(d3webauthnWrongAuthException::class);
Registry::getUtilsView()->addErrorToDisplay($oException);
// write to log
//dumpvar(openssl_error_string());
//dumpvar($exception);
}
if (false == $blVerify) {
$oException = oxNew(d3webauthnWrongAuthException::class);
throw $oException;
}
return $blVerify;
}
/**
* @param $sUserId
* @return PublicKeyCredentialCreationOptions
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function setAuthnRegister($sUserId)
{
$rpEntity = oxNew(d3PublicKeyCredentialRpEntity::class, Registry::getConfig()->getActiveShop());
$publicKeyCredentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
$server = new Server(
$rpEntity,
$publicKeyCredentialSourceRepository,
new d3MetadataStatementRepository()
);
/*
if (!($user = Registry::getSession()->getUser())) {
$e = oxNew(\Exception::class, 'no user loaded');
throw $e;
}
*/
$user = oxNew(User::class);
$user->load($sUserId);
$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
);
if (!Registry::getSession()->isSessionStarted()) {
Registry::getSession()->start();
}
Registry::getSession()->setVariable('authnobject', $publicKeyCredentialCreationOptions);
return $publicKeyCredentialCreationOptions;
}
/**
* @param $request
*/
public function registerNewKey($request)
{
/** @var PublicKeyCredentialCreationOptions $publicKeyCredentialCreationOptions */
$publicKeyCredentialCreationOptions = Registry::getSession()->getVariable('authnobject');
// Retrieve de data sent by the device
$data = base64_decode($request, 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
// ToDo: is counter set and why will not save in case of login?
$publicKeyCredentialSourceRepository->saveCredentialSource($publicKeyCredentialSource);
} catch(\Exception $exception) {
dumpvar($exception);
}
dumpvar('registered');
}
}

Datei anzeigen

@ -0,0 +1,25 @@
<?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;
class d3webauthn_conf
{
const WEBAUTHN_SESSION_AUTH = 'webauthn_auth';
const WEBAUTHN_LOGIN_OBJECT = 'authnloginobject';
const WEBAUTHN_SESSION_CURRENTUSER = 'd3webauthnCurrentUser';
const WEBAUTHN_SESSION_CURRENTCLASS = 'd3webauthnCurrentClass';
const WEBAUTHN_SESSION_NAVFORMPARAMS = 'd3webauthnNavFormParams';
}

Datei anzeigen

@ -0,0 +1,20 @@
<?php
$sLangName = 'Deutsch';
// -------------------------------
// RESOURCE IDENTIFIER = STRING
// -------------------------------
$aLang = array(
'charset' => 'UTF-8',
'D3_WEBAUTHN_ACCOUNT' => 'd3 Webauthn',
'WEBAUTHN_INPUT_HELP' => 'Bitte mit Hardwareschlüssel authentisieren.',
'WEBAUTHN_CANCEL_LOGIN' => 'Anmeldung abbrechen',
'D3_WEBAUTHN_BREADCRUMB' => 'Passwortloses Anmelden',
'D3_WEBAUTHN_ACCOUNT_TYPE0' => 'nur Passwort',
'D3_WEBAUTHN_ACCOUNT_TYPE1' => 'nur Auth-Stick',
'D3_WEBAUTHN_ACCOUNT_TYPE2' => 'nur Auth-Stick, Passwort als Alternative',
'D3_WEBAUTHN_ACCOUNT_TYPE3' => 'Auth-Stick und Passwort in Kombination',
);

Datei anzeigen

@ -0,0 +1,85 @@
[{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}]

Datei anzeigen

@ -0,0 +1,53 @@
<?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
*/
$sLangName = "Deutsch";
$aLang = [
'charset' => 'UTF-8',
'D3_WEBAUTHN_ERROR_UNVALID' => 'Der verwendete Schlüssel ist ungültig oder kann nicht geprüft werden.',
'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',
/*
'd3mxuser_totp' => '2-Faktor-Authentisierung',
'D3_TOTP_REGISTERNEW' => 'neue Registrierung erstellen',
'D3_TOTP_QRCODE' => 'QR-Code',
'D3_TOTP_QRCODE_HELP' => 'Scannen Sie diesen QR-Code mit Ihrer Authentisierungs-App, um dieses Benutzerkonto dort zu hinterlegen.',
'D3_TOTP_SECRET' => 'QR-Code kann nicht gescannt werden?',
'D3_TOTP_SECRET_HELP' => 'Setzen Sie keine App ein, die den QR-Code scannen kann, können Sie diese Zeichenkette auch in Ihr Authentisierungstool kopieren. Stellen Sie bitte die Passwortlänge auf 6 Zeichen und das Zeitinterval auf 30 Sekunden ein.',
'D3_TOTP_CURROTP' => 'Bestätigung mit Einmalpasswort',
'D3_TOTP_CURROTP_HELP' => 'Haben Sie dieses Kundenkonto in Ihrer Authentisierungs-App registriert, generieren Sie damit ein Einmalpasswort, tragen Sie es hier ein und senden das Formular direkt darauf hin ab.',
'D3_TOTP_REGISTEREXIST' => 'vorhandene Registrierung',
'D3_TOTP_REGISTERDELETE' => 'Registrierung löschen',
'D3_TOTP_REGISTERDELETE_DESC' => 'Um die Registrierung zu ändern, löschen Sie diese bitte vorerst. Sie können sofort im Anschluss eine neue Registrierung anlegen.<br>Wenn Sie die Registrierung löschen, ist das Konto nicht mehr durch die Zwei-Faktor-Authentisierung geschützt.',
'D3_TOTP_REGISTERDELETED' => 'Die Registrierung wurde gelöscht.',
'D3_TOTP_BACKUPCODES' => 'Backupcodes',
'D3_TOTP_BACKUPCODES_DESC' => 'Mit diesen Backupcodes können Sie sich anmelden, wenn die Generierung des Einmalpasswortes nicht möglich ist (z.B. Gerät verloren oder neu installiert). Sie können dann die Einstellungen zur Verwendung der 2-Faktor-Authentisierung ändern oder einen neuen Zugang erstellen. Speichern Sie sich diese Codes bitte in diesem Moment sicher ab. Nach Verlassen dieser Seite können diese Codes nicht erneut angezeigt werden.',
'D3_TOTP_AVAILBACKUPCODECOUNT' => 'noch %1$s Backupcode(s) verfügbar',
'D3_TOTP_AVAILBACKUPCODECOUNT_DESC' => 'Um neue Backupcodes zu erstellen, löschen Sie die bestehende Registrierung und legen diese bitte neu an.',
'D3_TOTP_SAVE' => 'Speichern',
'D3_TOTP_ERROR_UNVALID' => 'Das Einmalpasswort ist ungültig.',
'D3_TOTP_ALREADY_EXIST' => 'Die Registrierung wurde schon gespeichert.',
*/
];

Datei anzeigen

@ -0,0 +1,53 @@
<?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
*/
$sLangName = "English";
$aLang = [
'charset' => 'UTF-8',
'D3_WEBAUTHN_ERROR_UNVALID' => 'Der verwendete Schlüssel ist ungültig oder kann nicht geprüft werden.',
'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' => 'Please authenticate with hardware key.',
'WEBAUTHN_CANCEL_LOGIN' => 'Anmeldung abbrechen',
/*
'd3mxuser_totp' => '2-Faktor-Authentisierung',
'D3_TOTP_REGISTERNEW' => 'neue Registrierung erstellen',
'D3_TOTP_QRCODE' => 'QR-Code',
'D3_TOTP_QRCODE_HELP' => 'Scannen Sie diesen QR-Code mit Ihrer Authentisierungs-App, um dieses Benutzerkonto dort zu hinterlegen.',
'D3_TOTP_SECRET' => 'QR-Code kann nicht gescannt werden?',
'D3_TOTP_SECRET_HELP' => 'Setzen Sie keine App ein, die den QR-Code scannen kann, können Sie diese Zeichenkette auch in Ihr Authentisierungstool kopieren. Stellen Sie bitte die Passwortlänge auf 6 Zeichen und das Zeitinterval auf 30 Sekunden ein.',
'D3_TOTP_CURROTP' => 'Bestätigung mit Einmalpasswort',
'D3_TOTP_CURROTP_HELP' => 'Haben Sie dieses Kundenkonto in Ihrer Authentisierungs-App registriert, generieren Sie damit ein Einmalpasswort, tragen Sie es hier ein und senden das Formular direkt darauf hin ab.',
'D3_TOTP_REGISTEREXIST' => 'vorhandene Registrierung',
'D3_TOTP_REGISTERDELETE' => 'Registrierung löschen',
'D3_TOTP_REGISTERDELETE_DESC' => 'Um die Registrierung zu ändern, löschen Sie diese bitte vorerst. Sie können sofort im Anschluss eine neue Registrierung anlegen.<br>Wenn Sie die Registrierung löschen, ist das Konto nicht mehr durch die Zwei-Faktor-Authentisierung geschützt.',
'D3_TOTP_REGISTERDELETED' => 'Die Registrierung wurde gelöscht.',
'D3_TOTP_BACKUPCODES' => 'Backupcodes',
'D3_TOTP_BACKUPCODES_DESC' => 'Mit diesen Backupcodes können Sie sich anmelden, wenn die Generierung des Einmalpasswortes nicht möglich ist (z.B. Gerät verloren oder neu installiert). Sie können dann die Einstellungen zur Verwendung der 2-Faktor-Authentisierung ändern oder einen neuen Zugang erstellen. Speichern Sie sich diese Codes bitte in diesem Moment sicher ab. Nach Verlassen dieser Seite können diese Codes nicht erneut angezeigt werden.',
'D3_TOTP_AVAILBACKUPCODECOUNT' => 'noch %1$s Backupcode(s) verfügbar',
'D3_TOTP_AVAILBACKUPCODECOUNT_DESC' => 'Um neue Backupcodes zu erstellen, löschen Sie die bestehende Registrierung und legen diese bitte neu an.',
'D3_TOTP_SAVE' => 'Speichern',
'D3_TOTP_ERROR_UNVALID' => 'Das Einmalpasswort ist ungültig.',
'D3_TOTP_ALREADY_EXIST' => 'Die Registrierung wurde schon gespeichert.',
*/
];

Datei anzeigen

@ -0,0 +1,179 @@
[{include file="headitem.tpl" title="GENERAL_ADMIN_TITLE"|oxmultilangassign}]
[{*assign var="webauthn" value=$edit->d3GetWebauthn()}]*}]
[{assign var="userid" value=$edit->getId()}]
[{*$webauthn->loadByUserId($userid)*}]
[{if $readonly}]
[{assign var="readonly" value="readonly disabled"}]
[{else}]
[{assign var="readonly" value=""}]
[{/if}]
<style type="text/css">
td.edittext {
white-space: normal;
}
</style>
<form name="transfer" id="transfer" action="[{$oViewConf->getSelfLink()}]" method="post">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="oxid" value="[{$oxid}]">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
</form>
<form name="myedit" id="myedit" action="[{$oViewConf->getSelfLink()}]" method="post" style="padding: 0;margin: 0;height:0;">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" id="fncname" name="fnc" value="">
<input type="hidden" id="authnvalue" name="authnvalue" value="">
<input type="hidden" id="errorvalue" name="errorvalue" value="">
<input type="hidden" name="oxid" value="[{$oxid}]">
<button type="submit" style="display: none;"></button>
[{* <input type="hidden" name="editval[d3totp__oxid]" value="[{$webauthn->getId()}]">
<input type="hidden" name="editval[d3totp__oxuserid]" value="[{$oxid}]">
*}]
[{if $sSaveError}]
<table style="padding:0; border:0; width:98%;">
<tr>
<td></td>
<td class="errorbox">[{oxmultilang ident=$sSaveError}]</td>
</tr>
</table>
[{/if}]
[{capture name="javascripts"}]
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() {
try {
let publicKey = [{$webauthn_publickey_register}];
console.log('71');
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);});
console.log('74');
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;
});
}
console.log('81');
navigator.credentials.create({ 'publicKey': publicKey }).then(function(data){
console.log('83');
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))
}
};
console.log('92');
document.getElementById('fncname').value = 'registerNewKey';
console.log('94');
document.getElementById('authnvalue').value = btoa(JSON.stringify(publicKeyCredential));
console.log('96');
}).catch(function(error){
console.log(error);
// document.getElementById('errorvalue').value = btoa(JSON.stringify(error));
// document.getElementById('myedit').submit();
});
}
catch (e) {
console.log(e);
}
}
function deleteItem(id) {
document.getElementById('fncname').value = 'deleteKey';
document.getElementById('oxidvalue').value = id;
document.getElementById('actionform').submit();
}
function toggle(elementId) {
$("#" + elementId).toggle();
}
[{/capture}]
[{oxscript add=$smarty.capture.javascripts}]
[{if $oxid && $oxid != '-1'}]
<table style="padding:0; border:0; width:98%;">
<tr>
<td class="edittext" style="vertical-align: top; padding-top:10px;padding-left:10px; width: 50%;">
<table style="padding:0; border:0">
[{block name="user_d3user_totp_form1"}]
<tr>
<td class="edittext">
<h4>[{oxmultilang ident="D3_TOTP_REGISTERNEW"}]</h4>
</td>
</tr>
<tr>
<td class="edittext">
<button onclick="authnregister();">Register</button>
</td>
</tr>
[{/block}]
</table>
</td>
<!-- Anfang rechte Seite -->
<td class="edittext" style="text-align: left; vertical-align: top; height:99%;padding-left:5px;padding-bottom:30px;padding-top:10px; width: 50%;">
<table style="padding:0; border:0">
[{block name="user_d3user_totp_form2"}]
<tr>
<td class="edittext" colspan="2">
<h4>registered keys</h4>
</td>
</tr>
[{foreach from=$oView->getCredentialList($userid) item="credential"}]
<tr>
<td class="edittext">
<label for="secret">[{$credential->d3GetName()}]</label>
</td>
<td class="edittext">
<a href="#" onclick="toggle('keydetails_[{$credential->getId()}]'); return false;" class="list-group-item">
[{$credential->d3GetName()}] (last used: XX)
</a>
<div class="list-group-item" id="keydetails_[{$credential->getId()}]" style="display: none">
<a onclick="deleteItem('[{$credential->getId()}]'); return false;"><span class="glyphicon glyphicon-pencil">delete</span></a>
</div>
</td>
</tr>
[{/foreach}]
[{/block}]
</table>
</td>
<!-- Ende rechte Seite -->
</tr>
</table>
[{/if}]
</form>
[{include file="bottomnaviitem.tpl"}]
[{include file="bottomitem.tpl"}]

Datei anzeigen

@ -0,0 +1,4 @@
[{$smarty.block.parent}]
<li class="list-group-item[{if $active_link == "d3webauthn"}] active[{/if}]">
<a class="[{* wave *}] list-group-link" href="[{oxgetseourl ident=$oViewConf->getSelfLink()|cat:"cl=d3_account_webauthn"}]" title="[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT"}]">[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT"}]</a>
</li>

Datei anzeigen

@ -0,0 +1,95 @@
[{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}]

Datei anzeigen

@ -0,0 +1,256 @@
[{capture append="oxidBlock_content"}]
[{capture name="javascripts"}]
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))
}
};
document.getElementById('fncname').value = 'registerNewKey';
document.getElementById('authnvalue').value = btoa(JSON.stringify(publicKeyCredential));
document.getElementById('actionform').submit();
}).catch(function(error){
document.getElementById('errorvalue').value = btoa(JSON.stringify(error));
document.getElementById('actionform').submit();
});
}
function deleteItem(id) {
document.getElementById('fncname').value = 'deleteKey';
document.getElementById('oxidvalue').value = id;
document.getElementById('actionform').submit();
}
function toggle(elementId) {
$("#" + elementId).toggle();
}
[{/capture}]
[{oxscript add=$smarty.capture.javascripts}]
<h1 class="page-header">[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT"}]</h1>
<form action="[{$oViewConf->getSelfActionLink()}]" id="actionform" name="d3webauthnform" class="form-horizontal" method="post">
<div class="hidden">
[{$oViewConf->getHiddenSid()}]
[{$oViewConf->getNavFormParams()}]
<input type="hidden" id="fncname" name="fnc" value="">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" id="oxidvalue" name="oxid" value="">
<input type="hidden" id="authnvalue" name="authn" value="">
<input type="hidden" id="errorvalue" name="error" value="">
</div>
</form>
<div class="panel panel-default">
<div class="panel-heading">settings</div>
<div class="panel-body">
<div class="row" style="margin-right: 0">
<div class="col-xs-12 col-md-9">
<input id="authtype_0" type="radio" name="authtype" value="0"> <label for="authtype_0">[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT_TYPE0"}]</label>
</div>
<div class="col-xs-8 col-md-3 progress pull-right" style="padding: 0; margin-bottom: 0">
<div class="progress-bar" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width:70%">
Sicherheit
</div>
</div>
</div>
<div class="row" style="margin-right: 0">
<div class="col-xs-12 col-md-9">
<input id="authtype_1" type="radio" name="authtype" value="1"> <label for="authtype_1">[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT_TYPE1"}]</label>
</div>
<div class="col-xs-8 col-md-3 progress pull-right" style="padding: 0; margin-bottom: 0">
<div class="progress-bar" role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="width:70%">
Sicherheit
</div>
</div>
</div>
<div class="row" style="margin-right: 0">
<div class="col-xs-12 col-md-9">
<input id="authtype_2" type="radio" name="authtype" value="2"> <label for="authtype_2">[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT_TYPE2"}]</label>
</div>
<div class="col-xs-8 col-md-3 progress pull-right" style="padding: 0; margin-bottom: 0">
<div class="progress-bar" role="progressbar" aria-valuenow="30" aria-valuemin="0" aria-valuemax="100" style="width:70%">
Sicherheit
</div>
</div>
</div>
<div class="row" style="margin-right: 0">
<div class="col-xs-12 col-md-9">
<input id="authtype_3" type="radio" name="authtype" value="3"> <label for="authtype_3">[{oxmultilang ident="D3_WEBAUTHN_ACCOUNT_TYPE3"}]</label>
</div>
<div class="col-xs-8 col-md-3 progress pull-right" style="padding: 0; margin-bottom: 0">
<div class="progress-bar" role="progressbar" aria-valuenow="85" aria-valuemin="0" aria-valuemax="100" style="width:70%">
Sicherheit
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">registration</div>
<div class="panel-body">
<button onclick="authnregister();">Register</button>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">registered keys</div>
<div class="panel-body">
<div class="list-group">
[{foreach from=$oView->getCredentialList() item="credential"}]
<a href="#" onclick="toggle('keydetails_[{$credential->getId()}]'); return false;" class="list-group-item">
[{$credential->d3GetName()}] (last used: XX)
</a>
<div class="list-group-item" id="keydetails_[{$credential->getId()}]" style="display: none">
<a onclick="deleteItem('[{$credential->getId()}]'); return false;"><span class="glyphicon glyphicon-pencil">delete</span></a>
</div>
[{/foreach}]
</div>
</div>
</div>
[{if 1 == 0 && false == $totp->getId()}]
<div class="registerNew [{* flow *}] panel panel-default [{* wave *}] card">
<div class="[{* flow *}] panel-heading [{* wave *}] card-header">
[{oxmultilang ident="D3_TOTP_REGISTERNEW"}]
</div>
<div class="[{* flow *}] panel-body [{* wave *}] card-body">
<dl>
<dt>
[{oxmultilang ident="D3_TOTP_QRCODE"}]&nbsp;
</dt>
<dd>
[{$totp->getQrCodeElement()}]
</dd>
</dl>
<p>
[{oxmultilang ident="D3_TOTP_QRCODE_HELP"}]
</p>
<hr>
<dl>
<dt>
<label for="secret">[{oxmultilang ident="D3_TOTP_SECRET"}]</label>
</dt>
<dd>
<textarea rows="3" cols="50" id="secret" name="secret" class="editinput" readonly="readonly">[{$totp->getSecret()}]</textarea>
</dd>
</dl>
<p>
[{oxmultilang ident="D3_TOTP_SECRET_HELP"}]
</p>
<hr>
<dl>
<dt>
<label for="otp">[{oxmultilang ident="D3_TOTP_CURROTP"}]</label>
</dt>
<dd>
<input type="text" class="editinput" size="6" maxlength="6" id="otp" name="otp" value="" [{$readonly}]>
</dd>
</dl>
<p>
[{oxmultilang ident="D3_TOTP_CURROTP_HELP"}]
</p>
</div>
</div>
[{/if}]
[{if 1 == 0 && $totp->getId()}]
[{block name="d3_account_totp_deletenotes"}]
<div class="[{* flow *}] panel panel-default [{* wave *}] card">
<div class="[{* flow *}] panel-heading [{* wave *}] card-header">
[{oxmultilang ident="D3_TOTP_REGISTEREXIST"}]
</div>
<div class="[{* flow *}] panel-body [{* wave *}] card-body">
[{oxmultilang ident="D3_TOTP_REGISTERDELETE_DESC"}]
</div>
</div>
[{/block}]
[{block name="d3_account_totp_backupcodes"}]
<div class="[{* flow *}] panel panel-default [{* wave *}] card">
<div class="[{* flow *}] panel-heading [{* wave *}] card-header">
[{oxmultilang ident="D3_TOTP_BACKUPCODES"}]
</div>
<div class="[{* flow *}] panel-body [{* wave *}] card-body">
[{if $oView->getBackupCodes()}]
[{block name="d3_account_totp_backupcodes_list"}]
<label for="backupcodes">[{oxmultilang ident="D3_TOTP_BACKUPCODES_DESC"}]</label>
<textarea id="backupcodes" rows="10" cols="20">[{$oView->getBackupCodes()}]</textarea>
[{/block}]
[{else}]
[{block name="d3_account_totp_backupcodes_info"}]
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT" args=$oView->getAvailableBackupCodeCount()}]<br>
[{oxmultilang ident="D3_TOTP_AVAILBACKUPCODECOUNT_DESC"}]
[{/block}]
[{/if}]
</div>
</div>
[{/block}]
[{/if}]
[{*
<p class="submitBtn">
<button type="submit" class="btn btn-primary"
[{if $totp->getId()}]
onclick="
if(false === document.getElementById('totp_use').checked && false === confirm('[{oxmultilang ident="D3_TOTP_REGISTERDELETE_CONFIRM"}]')) {return false;}
document.getElementById('fncname').value = 'delete';
"
[{else}]
onclick="document.getElementById('fncname').value = 'create';"
[{/if}]
>
[{oxmultilang ident="D3_TOTP_ACCOUNT_SAVE"}]
</button>
</p>
</form>
[{/block}]
*}]
[{/capture}]
[{capture append="oxidBlock_sidebar"}]
[{include file="page/account/inc/account_menu.tpl" active_link="d3webauthn"}]
[{/capture}]
[{include file="layout/page.tpl" sidebar="Left"}]

Datei anzeigen

@ -0,0 +1,112 @@
[{capture append="oxidBlock_content"}]
[{assign var="template_title" value=""}]
[{if $oView->previousClassIsOrderStep()}]
[{* ordering steps *}]
[{include file="page/checkout/inc/steps.tpl" active=2}]
[{/if}]
<div class="row">
<div class="webauthncol col-xs-12 col-sm-10 col-md-6 [{* flow *}] col-sm-offset-1 col-md-offset-3 [{* wave *}] offset-sm-1 offset-md-3 mainforms">
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="webauthnlogin" id="webauthnlogin">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="checkWebauthnlogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
<input type="hidden" name="keyauth" id="keyauth" value="">
[{$navFormParams}]
[{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;">
</form>
<form action="[{$oViewConf->getSelfActionLink()}]" method="post" name="webauthnlogout" id="webauthnlogout">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="fnc" value="cancelWebauthnlogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
[{$navFormParams}]
<button class="btn btn_cancel" type="submit">
[{oxmultilang ident="WEBAUTHN_CANCEL_LOGIN"}]
</button>
</form>
</div>
</div>
[{if $webauthn_publickey_login}]
[{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('webauthnlogin').submit();
})
.catch(function(error){
// alert('Open your browser console!');
console.log('FAIL', error);
});
[{/capture}]
[{oxscript add=$smarty.capture.webauthn_login}]
[{oxscript}]
[{/if}]
[{oxstyle include=$oViewConf->getModuleUrl('d3webauthn', 'out/flow/src/css/d3webauthnlogin.css')}]
[{oxstyle}]
[{insert name="oxid_tracker" title=$template_title}]
[{/capture}]
[{include file="layout/page.tpl"}]

61
src/IntelliSenseHelper.php Ausführbare Datei
Datei anzeigen

@ -0,0 +1,61 @@
<?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\Component
{
use OxidEsales\Eshop\Application\Component\UserComponent;
class d3_webauthn_UserComponent_parent extends UserComponent {}
}
namespace D3\Webauthn\Modules\Application\Controller
{
use OxidEsales\Eshop\Application\Controller\OrderController;
use OxidEsales\Eshop\Application\Controller\PaymentController;
use OxidEsales\Eshop\Application\Controller\StartController;
use OxidEsales\Eshop\Application\Controller\UserController;
class d3_StartController_Webauthn_parent extends StartController {}
class d3_webauthn_UserController_parent extends UserController {}
class d3_webauthn_OrderController_parent extends OrderController {}
class d3_webauthn_PaymentController_parent extends PaymentController {}
}
namespace D3\Webauthn\Modules\Application\Controller\Admin
{
use OxidEsales\Eshop\Application\Controller\Admin\LoginController;
class d3_LoginController_Webauthn_parent extends LoginController {}
}
namespace D3\Webauthn\Modules\Application\Model
{
use OxidEsales\Eshop\Application\Model\User;
class d3_User_Webauthn_parent extends User {}
}
namespace D3\Webauthn\Modules\Core
{
use OxidEsales\Eshop\Core\Utils;
class d3_webauthn_utils_parent extends Utils {}
}

Datei anzeigen

@ -0,0 +1,204 @@
<?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\Component;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\d3webauthn_conf;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnMissingPublicKeyCredentialRequestOptions;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnWrongAuthException;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Doctrine\DBAL\DBALException;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\UtilsView;
class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
{
/**
* @return string|void
* @throws DBALException
* @throws DatabaseConnectionException
*/
public function login_noredirect()
{
$sUser = Registry::getRequest()->getRequestParameter('lgn_usr');
$oUser = oxNew(User::class);
$q = "SELECT * FROM ".$oUser->getViewName()." WHERE oxusername = ? and oxshopid = ?";
$userId = DatabaseProvider::getDb()->getOne(
$q,
array($sUser, Registry::getConfig()->getActiveShop()->getId())
);
if ($sUser) {
$webauthn = $this->d3GetWebauthnObject();
$webauthn->loadByUserId($userId);
if ($webauthn->isActive()
&& false == Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH)
) {
Registry::getSession()->setVariable(
d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS,
$this->getParent()->getClassKey() != 'd3webauthnlogin' ? $this->getParent()->getClassKey() : 'start');
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER, $oUser->getId());
Registry::getSession()->setVariable(
d3webauthn_conf::WEBAUTHN_SESSION_NAVFORMPARAMS,
$this->getParent()->getViewConfig()->getNavFormParams()
);
//$oUser->d3templogout();
return "d3webauthnlogin";
}
}
parent::login_noredirect();
/** @var d3_User_Webauthn $oUser */
/*
$oUser = $this->getUser();
if ($oUser && $oUser->getId()) {
$webauthn = $this->d3GetWebauthnObject();
$webauthn->loadByUserId($oUser->getId());
if ($webauthn->isActive()
&& false == Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH)
) {
Registry::getSession()->setVariable(
d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS,
$this->getParent()->getClassKey() != 'd3webauthnlogin' ? $this->getParent()->getClassKey() : 'start');
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER, $oUser->getId());
Registry::getSession()->setVariable(
d3webauthn_conf::WEBAUTHN_SESSION_NAVFORMPARAMS,
$this->getParent()->getViewConfig()->getNavFormParams()
);
$oUser->d3templogout();
return "d3webauthnlogin";
}
}
*/
}
/**
* @return d3webauthn
*/
public function d3GetWebauthnObject()
{
return oxNew(d3webauthn::class);
}
/**
* @return bool|string
* @throws DatabaseConnectionException
* @throws d3webauthnMissingPublicKeyCredentialRequestOptions
*/
public function checkWebauthnlogin()
{
$sWebauth = base64_decode(Registry::getRequest()->getRequestParameter('keyauth'));
$sUserId = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER);
$oUser = oxNew(User::class);
$oUser->load($sUserId);
$webauthn = $this->d3GetWebauthnObject();
$webauthn->loadByUserId($sUserId);
try {
if (false == $this->isNoWebauthnOrNoLogin($webauthn) && $this->hasValidWebauthn($sWebauth, $webauthn)) {
$this->d3WebauthnRelogin($oUser, $sWebauth);
$this->d3WebauthnClearSessionVariables();
return false;
}
} catch (d3webauthnWrongAuthException $oEx) {
$this->d3GetUtilsView()->addErrorToDisplay($oEx, false, false, "", 'd3webauthnlogin');
}
return 'd3webauthnlogin';
}
/**
* @return UtilsView
*/
public function d3GetUtilsView()
{
return Registry::getUtilsView();
}
public function cancelWebauthnLogin()
{
$this->d3WebauthnClearSessionVariables();
return false;
}
/**
* @param d3webauthn $webauthn
* @return bool
*/
public function isNoWebauthnOrNoLogin($webauthn)
{
return false == $this->d3GetSession()->getVariable("auth")
|| false == $webauthn->isActive();
}
/**
* @param string $sWebauth
* @param d3webauthn $webauthn
* @return bool
* @throws d3webauthnMissingPublicKeyCredentialRequestOptions
* @throws d3webauthnWrongAuthException
*/
public function hasValidWebauthn($sWebauth, $webauthn)
{
return Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH) ||
(
$sWebauth && $webauthn->verify($sWebauth)
);
}
/**
* @param User $oUser
* @param $sWebauthn
*/
public function d3WebauthnRelogin(User $oUser, $sWebauthn)
{
$this->d3GetSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH, $sWebauthn);
$this->d3GetSession()->setVariable('usr', $oUser->getId());
$this->setUser(null);
$this->setLoginStatus(USER_LOGIN_SUCCESS);
$this->_afterLogin($oUser);
}
public function d3WebauthnClearSessionVariables()
{
$this->d3GetSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS);
$this->d3GetSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER);
$this->d3GetSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_SESSION_NAVFORMPARAMS);
}
/**
* @return Session
*/
public function d3GetSession()
{
return Registry::getSession();
}
}

Datei anzeigen

@ -0,0 +1,148 @@
<?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\Admin;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\d3webauthn_conf;
use D3\Webauthn\Application\Model\Exceptions\d3WebauthnExceptionAbstract;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnMissingPublicKeyCredentialRequestOptions;
use D3\Webauthn\Application\Model\Exceptions\d3webauthnWrongAuthException;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
use OxidEsales\Eshop\Core\UtilsView;
class d3_LoginController_Webauthn extends d3_LoginController_Webauthn_parent
{
/**
* @return string
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function render()
{
$auth = $this->d3GetSession()->getVariable("auth");
$return = parent::render();
if ($auth) {
$webauthn = $this->d3GetWebauthnObject();
$publicKeyCredentialRequestOptions = $webauthn->getCredentialRequestOptions($auth);
$this->addTplParam(
'webauthn_publickey_login',
$publicKeyCredentialRequestOptions
);
$this->addTplParam('request_webauthn', true);
}
return $return;
}
/**
* @return d3webauthn
*/
public function d3GetWebauthnObject()
{
return oxNew(d3webauthn::class);
}
/**
* @return UtilsView
*/
public function d3GetUtilsView()
{
return Registry::getUtilsView();
}
/**
* @return Session
*/
public function d3GetSession()
{
return Registry::getSession();
}
/**
* @return mixed|string
* @throws DatabaseConnectionException
*/
public function checklogin()
{
//$sWebauth = Registry::getRequest()->getRequestEscapedParameter('keyauth');
$sWebauth = base64_decode(Registry::getRequest()->getRequestParameter('keyauth'));
$webauthn = $this->d3GetWebauthnObject();
$webauthn->loadByUserId(Registry::getSession()->getVariable("auth"));
$return = 'login';
try {
if ($this->isNoWebauthnOrNoLogin($webauthn)) {
$return = parent::checklogin();
} elseif ($this->hasValidWebauthn($sWebauth, $webauthn)) {
$this->d3GetSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH, $sWebauth);
$return = "admin_start";
}
} catch (d3webauthnExceptionAbstract $oEx) {
$this->d3GetUtilsView()->addErrorToDisplay($oEx);
}
return $return;
}
/**
* @param d3webauthn $webauthn
* @return bool
*/
public function isNoWebauthnOrNoLogin($webauthn)
{
return false == $this->d3GetSession()->getVariable("auth")
|| false == $webauthn->isActive();
}
/**
* @param string $sWebauth
* @param d3webauthn $webauthn
* @return bool
* @throws d3webauthnMissingPublicKeyCredentialRequestOptions
* @throws d3webauthnWrongAuthException
*/
public function hasValidWebauthn($sWebauth, $webauthn)
{
return Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH) ||
(
$sWebauth && $webauthn->verify($sWebauth)
);
}
public function d3WebauthnCancelLogin()
{
$oUser = $this->d3GetUserObject();
$oUser->logout();
}
/**
* @return User
*/
public function d3GetUserObject()
{
return oxNew(User::class);
}
}

Datei anzeigen

@ -0,0 +1,214 @@
<?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()
);
/*
if (!($user = Registry::getSession()->getUser())) {
$e = oxNew(\Exception::class, 'no user loaded');
throw $e;
}
*/
$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');
}
}

Datei anzeigen

@ -0,0 +1,22 @@
<?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;
class d3_webauthn_OrderController extends d3_webauthn_OrderController_parent
{
use d3_webauthn_getUserTrait;
}

Datei anzeigen

@ -0,0 +1,22 @@
<?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;
class d3_webauthn_PaymentController extends d3_webauthn_PaymentController_parent
{
use d3_webauthn_getUserTrait;
}

Datei anzeigen

@ -0,0 +1,22 @@
<?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;
class d3_webauthn_UserController extends d3_webauthn_UserController_parent
{
use d3_webauthn_getUserTrait;
}

Datei anzeigen

@ -0,0 +1,66 @@
<?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\d3webauthn;
use D3\Webauthn\Application\Model\d3webauthn_conf;
use Doctrine\DBAL\DBALException;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
trait d3_webauthn_getUserTrait
{
/**
* @return bool|object|User
* @throws DatabaseConnectionException
* @throws DBALException
*/
public function getUser()
{
$oUser = parent::getUser();
if ($oUser && $oUser->getId()) {
$webauthn = $this->d3GetWebauthnpObject();
$webauthn->loadByUserId($oUser->getId());
if ($webauthn->isActive()
&& false == $this->d3GetSessionObject()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH)
) {
return false;
}
}
return $oUser;
}
/**
* @return d3webauthn
*/
public function d3GetWebauthnpObject()
{
return oxNew(d3webauthn::class);
}
/**
* @return Session
*/
public function d3GetSessionObject()
{
return Registry::getSession();
}
}

Datei anzeigen

@ -0,0 +1,63 @@
<?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\Model;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\d3webauthn_conf;
use OxidEsales\Eshop\Core\Registry;
class d3_User_Webauthn extends d3_User_Webauthn_parent
{
public function logout()
{
$return = parent::logout();
Registry::getSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH);
Registry::getSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_LOGIN_OBJECT);
Registry::getSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER);
Registry::getSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS);
Registry::getSession()->deleteVariable(d3webauthn_conf::WEBAUTHN_SESSION_NAVFORMPARAMS);
return $return;
}
public function d3templogout()
{
$varname = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH);
$object = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_LOGIN_OBJECT);
$currentUser = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER);
$currentClass = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS);
$navFormParams = Registry::getSession()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_NAVFORMPARAMS);
$return = $this->logout();
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH, $varname);
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_LOGIN_OBJECT, $object);
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTUSER, $currentUser);
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_CURRENTCLASS, $currentClass);
Registry::getSession()->setVariable(d3webauthn_conf::WEBAUTHN_SESSION_NAVFORMPARAMS, $navFormParams);
return $return;
}
/**
* @return d3webauthn
*/
public function d3getWebauthn()
{
return oxNew(d3webauthn::class);
}
}

Datei anzeigen

@ -0,0 +1,69 @@
<?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\Core;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\d3webauthn_conf;
use Doctrine\DBAL\DBALException;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Session;
class d3_webauthn_utils extends d3_webauthn_utils_parent
{
/**
* @return bool
* @throws DBALException
* @throws DatabaseConnectionException
*/
public function checkAccessRights()
{
$blAuth = parent::checkAccessRights();
$userID = $this->d3GetSessionObject()->getVariable("auth");
$webauthnAuth = (bool) $this->d3GetSessionObject()->getVariable(d3webauthn_conf::WEBAUTHN_SESSION_AUTH);
/** @var d3webauthn $webauthn */
$webauthn = $this->d3GetWebauthnObject();
$webauthn->loadByUserId($userID);
if ($blAuth && $webauthn->isActive() && false === $webauthnAuth) {
$this->redirect('index.php?cl=login', true, 302);
if (false == defined('OXID_PHP_UNIT')) {
// @codeCoverageIgnoreStart
exit;
// @codeCoverageIgnoreEnd
}
}
return $blAuth;
}
/**
* @return Session
*/
public function d3GetSessionObject()
{
return Registry::getSession();
}
/**
* @return d3webauthn
*/
public function d3GetWebauthnObject()
{
return oxNew(d3webauthn::class);
}
}

48
src/Setup/Events.php Ausführbare Datei
Datei anzeigen

@ -0,0 +1,48 @@
<?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\Setup;
use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException;
use D3\ModCfg\Application\Model\Install\d3install;
use Doctrine\DBAL\DBALException;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Exception\SystemComponentException;
class Events
{
/**
* @throws d3ShopCompatibilityAdapterException
* @throws DBALException
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws StandardException
* @throws SystemComponentException
*/
public static function onActivate()
{
if (class_exists(d3install::class)) {
d3install::checkUpdateStart();
}
}
public static function onDeactivate()
{
}
}

246
src/Setup/Installation.php Ausführbare Datei
Datei anzeigen

@ -0,0 +1,246 @@
<?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\Setup;
use D3\ModCfg\Application\Model\d3database;
use D3\ModCfg\Application\Model\Install\d3install_updatebase;
use Doctrine\DBAL\DBALException;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\ConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
class Installation extends d3install_updatebase
{
protected $_aUpdateMethods = array(
array('check' => 'doesPublicKeyCredentialTableNotExist',
'do' => 'addPublicKeyCredentialTable'),
array('check' => 'checkFields',
'do' => 'fixFields'),
array('check' => 'checkIndizes',
'do' => 'fixIndizes'),
array('check' => 'checkSEONotExists',
'do' => 'addSEO'),
);
public $aMultiLangTables = array();
public $aFields = array(
'OXID' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'OXID',
'sType' => 'CHAR(32)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'NAME' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'Name',
'sType' => 'VARCHAR(255)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'CREDENTIALID' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'CredentialId',
'sType' => 'BINARY(48)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'TYPE' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'Type',
'sType' => 'CHAR(20)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'TRANSPORTS' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'Transports',
'sType' => 'VARCHAR(255)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'ATTESTATIONTYPE' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'AttestationType',
'sType' => 'CHAR(100)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'TRUSTPATH' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'TrustPath',
'sType' => 'VARCHAR(255)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'AAGUID' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'Aaguid',
'sType' => 'VARCHAR(255)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'PUBLICKEY' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'PublicKey',
'sType' => 'BINARY(77)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'USERHANDLE' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'UserHandle',
'sType' => 'CHAR(36)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'COUNTER' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'Counter',
'sType' => 'INT(5)',
'blNull' => false,
'sDefault' => false,
'sComment' => '',
'sExtra' => '',
'blMultilang' => false,
),
'OXTIMESTAMP' => array(
'sTableName' => 'd3PublicKeyCredential',
'sFieldName' => 'oxtimestamp',
'sType' => 'TIMESTAMP',
'blNull' => false,
'sDefault' => 'CURRENT_TIMESTAMP',
'sComment' => 'Timestamp',
'sExtra' => '',
'blMultilang' => false,
)
);
public $aIndizes = array(
'OXID' => array(
'sTableName' => 'd3PublicKeyCredential',
'sType' => d3database::INDEX_TYPE_PRIMARY,
'sName' => 'PRIMARY',
'aFields' => array(
'OXID' => 'OXID',
),
),
'OXUSERID' => array(
'sTableName' => 'd3PublicKeyCredential',
'sType' => d3database::INDEX_TYPE_UNIQUE,
'sName' => 'CredentialId',
'aFields' => array(
'CredentialId' => 'CredentialId',
),
)
);
protected $_aRefreshMetaModuleIds = array('d3webauthn');
/**
* @return bool
* @throws DBALException
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function doesPublicKeyCredentialTableNotExist()
{
return $this->_checkTableNotExist('d3PublicKeyCredential');
}
/**
* @return bool
* @throws ConnectionException
* @throws DBALException
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function addPublicKeyCredentialTable()
{
$blRet = false;
if ($this->doesPublicKeyCredentialTableNotExist()) {
$this->setInitialExecMethod(__METHOD__);
$blRet = $this->_addTable2(
'd3PublicKeyCredential',
$this->aFields,
$this->aIndizes,
'key credentials',
'InnoDB'
);
}
return $blRet;
}
/**
* @return bool
* @throws DatabaseConnectionException
*/
public function checkSEONotExists()
{
$query = "SELECT 1 FROM " . getViewName('oxseo') . " WHERE oxstdurl = 'index.php?cl=d3_account_webauthn'";
return !DatabaseProvider::getDb()->getOne($query);
}
/**
* @return bool
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
*/
public function addSEO()
{
$query = [
"INSERT INTO `oxseo` (`OXOBJECTID`, `OXIDENT`, `OXSHOPID`, `OXLANG`, `OXSTDURL`, `OXSEOURL`, `OXTYPE`, `OXFIXED`, `OXEXPIRED`, `OXPARAMS`, `OXTIMESTAMP`) VALUES
('ff57646b47249ee33c6b672741ac371a', 'be07f06fe03a4d5d7936f2eac5e3a87b', 1, 1, 'index.php?cl=d3_account_webauthn', 'en/key-authintication/', 'static', 0, 0, '', NOW()),
('ff57646b47249ee33c6b672741ac371a', '220a1af77362196789eeed4741dda184', 1, 0, 'index.php?cl=d3_account_webauthn', 'key-authentisierung/', 'static', 0, 0, '', NOW());"
];
return $this->_executeMultipleQueries($query);
}
}

10
src/menu.xml Ausführbare Datei
Datei anzeigen

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<OX>
<OXMENU id="NAVIGATION_ESHOPADMIN">
<MAINMENU id="mxuadmin">
<SUBMENU id="mxusers" cl="admin_user" list="user_list">
<TAB id="d3mxuser_webauthn" cl="d3user_webauthn" />
</SUBMENU>
</MAINMENU>
</OXMENU>
</OX>

107
src/metadata.php Ausführbare Datei
Datei anzeigen

@ -0,0 +1,107 @@
<?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
*/
// https://github.com/web-auth/webauthn-framework/tree/master/doc
// https://webauthn-doc.spomky-labs.com/
// https://docs.solokeys.io/solo/
use D3\Webauthn\Application\Controller\Admin\d3user_webauthn;
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;
/**
* Metadata version
*/
$sMetadataVersion = '2.1';
$sModuleId = 'd3webauthn';
$logo = '<img src="https://logos.oxidmodule.com/d3logo.svg" alt="(D3)" style="height:1em;width:1em">';
/**
* Module information
*/
$aModule = array(
'id' => $sModuleId,
'title' => $logo.' Webauthn / FIDO2 Login',
'description' => [
'de' => 'Webauthn f&uuml;r OXID eSales Shop',
'en' => 'Webauthn for OXID eSales shop',
],
'version' => '0.0.1',
'author' => 'D&sup3; Data Development (Inh.: Thomas Dartsch)',
'email' => 'support@shopmodule.com',
'url' => 'http://www.oxidmodule.com/',
'extend' => [
UserController::class => d3_webauthn_UserController::class,
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,
],
'controllers' => [
'd3user_webauthn' => d3user_webauthn::class,
'd3webauthnlogin' => d3webauthnlogin::class,
'd3_account_webauthn' => d3_account_webauthn::class
],
'templates' => [
'd3user_webauthn.tpl' => 'd3/webauthn/Application/views/admin/tpl/d3user_webauthn.tpl',
'd3webauthnlogin.tpl' => 'd3/webauthn/Application/views/tpl/d3webauthnlogin.tpl',
'd3_account_webauthn.tpl' => 'd3/webauthn/Application/views/tpl/d3_account_webauthn.tpl',
],
'events' => [
'onActivate' => '\D3\Webauthn\Setup\Events::onActivate',
'onDeactivate' => '\D3\Webauthn\Setup\Events::onDeactivate',
],
'blocks' => [
[
'template' => 'page/account/inc/account_menu.tpl',
'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',
]
]
);

Datei anzeigen

@ -0,0 +1,28 @@
.d3webauthn_icon {
background-color: transparent;
height: 175px;
width: 278px;
}
.d3webauthn_icon .svg-container {
position: relative;
height:0;
width: 100%;
padding: 0 0 50% 0;
}
.d3webauthn_icon .svg-container svg {
fill: darkgrey;
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
}
.d3webauthn_icon .message {
color: darkgrey;
margin-top: 12px;
text-align: center;
font-size: 105%;
}

Datei anzeigen

@ -0,0 +1,45 @@
#login {
display: flex;
flex-flow: column;
}
#login .btn.btn_cancel {
background: silver;
color: black;
}
.d3webauthn_icon {
background-color: transparent;
height: 185px;
width: 100%;
}
.d3webauthn_icon .svg-container {
position: relative;
height:0;
width: 100%;
padding: 0 0 25% 0;
}
.d3webauthn_icon .svg-container svg {
fill: darkgrey;
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
}
.d3webauthn_icon .message {
color: darkgrey;
margin-top: 12px;
text-align: center;
font-size: 105%;
}
#webauthnlogout {
text-align: center;
}
body.cl-d3webauthnlogin .webauthncol {
margin-bottom: 20px;
}

66
src/out/img/fingerprint.svg Ausführbare Datei
Datei anzeigen

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="366.541px" height="366.541px" viewBox="0 0 366.541 366.541" style="enable-background:new 0 0 366.541 366.541;"
xml:space="preserve">
<g>
<g>
<path d="M267.833,0H98.698c-11.225,0-20.35,8.79-20.35,19.597v327.349c0,10.807,9.125,19.595,20.35,19.595h169.135
c11.225,0,20.358-8.788,20.358-19.595V19.597C288.189,8.79,279.058,0,267.833,0z M183.267,346.021
c-6.276,0-11.382-5.099-11.382-11.382c0-6.256,5.106-11.361,11.382-11.361c6.281,0,11.38,5.105,11.38,11.361
C194.647,340.922,189.548,346.021,183.267,346.021z M274.294,303.443H92.265V58.178h182.03V303.443z"/>
</g>
<g>
<path d="M255.658,204.864c-8.323-8.329-21.875-8.322-30.197,0c-5.756,5.757-7.525,14.011-5.325,21.307l-11.525,11.526
c-0.37,0.376-0.521,0.924-0.384,1.437l1.602,6.133l-6.218-1.54c-0.51-0.13-1.044,0.021-1.41,0.391l-2.707,2.71
c-0.366,0.362-0.513,0.883-0.403,1.376l0.773,3.497l-4.306-0.239c-0.417-0.021-0.835,0.13-1.129,0.431l-3.717,3.72
c-0.273,0.271-0.424,0.637-0.438,1.021l-0.267,12.371c-0.014,0.438,0.171,0.862,0.499,1.149c0.335,0.288,0.773,0.418,1.212,0.359
l11.204-1.704c0.314-0.048,0.602-0.191,0.828-0.414l28.685-28.688c7.733,3.165,16.96,1.629,23.229-4.641
C263.98,226.732,263.98,213.187,255.658,204.864z M251.161,231.612l-0.523,0.527l-22.261-22.258l0.526-0.521
c6.133-6.14,16.118-6.14,22.258,0C257.293,215.494,257.293,225.479,251.161,231.612z"/>
<path d="M124.163,144.762c1.179,0.253,2.264,0.231,5.625-2.34c1.494-1.148,3.698-3.08,5.73-5.768l0.896-1.184
c7.62-10.138,25.462-33.873,51.238-32.579c21.249,1.552,36.323,17.602,43.564,46.418c0.254,0.977,0.404,1.579,0.568,2.046
c1.108,4.709,2.964,10.582,5.64,12.517c0.66,0.481,1.427,0.705,2.197,0.705c1.163,0,2.313-0.541,3.046-1.554
c1.17-1.622,0.852-3.869-0.678-5.112c-0.746-0.957-2.136-4.969-2.964-8.554c-0.052-0.214-0.116-0.418-0.209-0.623
c-0.071-0.24-0.209-0.775-0.325-1.252c-1.146-4.572-4.199-16.707-11.601-28.115c-9.562-14.734-22.594-22.799-38.746-23.962
c-0.027-0.007-0.055-0.007-0.082-0.007c-29.771-1.521-49.312,24.467-57.655,35.562l-0.876,1.17
c-2.285,3.019-4.983,4.899-5.916,5.443c-0.314,0.121-0.625,0.294-0.903,0.508c-1.164,0.883-1.738,2.396-1.424,3.826
C121.62,143.327,122.734,144.451,124.163,144.762z"/>
<path d="M218.151,159.151c2.837-7.079,2.901-14.382,0.185-20.054c-2.412-5.058-6.927-8.72-13.038-10.586
c-8.98-2.745-19.854,2.238-29.821,13.653c-0.128,0.137-14.311,15.057-29.211,24.179c-0.108,0.07-11.021,6.604-22.403,9.71
c-1.997,0.539-3.181,2.607-2.635,4.601c0.541,2.004,2.607,3.182,4.604,2.637c12.388-3.369,23.833-10.233,24.332-10.535
c16.158-9.897,30.813-25.487,30.935-25.637c6.059-6.928,15.008-13.572,21.989-11.438c4.113,1.258,6.968,3.497,8.474,6.658
c1.807,3.778,1.67,8.891-0.384,14.024c-3.826,9.557-15.725,21.089-33.52,32.486c-8.925,5.716-31.388,19.746-47.258,26.525
c-1.91,0.814-2.792,3.019-1.977,4.928c0.609,1.421,1.997,2.276,3.448,2.276c0.494,0,0.992-0.093,1.478-0.302
c16.387-7.009,39.28-21.292,48.357-27.106C195.215,186.521,212.511,173.242,218.151,159.151z"/>
<path d="M157.713,131.139c-1.702,1.874-3.23,3.886-4.881,5.809c-2.154,2.527-4.511,4.875-6.973,7.096
c-2.527,2.286-4.574,3.915-7.556,5.631c-3.167,1.816-6.391,3.657-9.671,5.25c-1.129,0.548-2.103,0.912-3.368,1.294
c-1.959,0.582-3.169,2.601-2.622,4.611c0.532,1.938,2.656,3.21,4.611,2.621c3.84-1.156,7.317-3.136,10.778-5.099
c2.931-1.663,5.832-3.161,8.523-5.207c5.243-3.983,9.753-8.638,13.902-13.729c1.682-2.061,3.475-4.023,5.351-5.916
c1.02-1.026,2.081-2.019,3.161-2.993c0.568-0.508,1.136-0.999,1.718-1.485c0.294-0.246,0.589-0.487,0.89-0.732
c0.074-0.063,0.659-0.52,0.806-0.645c2.607-1.999,5.393-3.778,8.323-5.286c2.765-1.422,5.544-2.415,8.911-3.121
c1.115-0.233,1.362-0.26,2.197-0.344c2.032-0.214,3.75-1.545,3.75-3.749c0-1.889-1.711-3.972-3.75-3.758
C178.255,112.808,166.652,121.288,157.713,131.139z"/>
<path d="M128.512,202.989c8.152-2.8,50.01-18.425,73.444-51.284c1.209-1.682,0.814-4.023-0.876-5.228
c-1.68-1.207-4.027-0.813-5.229,0.876c-22.112,30.991-62.006,45.869-69.774,48.549c-0.173,0.055-0.274,0.085-0.31,0.099
c-1.937,0.732-2.914,2.902-2.175,4.839c0.566,1.503,1.99,2.423,3.511,2.423c0.424,0,0.854-0.068,1.278-0.222L128.512,202.989z"/>
<path d="M191.3,211.326c3.183-0.545,10.985,5.006,17.248,9.465c0.835,0.596,1.677,1.198,2.519,1.787
c-0.075-2.857,0.308-5.695,1.108-8.416c-8.604-6.125-16.057-11.282-22.154-10.231c-4.711,0.811-12.943,6.074-25.94,14.646
c-9.37,6.177-19.992,13.179-24.604,14.414c-2.004,0.544-3.188,2.598-2.656,4.603c0.458,1.681,1.973,2.786,3.627,2.786
c0.322,0,0.65-0.052,0.97-0.13c5.789-1.567,15.994-8.289,26.794-15.406C176.934,219.086,187.79,211.934,191.3,211.326z"/>
<path d="M200.061,239.715c0.205-1.595,0.931-3.121,2.108-4.298l1.649-1.646c-7.386-4.441-12.484-6.971-16.297-6.311
c-4.637,0.805-14.383,7.426-30.741,19.366c-2.004,1.458-3.729,2.724-4.933,3.576c-1.689,1.201-2.096,3.535-0.898,5.229
c0.732,1.033,1.889,1.588,3.066,1.588c0.754,0,1.507-0.227,2.163-0.691c1.244-0.883,2.996-2.163,5.025-3.642
c6.514-4.746,23.811-17.378,27.553-18.031c1.646-0.106,6.27,2.413,10.63,5C199.609,239.811,199.829,239.75,200.061,239.715z"/>
<path d="M166.277,264.834c-1.442,1.491-1.401,3.866,0.081,5.311c0.732,0.705,1.672,1.062,2.608,1.062
c0.985,0,1.966-0.38,2.702-1.146c3.153-3.262,7.146-6.797,10.362-9.1l0.063-2.547c0.034-1.923,0.812-3.736,2.184-5.099
l2.354-2.361c-0.431,0.027-0.869,0.062-1.307,0.144C178.76,252.227,168.303,262.747,166.277,264.834z"/>
<path d="M237.787,191.388c1.883,0.801,3.997,0.588,5.134-1.349c0.938-1.588,0.54-4.333-1.349-5.134
c-3.128-1.32-6.399-2.71-9.027-4.9c-2.896-2.411-4.25-5.578-4.853-9.233c-0.78-4.764-8.015-2.738-7.234,1.99
C222.059,182.503,229.225,187.774,237.787,191.388z"/>
<path d="M213.49,186.884c-0.773-1.896-2.471-3.217-4.616-2.621c-1.804,0.492-3.392,2.717-2.622,4.619
c2.413,5.928,6.499,11.067,11.52,15.105c0.407-0.472,0.804-0.958,1.249-1.41c1.396-1.393,2.923-2.597,4.552-3.647
C219.246,195.85,215.488,191.819,213.49,186.884z"/>
</g>
</g>
</svg>

Nachher

Breite:  |  Höhe:  |  Größe: 6.1 KiB