add key assertion in frontend login, translate error messages
This commit is contained in:
parent
de4f837a94
commit
71a1f8e53b
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
/**
|
||||
|
@ -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
|
||||
}
|
@ -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, ':'))));
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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.',
|
||||
];
|
||||
|
@ -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.',
|
||||
*/
|
||||
];
|
||||
|
@ -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}]
|
||||
|
||||
|
26
src/Application/views/tpl/inc/js_login.tpl
Normal file
26
src/Application/views/tpl/inc/js_login.tpl
Normal 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>
|
@ -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()
|
||||
|
@ -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`),
|
||||
|
@ -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',
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user