add key assertion in frontend login, translate error messages

This commit is contained in:
Daniel Seifert 2022-10-28 00:45:32 +02:00
parent de4f837a94
commit 71a1f8e53b
Signed by: DanielS
GPG Key ID: 6A513E13AEE66170
15 changed files with 180 additions and 210 deletions

View File

@ -15,11 +15,17 @@
namespace D3\Webauthn\Application\Controller;
use D3\Webauthn\Application\Model\d3webauthn;
use D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\WebauthnConf;
use D3\Webauthn\Application\Model\WebauthnErrors;
use D3\Webauthn\Modules\Application\Component\d3_webauthn_UserComponent;
use D3\Webauthn\Modules\Application\Model\d3_User_Webauthn;
use Exception;
use OxidEsales\Eshop\Application\Controller\FrontendController;
use OxidEsales\Eshop\Application\Model\User;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Utils;
@ -34,8 +40,6 @@ class d3webauthnlogin extends FrontendController
*/
public function render()
{
dumpvar(__METHOD__.__LINE__);
die();
if (Registry::getSession()->hasVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH) ||
false == Registry::getSession()->hasVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER)
) {
@ -61,13 +65,44 @@ die();
public function generateCredentialRequest()
{
$auth = Registry::getSession()->getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER);
$webauthn = oxNew(d3webauthn::class);
$publicKeyCredentialRequestOptions = $webauthn->getCredentialRequestOptions($auth);
/** @var Webauthn $webauthn */
$webauthn = oxNew(Webauthn::class);
$publicKeyCredentialRequestOptions = $webauthn->getRequestOptions();
$this->addTplParam('webauthn_publickey_login', $publicKeyCredentialRequestOptions);
$this->addTplParam('isAdmin', isAdmin());
}
$this->addTplParam(
'webauthn_publickey_login',
$publicKeyCredentialRequestOptions
);
public function assertAuthn()
{
/** @var d3_User_Webauthn $user */
$user = oxNew(User::class);
try {
if (strlen(Registry::getRequest()->getRequestEscapedParameter('error'))) {
$errors = oxNew(WebauthnErrors::class);
throw oxNew(
StandardException::class,
$errors->translateError(Registry::getRequest()->getRequestEscapedParameter('error'))
);
}
if (strlen(Registry::getRequest()->getRequestEscapedParameter('credential'))) {
$credential = Registry::getRequest()->getRequestEscapedParameter('credential');
$webAuthn = oxNew(Webauthn::class);
$webAuthn->assertAuthn($credential);
$user->load(Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER));
/** @var d3_webauthn_UserComponent $userCmp */
$userCmp = $this->getComponent('oxcmp_user');
$userCmp->d3WebauthnRelogin($user, $credential);
}
} catch (Exception $e) {
Registry::getUtilsView()->addErrorToDisplay($e->getMessage());
$user->logout();
$this->getUtils()->redirect('index.php?cl=start');
}
}
/**

View File

@ -17,6 +17,7 @@
namespace D3\Webauthn\Application\Model\Credential;
use DateTime;
use Doctrine\DBAL\Query\QueryBuilder;
use OxidEsales\Eshop\Core\Model\BaseModel;
use OxidEsales\Eshop\Core\Registry;
@ -80,16 +81,6 @@ class PublicKeyCredential extends BaseModel
{
return unserialize(hex2bin($this->__get($this->_getFieldLongName('credential'))->rawValue));
}
/*
public function setPublicKey($publicKey)
{
$this->assign(['PublicKey' => $publicKey]);
}
public function getPublicKey()
{
return $this->__get($this->_getFieldLongName('PublicKey'))->rawValue;
}
/**
* @param PublicKeyCredentialSource $publicKeyCredentialSource
@ -100,18 +91,16 @@ class PublicKeyCredential extends BaseModel
{
// will save on every successfully assertion, set id to prevent duplicated database entries
$id = $this->getIdByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId());
$this->setId($id);
if ($this->exists($id)) {
$this->load($id);
}
$this->setShopId(Registry::getConfig()->getShopId());
$this->setUserId($publicKeyCredentialSource->getUserHandle());
$this->setCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId());
$this->setCredential($publicKeyCredentialSource);
$this->setName($keyName ?: $this->getName());
// ToDo: required??
$this->assign([
'pubkey_hex' => bin2hex($publicKeyCredentialSource->getCredentialPublicKey()),
]);
$this->setName($keyName ?: $this->getName() ?: (new DateTime())->format('Y-m-d H:i:s'));
$this->save();
}

View File

@ -75,8 +75,8 @@ class Webauthn
$userEntity = $user->d3GetWebauthnUserEntity();
// Get the list of authenticators associated to the user
$credentialSourceRepository = oxNew(PublicKeyCredentials::class);
$credentialSources = $credentialSourceRepository->findAllForUserEntity($userEntity);
$credentialList = oxNew(PublicKeyCredentialList::class);
$credentialSources = $credentialList->findAllForUserEntity($userEntity);
// Convert the Credential Sources into Public Key Credential Descriptors
$allowedCredentials = array_map(function (PublicKeyCredentialSource $credential) {
@ -139,47 +139,30 @@ class Webauthn
public function assertAuthn(string $response)
{
try {
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory,
$psr17Factory,
$psr17Factory,
$psr17Factory
);
$serverRequest = $creator->fromGlobals();
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory,
$psr17Factory,
$psr17Factory,
$psr17Factory
);
$serverRequest = $creator->fromGlobals();
/** @var d3_User_Webauthn $user */
$user = oxNew(User::class);
$user->load('oxdefaultadmin');
$userEntity = $user->d3GetWebauthnUserEntity();
/** @var d3_User_Webauthn $user */
$user = oxNew(User::class);
$user->load('oxdefaultadmin');
$userEntity = $user->d3GetWebauthnUserEntity();
$publicKeySource = $this->getServer()->loadAndCheckAssertionResponse(
html_entity_decode($response),
Registry::getSession()->getVariable(self::SESSION_ASSERTION_OPTIONS),
$userEntity,
$serverRequest
);
/*
dumpvar($publicKeySource);
dumpvar(serialize($publicKeySource));
dumpvar(unserialize(serialize($publicKeySource)));
echo "<hr>";
dumpvar(bin2hex(serialize($publicKeySource)));
dumpvar(unserialize(hex2bin(bin2hex(serialize($publicKeySource)))));
*/
dumpvar('successfully');
} catch (\Exception $e) {
dumpvar($e->getMessage());
dumpvar($e);
die();
}
$this->getServer()->loadAndCheckAssertionResponse(
html_entity_decode($response),
Registry::getSession()->getVariable(self::SESSION_ASSERTION_OPTIONS),
$userEntity,
$serverRequest
);
}
/**
* @param $userId
* @return bool
*/
public function isActive($userId): bool
@ -198,7 +181,6 @@ class Webauthn
$user = oxNew(User::class);
$user->load($userId);
$entity = $user->d3GetWebauthnUserEntity();
$credentionList = oxNew(PublicKeyCredentialList::class);
$list = $credentionList->findAllForUserEntity($entity);

View File

@ -20,6 +20,8 @@ namespace D3\Webauthn\Application\Model\Webauthn;
use D3\Webauthn\Application\Model\Credential\publicKeyCredential;
use Webauthn\PublicKeyCredentialSource;
/** @deprecated */
class d3PublicKeyCredentialSource extends PublicKeyCredentialSource
{
/**

View File

@ -17,9 +17,9 @@ namespace D3\Webauthn\Application\Model;
class WebauthnConf
{
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';
const WEBAUTHN_SESSION_AUTH = 'webauthn_auth'; // has valid webauthn, user is logged in completly
const WEBAUTHN_LOGIN_OBJECT = 'authnloginobject'; // webauthn register options, required for credential check
const WEBAUTHN_SESSION_CURRENTUSER = 'd3webauthnCurrentUser'; // oxid assigned to user from entered username
const WEBAUTHN_SESSION_CURRENTCLASS = 'd3webauthnCurrentClass'; // no usage
const WEBAUTHN_SESSION_NAVFORMPARAMS = 'd3webauthnNavFormParams'; // no usage
}

View File

@ -2,15 +2,53 @@
namespace D3\Webauthn\Application\Model;
use OxidEsales\Eshop\Core\Registry;
class WebauthnErrors
{
public const INVALIDSTATE = 'invalidstateerror';
public const NOTALLWED = 'notallowederror';
public const ABORT = 'aborterror';
public const CONSTRAINT = 'constrainterror';
public const NOTSUPPORTED = 'notsupporederror';
public const UNKNOWN = 'unknownerror';
public const NOPUBKEYSUPPORT= 'd3nopublickeycredentialsupportederror';
/**
* @see https://webidl.spec.whatwg.org/
* @param $msg
* @return mixed|string
*/
public function translateError($msg)
{
switch ($msg) {
case 'InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable':
return 'A key from this token is already saved';
$lang = Registry::getLang();
switch ($this->getErrIdFromMessage($msg)) {
case self::INVALIDSTATE:
return $lang->translateString('D3_WEBAUTHN_ERR_INVALIDSTATE', null, true);
case self::NOTALLWED:
return $lang->translateString('D3_WEBAUTHN_ERR_NOTALLOWED', null, true);
case self::ABORT:
return $lang->translateString('D3_WEBAUTHN_ERR_ABORT', null, true);
case self::CONSTRAINT:
return $lang->translateString('D3_WEBAUTHN_ERR_CONSTRAINT', null, true);
case self::NOTSUPPORTED:
return $lang->translateString('D3_WEBAUTHN_ERR_NOTSUPPORTED', null, true);
case self::UNKNOWN:
return $lang->translateString('D3_WEBAUTHN_ERR_UNKNOWN', null, true);
case self::NOPUBKEYSUPPORT:
return $lang->translateString('D3_WEBAUTHN_ERR_NOPUBKEYSUPPORT', null, true);
}
return $msg;
}
/**
* @param string $msg
* @return string
*/
public function getErrIdFromMessage(string $msg): string
{
return trim(strtolower(substr($msg, 0, strpos($msg, ':'))));
}
}

View File

@ -146,7 +146,7 @@ class d3webauthn extends BaseModel
$user = $this->getUser();
$userEntity = new d3PublicKeyCredentialUserEntity($user);
dumpvar($userEntity);
$allowedCredentials = [];
$credentialSourceRepository = oxNew(d3PublicKeyCredentialSourceRepository::class);
/** @var d3PublicKeyCredentialSource $credentialSource */
@ -159,7 +159,7 @@ dumpvar($userEntity);
PublicKeyCredentialRequestOptions::USER_VERIFICATION_REQUIREMENT_PREFERRED, // Default value
$allowedCredentials
);
dumpvar($publicKeyCredentialRequestOptions);
$requestOptions = json_encode($publicKeyCredentialRequestOptions, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_LOGIN_OBJECT, $publicKeyCredentialRequestOptions);

View File

@ -33,27 +33,11 @@ $aLang = [
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registrierte Schlüssel',
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'Die Verwendung von Sicherheitsschlüsseln ist nur bei gesicherten Verbindungen (https) möglich.',
/*
'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.',
*/
'D3_WEBAUTHN_ERR_INVALIDSTATE' => 'Der Schlüssel vom Token kann nicht oder nicht mehr verwendet werden. Möglicherweise wurde dieser in Ihrem Konto schon einmal gespeichert.',
'D3_WEBAUTHN_ERR_NOTALLOWED' => 'Die Anfrage wurde vom Browser oder der Plattform nicht zugelassen. Möglicherweise fehlen Berechtigungen oder die Zeit ist abgelaufen.',
'D3_WEBAUTHN_ERR_ABORT' => 'Die Aktion wurde vom Browser oder der Plattform abgebrochen.',
'D3_WEBAUTHN_ERR_CONSTRAINT' => 'Die Aktion konnte vom authentisierenden Gerät nicht durchgeführt werden.',
'D3_WEBAUTHN_ERR_NOTSUPPORTED' => 'Die Aktion wird nicht unterstützt.',
'D3_WEBAUTHN_ERR_UNKNOWN' => 'Die Aktion wurde wegen eines unbekannten Fehlers abgebrochen.',
'D3_WEBAUTHN_ERR_NOPUBKEYSUPPORT' => 'Ihr Browser unterstützt die Verwendung von Hardwareschlüsseln leider nicht.',
];

View File

@ -33,27 +33,12 @@ $aLang = [
'D3_WEBAUTHN_REGISTEREDKEYS' => 'registered keys',
'D3_WEBAUTHN_ERR_UNSECURECONNECTION' => 'The use of security keys is only possible with secured connections (https).',
/*
'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_WEBAUTHN_ERR_INVALIDSTATE' => 'The key from the token cannot be used or can no longer be used. It may have been stored in your account before.',
'D3_WEBAUTHN_ERR_NOTALLOWED' => 'The request was not allowed by the browser or the platform. Possibly permissions are missing or the time has expired.',
'D3_WEBAUTHN_ERR_ABORT' => 'The action was aborted by the browser or the platform.',
'D3_WEBAUTHN_ERR_CONSTRAINT' => 'The action could not be performed by the authenticating device.',
'D3_WEBAUTHN_ERR_NOTSUPPORTED' => 'The action is not supported.',
'D3_WEBAUTHN_ERR_UNKNOWN' => 'The action was cancelled due to an unknown error.',
'D3_WEBAUTHN_ERR_NOPUBKEYSUPPORT' => 'Unfortunately, your browser does not support the use of hardware keys.',
'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.',
*/
];

View File

@ -8,29 +8,19 @@
<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()}]
[{if !empty($Errors.default)}]
[{include file="inc_error.tpl" Errorlist=$Errors.default}]
[{/if}]
<input type="hidden" name="fnc" value="checkWebauthnlogin">
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
<input type="hidden" name="keyauth" id="keyauth" value="">
[{$navFormParams}]
[{include file="js_login.tpl"}]
[{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 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()}]
@ -38,71 +28,13 @@
<input type="hidden" name="cl" value="[{$oView->getPreviousClass()}]">
[{$navFormParams}]
<button class="btn btn_cancel" type="submit">
<button class="btn btn_cancel btn-outline-danger" 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}]

View File

@ -0,0 +1,26 @@
[{*** require creationOptions variable containing ... ***}]
[{oxscript include=$oViewConf->getModuleUrl('d3webauthn', 'out/src/js/webauthn.js')}]
[{capture name="d3script"}]
var requestOptions = [{$webauthn_publickey_login}];
requestCredentials(requestOptions);
[{/capture}]
[{oxscript add=$smarty.capture.d3script}]
[{if $isAdmin}]
[{assign var="action" value=$oViewConf->getSelfLink()}]
[{assign var="formNavParams" value=""}]
[{else}]
[{assign var="action" value=$oViewConf->getSelfActionLink()}]
[{assign var="formNavParams" value=""}]
[{/if}]
<form id="webauthn" action="[{$action}]" method="post">
[{$oViewConf->getHiddenSid()}]
[{$formNavParams}]
<input type="hidden" name="fnc" value="assertAuthn">
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" name="credential" value=''>
<input type="hidden" name="error" value=''>
</form>

View File

@ -45,7 +45,7 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
/** @var QueryBuilder $qb */
$qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create();
$qb->select('*')
$qb->select('oxid')
->from($user->getViewName())
->where(
$qb->expr()->and(
@ -62,15 +62,16 @@ class d3_webauthn_UserComponent extends d3_webauthn_UserComponent_parent
$userId = $qb->execute()->fetchOne();
if ($lgn_user) {
if ($lgn_user && $userId) {
$webauthn = $this->d3GetWebauthnObject();
if ($webauthn->isActive($userId)
&& false == Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH)
) {
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS,
$this->getParent()->getClassKey() != 'd3webauthnlogin' ? $this->getParent()->getClassKey() : 'start');
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $user->getId());
Registry::getSession()->setVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $userId);
Registry::getSession()->setVariable(
WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS,
$this->getParent()->getViewConfig()->getNavFormParams()

View File

@ -35,7 +35,6 @@ class Events
`OXSHOPID` int(11) NOT NULL,
`NAME` varchar(100) NOT NULL,
`CREDENTIALID` char(100) NOT NULL,
`PUBKEY_HEX` char(150) NOT NULL,
`CREDENTIAL` varchar(2000) NOT NULL,
`OXTIMESTAMP` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`OXID`),

View File

@ -84,6 +84,7 @@ $aModule = array(
'd3_account_webauthn.tpl' => 'd3/oxwebauthn/Application/views/tpl/d3_account_webauthn.tpl',
'js_create.tpl' => 'd3/oxwebauthn/Application/views/tpl/inc/js_create.tpl',
'js_login.tpl' => 'd3/oxwebauthn/Application/views/tpl/inc/js_login.tpl',
],
'events' => [
'onActivate' => '\D3\Webauthn\Setup\Events::onActivate',

View File

@ -1,5 +1,6 @@
if (!window.PublicKeyCredential) {
console.error('no window pubkeycred available');
document.getElementById('webauthn').error.value = 'd3NoPublicKeyCredentialSupportedError: aborting';
document.getElementById('webauthn').submit();
}
const base64UrlDecode = (input) => {
@ -152,13 +153,11 @@ const createCredentials = (publicKey) => {
const requestCredentials = (publicKey) => {
"use strict";
console.log('--AB--');
prepareOptions(publicKey);
navigator.credentials.get({publicKey: publicKey})
.then(function (authenticateInfo) {
console.log(authenticateInfo);
// Send authenticate info to server for verification.
var cred = {
id: authenticateInfo.id,
@ -171,14 +170,11 @@ console.log(authenticateInfo);
},
type: authenticateInfo.type
};
console.log(cred);
document.getElementById('webauthn').credential.value = JSON.stringify(cred);
document.getElementById('webauthn').submit();
}).catch(function (err) {
console.log('--2--');
console.log('WebAuthn auth: ' + err);
// No acceptable authenticator or user refused consent. Handle appropriately.
document.getElementById('webauthn').error.value = err;
document.getElementById('webauthn').submit();
});
}