migrating from u2f-api.js to webauthn
parent
e2b4b6f6bc
commit
d1d134038f
|
@ -1142,7 +1142,7 @@ function set_tfa($_data) {
|
|||
global $yubi;
|
||||
global $u2f;
|
||||
global $tfa;
|
||||
$_data_log = $_data;
|
||||
$_data_log = $_data["tfa_method"];
|
||||
!isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*';
|
||||
$username = $_SESSION['mailcow_cc_username'];
|
||||
if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
|
||||
|
@ -1183,6 +1183,8 @@ function set_tfa($_data) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch ($_data["tfa_method"]) {
|
||||
case "yubi_otp":
|
||||
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
||||
|
@ -1240,14 +1242,18 @@ function set_tfa($_data) {
|
|||
'msg' => array('object_modified', htmlspecialchars($username))
|
||||
);
|
||||
break;
|
||||
// u2f - deprecated, should be removed
|
||||
case "u2f":
|
||||
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
||||
try {
|
||||
$reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($_data['token']));
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'u2f'");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`) VALUES (?, ?, 'u2f', ?, ?, ?, ?, '1')");
|
||||
$stmt->execute(array($username, $key_id, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_data_log),
|
||||
|
@ -1286,6 +1292,29 @@ function set_tfa($_data) {
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "webauthn":
|
||||
$key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
|
||||
|
||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'webauthn'");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
|
||||
VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
|
||||
$stmt->execute(array(
|
||||
$username,
|
||||
$key_id,
|
||||
base64_encode($_data['registration']->credentialId),
|
||||
$_data['registration']->credentialPublicKey,
|
||||
$_data['registration']->certificate,
|
||||
0
|
||||
));
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_data_log),
|
||||
'msg' => array('object_modified', $username)
|
||||
);
|
||||
break;
|
||||
case "none":
|
||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
|
@ -1516,6 +1545,7 @@ function get_tfa($username = null) {
|
|||
}
|
||||
return $data;
|
||||
break;
|
||||
// u2f - deprecated, should be removed
|
||||
case "u2f":
|
||||
$data['name'] = "u2f";
|
||||
$data['pretty'] = "Fido U2F";
|
||||
|
@ -1534,7 +1564,7 @@ function get_tfa($username = null) {
|
|||
$data['pretty'] = "HMAC-based OTP";
|
||||
return $data;
|
||||
break;
|
||||
case "totp":
|
||||
case "totp":
|
||||
$data['name'] = "totp";
|
||||
$data['pretty'] = "Time-based OTP";
|
||||
$stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username");
|
||||
|
@ -1546,7 +1576,20 @@ function get_tfa($username = null) {
|
|||
$data['additional'][] = $row;
|
||||
}
|
||||
return $data;
|
||||
break;
|
||||
break;
|
||||
case "webauthn":
|
||||
$data['name'] = "webauthn";
|
||||
$data['pretty'] = "WebAuthn";
|
||||
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'webauthn' AND `username` = :username");
|
||||
$stmt->execute(array(
|
||||
':username' => $username,
|
||||
));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$data['additional'][] = $row;
|
||||
}
|
||||
return $data;
|
||||
break;
|
||||
default:
|
||||
$data['name'] = 'none';
|
||||
$data['pretty'] = "-";
|
||||
|
@ -1560,140 +1603,261 @@ function get_tfa($username = null) {
|
|||
return $data;
|
||||
}
|
||||
}
|
||||
function verify_tfa_login($username, $token) {
|
||||
global $pdo;
|
||||
global $yubi;
|
||||
global $u2f;
|
||||
global $tfa;
|
||||
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
||||
WHERE `username` = :username AND `active` = '1'");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
function verify_tfa_login($username, $_data, $WebAuthn) {
|
||||
global $pdo;
|
||||
global $yubi;
|
||||
global $u2f;
|
||||
global $tfa;
|
||||
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
||||
WHERE `username` = :username AND `active` = '1'");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
switch ($row["authmech"]) {
|
||||
case "yubi_otp":
|
||||
if (!ctype_alnum($token) || strlen($token) != 44) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('yotp_verification_failed', 'token length error')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$yubico_modhex_id = substr($token, 0, 12);
|
||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||
WHERE `username` = :username
|
||||
AND `authmech` = 'yubi_otp'
|
||||
AND `active`='1'
|
||||
AND `secret` LIKE :modhex");
|
||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$yubico_auth = explode(':', $row['secret']);
|
||||
$yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]);
|
||||
$yauth = $yubi->verify($token);
|
||||
if (PEAR::isError($yauth)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('yotp_verification_failed', $yauth->getMessage())
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
$_SESSION['tfa_id'] = $row['id'];
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'verified_yotp_login'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('yotp_verification_failed', 'unknown')
|
||||
);
|
||||
return false;
|
||||
break;
|
||||
case "u2f":
|
||||
try {
|
||||
$reg = $u2f->doAuthenticate(json_decode($_SESSION['authReq']), get_u2f_registrations($username), json_decode($token));
|
||||
$stmt = $pdo->prepare("SELECT `id` FROM `tfa` WHERE `keyHandle` = ?");
|
||||
$stmt->execute(array($reg->keyHandle));
|
||||
$row_key_id = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$_SESSION['tfa_id'] = $row_key_id['id'];
|
||||
$_SESSION['authReq'] = null;
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'verified_u2f_login'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('u2f_verification_failed', $e->getMessage())
|
||||
);
|
||||
$_SESSION['regReq'] = null;
|
||||
return false;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('u2f_verification_failed', 'unknown')
|
||||
);
|
||||
return false;
|
||||
break;
|
||||
case "hotp":
|
||||
return false;
|
||||
break;
|
||||
case "totp":
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||
WHERE `username` = :username
|
||||
AND `authmech` = 'totp'
|
||||
AND `active`='1'");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($rows as $row) {
|
||||
if ($tfa->verifyCode($row['secret'], $_POST['token']) === true) {
|
||||
$_SESSION['tfa_id'] = $row['id'];
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
switch ($row["authmech"]) {
|
||||
case "yubi_otp":
|
||||
if (!ctype_alnum($_data['token']) || strlen($_data['token']) != 44) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('yotp_verification_failed', 'token length error')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
$yubico_modhex_id = substr($_data['token'], 0, 12);
|
||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||
WHERE `username` = :username
|
||||
AND `authmech` = 'yubi_otp'
|
||||
AND `active`='1'
|
||||
AND `secret` LIKE :modhex");
|
||||
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$yubico_auth = explode(':', $row['secret']);
|
||||
$yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]);
|
||||
$yauth = $yubi->verify($_data['token']);
|
||||
if (PEAR::isError($yauth)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('yotp_verification_failed', $yauth->getMessage())
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
$_SESSION['tfa_id'] = $row['id'];
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'verified_yotp_login'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('yotp_verification_failed', 'unknown')
|
||||
);
|
||||
return false;
|
||||
break;
|
||||
case "hotp":
|
||||
return false;
|
||||
break;
|
||||
case "totp":
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
|
||||
WHERE `username` = :username
|
||||
AND `authmech` = 'totp'
|
||||
AND `active`='1'");
|
||||
$stmt->execute(array(':username' => $username));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($rows as $row) {
|
||||
if ($tfa->verifyCode($row['secret'], $_data['token']) === true) {
|
||||
$_SESSION['tfa_id'] = $row['id'];
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'verified_totp_login'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'totp_verification_failed'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
// u2f - deprecated, should be removed
|
||||
case "u2f":
|
||||
$tokenData = json_decode($_data['token']);
|
||||
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
|
||||
$authenticatorData = base64_decode($tokenData->authenticatorData);
|
||||
$signature = base64_decode($tokenData->signature);
|
||||
$id = base64_decode($tokenData->id);
|
||||
$challenge = $_SESSION['challenge'];
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `keyHandle` = :tokenId");
|
||||
$stmt->execute(array(':tokenId' => $tokenData->id));
|
||||
$process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($process_webauthn) || empty($process_webauthn['publicKey']) || empty($process_webauthn['username'])) return false;
|
||||
|
||||
if ($process_webauthn['publicKey'] === false) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', 'publicKey not found')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
||||
}
|
||||
catch (Throwable $ex) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', $ex->getMessage())
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($obj_props['superadmin'] === 1) {
|
||||
$_SESSION["mailcow_cc_role"] = "admin";
|
||||
}
|
||||
elseif ($obj_props['superadmin'] === 0) {
|
||||
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row['username'] == $process_webauthn['username']) {
|
||||
$_SESSION["mailcow_cc_role"] = "user";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', 'user who requests does not match with sql entry')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
||||
$_SESSION['tfa_id'] = $process_webauthn['key_id'];
|
||||
$_SESSION['authReq'] = null;
|
||||
unset($_SESSION["challenge"]);
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array("webauthn_login"),
|
||||
'msg' => array('logged_in_as', $process_webauthn['username'])
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
case "webauthn":
|
||||
$tokenData = json_decode($_data['token']);
|
||||
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
|
||||
$authenticatorData = base64_decode($tokenData->authenticatorData);
|
||||
$signature = base64_decode($tokenData->signature);
|
||||
$id = base64_decode($tokenData->id);
|
||||
$challenge = $_SESSION['challenge'];
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `keyHandle` = :tokenId");
|
||||
$stmt->execute(array(':tokenId' => $tokenData->id));
|
||||
$process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($process_webauthn) || empty($process_webauthn['publicKey']) || empty($process_webauthn['username'])) return false;
|
||||
|
||||
if ($process_webauthn['publicKey'] === false) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', 'publicKey not found')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);
|
||||
}
|
||||
catch (Throwable $ex) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', $ex->getMessage())
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($obj_props['superadmin'] === 1) {
|
||||
$_SESSION["mailcow_cc_role"] = "admin";
|
||||
}
|
||||
elseif ($obj_props['superadmin'] === 0) {
|
||||
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
||||
}
|
||||
else {
|
||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
||||
$stmt->execute(array(':username' => $process_webauthn['username']));
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($row['username'] == $process_webauthn['username']) {
|
||||
$_SESSION["mailcow_cc_role"] = "user";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('webauthn_verification_failed', 'user who requests does not match with sql entry')
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$_SESSION["mailcow_cc_username"] = $process_webauthn['username'];
|
||||
$_SESSION['tfa_id'] = $process_webauthn['key_id'];
|
||||
$_SESSION['authReq'] = null;
|
||||
unset($_SESSION["challenge"]);
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array("webauthn_login"),
|
||||
'msg' => array('logged_in_as', $process_webauthn['username'])
|
||||
);
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'verified_totp_login'
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'totp_verification_failed'
|
||||
);
|
||||
return false;
|
||||
'msg' => 'unknown_tfa_method'
|
||||
);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => array('mysql_error', $e)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $username, '*'),
|
||||
'msg' => 'unknown_tfa_method'
|
||||
);
|
||||
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function admin_api($access, $action, $data = null) {
|
||||
global $pdo;
|
||||
|
@ -1955,6 +2119,7 @@ function rspamd_ui($action, $data = null) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
// u2f - deprecated, should be removed
|
||||
function get_u2f_registrations($username) {
|
||||
global $pdo;
|
||||
$sel = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = ? AND `active` = '1'");
|
||||
|
|
|
@ -696,7 +696,7 @@ function init_db_schema() {
|
|||
"id" => "INT NOT NULL AUTO_INCREMENT",
|
||||
"key_id" => "VARCHAR(255) NOT NULL",
|
||||
"username" => "VARCHAR(255) NOT NULL",
|
||||
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp')",
|
||||
"authmech" => "ENUM('yubi_otp', 'u2f', 'hotp', 'totp', 'webauthn')",
|
||||
"secret" => "VARCHAR(255) DEFAULT NULL",
|
||||
"keyHandle" => "VARCHAR(255) DEFAULT NULL",
|
||||
"publicKey" => "VARCHAR(255) DEFAULT NULL",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace WebAuthn\Attestation;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\CBOR\CborDecoder;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\CBOR\CborDecoder;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author Lukas Buchs
|
||||
|
@ -12,6 +12,7 @@ use WebAuthn\Binary\ByteBuffer;
|
|||
class AttestationObject {
|
||||
private $_authenticatorData;
|
||||
private $_attestationFormat;
|
||||
private $_attestationFormatName;
|
||||
|
||||
public function __construct($binary , $allowedFormats) {
|
||||
$enc = CborDecoder::decode($binary);
|
||||
|
@ -29,13 +30,15 @@ class AttestationObject {
|
|||
}
|
||||
|
||||
$this->_authenticatorData = new AuthenticatorData($enc['authData']->getBinaryString());
|
||||
$this->_attestationFormatName = $enc['fmt'];
|
||||
|
||||
// Format ok?
|
||||
if (!in_array($enc['fmt'], $allowedFormats)) {
|
||||
throw new WebAuthnException('invalid atttestation format: ' . $enc['fmt'], WebAuthnException::INVALID_DATA);
|
||||
if (!in_array($this->_attestationFormatName, $allowedFormats)) {
|
||||
throw new WebAuthnException('invalid atttestation format: ' . $this->_attestationFormatName, WebAuthnException::INVALID_DATA);
|
||||
}
|
||||
|
||||
switch ($enc['fmt']) {
|
||||
|
||||
switch ($this->_attestationFormatName) {
|
||||
case 'android-key': $this->_attestationFormat = new Format\AndroidKey($enc, $this->_authenticatorData); break;
|
||||
case 'android-safetynet': $this->_attestationFormat = new Format\AndroidSafetyNet($enc, $this->_authenticatorData); break;
|
||||
case 'apple': $this->_attestationFormat = new Format\Apple($enc, $this->_authenticatorData); break;
|
||||
|
@ -47,6 +50,14 @@ class AttestationObject {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the attestation format name
|
||||
* @return string
|
||||
*/
|
||||
public function getAttestationFormatName() {
|
||||
return $this->_attestationFormatName;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the attestation public key in PEM format
|
||||
* @return AuthenticatorData
|
||||
|
@ -72,16 +83,19 @@ class AttestationObject {
|
|||
$issuer = '';
|
||||
if ($pem) {
|
||||
$certInfo = \openssl_x509_parse($pem);
|
||||
if (\is_array($certInfo) && \is_array($certInfo['issuer'])) {
|
||||
if ($certInfo['issuer']['CN']) {
|
||||
$issuer .= \trim($certInfo['issuer']['CN']);
|
||||
if (\is_array($certInfo) && \array_key_exists('issuer', $certInfo) && \is_array($certInfo['issuer'])) {
|
||||
|
||||
$cn = $certInfo['issuer']['CN'] ?? '';
|
||||
$o = $certInfo['issuer']['O'] ?? '';
|
||||
$ou = $certInfo['issuer']['OU'] ?? '';
|
||||
|
||||
if ($cn) {
|
||||
$issuer .= $cn;
|
||||
}
|
||||
if ($certInfo['issuer']['O'] || $certInfo['issuer']['OU']) {
|
||||
if ($issuer) {
|
||||
$issuer .= ' (' . \trim($certInfo['issuer']['O'] . ' ' . $certInfo['issuer']['OU']) . ')';
|
||||
} else {
|
||||
$issuer .= \trim($certInfo['issuer']['O'] . ' ' . $certInfo['issuer']['OU']);
|
||||
}
|
||||
if ($issuer && ($o || $ou)) {
|
||||
$issuer .= ' (' . trim($o . ' ' . $ou) . ')';
|
||||
} else {
|
||||
$issuer .= trim($o . ' ' . $ou);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,16 +112,19 @@ class AttestationObject {
|
|||
$subject = '';
|
||||
if ($pem) {
|
||||
$certInfo = \openssl_x509_parse($pem);
|
||||
if (\is_array($certInfo) && \is_array($certInfo['subject'])) {
|
||||
if ($certInfo['subject']['CN']) {
|
||||
$subject .= \trim($certInfo['subject']['CN']);
|
||||
if (\is_array($certInfo) && \array_key_exists('subject', $certInfo) && \is_array($certInfo['subject'])) {
|
||||
|
||||
$cn = $certInfo['subject']['CN'] ?? '';
|
||||
$o = $certInfo['subject']['O'] ?? '';
|
||||
$ou = $certInfo['subject']['OU'] ?? '';
|
||||
|
||||
if ($cn) {
|
||||
$subject .= $cn;
|
||||
}
|
||||
if ($certInfo['subject']['O'] || $certInfo['subject']['OU']) {
|
||||
if ($subject) {
|
||||
$subject .= ' (' . \trim($certInfo['subject']['O'] . ' ' . $certInfo['subject']['OU']) . ')';
|
||||
} else {
|
||||
$subject .= \trim($certInfo['subject']['O'] . ' ' . $certInfo['subject']['OU']);
|
||||
}
|
||||
if ($subject && ($o || $ou)) {
|
||||
$subject .= ' (' . trim($o . ' ' . $ou) . ')';
|
||||
} else {
|
||||
$subject .= trim($o . ' ' . $ou);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace WebAuthn\Attestation;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\CBOR\CborDecoder;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\CBOR\CborDecoder;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author Lukas Buchs
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
class AndroidKey extends FormatBase {
|
||||
private $_alg;
|
||||
private $_signature;
|
||||
private $_x5c;
|
||||
|
||||
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
|
||||
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
||||
parent::__construct($AttestionObject, $authenticatorData);
|
||||
|
||||
// check u2f data
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
class AndroidSafetyNet extends FormatBase {
|
||||
private $_signature;
|
||||
|
@ -11,7 +12,7 @@ class AndroidSafetyNet extends FormatBase {
|
|||
private $_x5c;
|
||||
private $_payload;
|
||||
|
||||
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
|
||||
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
||||
parent::__construct($AttestionObject, $authenticatorData);
|
||||
|
||||
// check data
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
class Apple extends FormatBase {
|
||||
private $_x5c;
|
||||
|
||||
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
|
||||
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
||||
parent::__construct($AttestionObject, $authenticatorData);
|
||||
|
||||
// check packed data
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
|
||||
|
||||
abstract class FormatBase {
|
||||
|
@ -14,9 +15,9 @@ abstract class FormatBase {
|
|||
/**
|
||||
*
|
||||
* @param Array $AttestionObject
|
||||
* @param \WebAuthn\Attestation\AuthenticatorData $authenticatorData
|
||||
* @param AuthenticatorData $authenticatorData
|
||||
*/
|
||||
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
|
||||
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
||||
$this->_attestationObject = $AttestionObject;
|
||||
$this->_authenticatorData = $authenticatorData;
|
||||
}
|
||||
|
@ -26,7 +27,7 @@ abstract class FormatBase {
|
|||
*/
|
||||
public function __destruct() {
|
||||
// delete X.509 chain certificate file after use
|
||||
if (\is_file($this->_x5c_tempFile)) {
|
||||
if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
|
||||
\unlink($this->_x5c_tempFile);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +37,7 @@ abstract class FormatBase {
|
|||
* @return string|null
|
||||
*/
|
||||
public function getCertificateChain() {
|
||||
if (\is_file($this->_x5c_tempFile)) {
|
||||
if ($this->_x5c_tempFile && \is_file($this->_x5c_tempFile)) {
|
||||
return \file_get_contents($this->_x5c_tempFile);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
|
||||
class None extends FormatBase {
|
||||
|
||||
|
||||
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
|
||||
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
||||
parent::__construct($AttestionObject, $authenticatorData);
|
||||
}
|
||||
|
||||
|
@ -28,12 +29,13 @@ class None extends FormatBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* validates the certificate against root certificates
|
||||
* validates the certificate against root certificates.
|
||||
* Format 'none' does not contain any ca, so always false.
|
||||
* @param array $rootCas
|
||||
* @return boolean
|
||||
* @throws WebAuthnException
|
||||
*/
|
||||
public function validateRootCertificate($rootCas) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
class Packed extends FormatBase {
|
||||
private $_alg;
|
||||
private $_signature;
|
||||
private $_x5c;
|
||||
|
||||
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
|
||||
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
||||
parent::__construct($AttestionObject, $authenticatorData);
|
||||
|
||||
// check packed data
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
class Tpm extends FormatBase {
|
||||
private $_TPM_GENERATED_VALUE = "\xFF\x54\x43\x47";
|
||||
|
@ -19,7 +20,7 @@ class Tpm extends FormatBase {
|
|||
private $_certInfo;
|
||||
|
||||
|
||||
public function __construct($AttestionObject, \WebAuthn\Attestation\AuthenticatorData $authenticatorData) {
|
||||
public function __construct($AttestionObject, AuthenticatorData $authenticatorData) {
|
||||
parent::__construct($AttestionObject, $authenticatorData);
|
||||
|
||||
// check packed data
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Attestation\Format;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\Attestation\Format;
|
||||
use lbuchs\WebAuthn\Attestation\AuthenticatorData;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
class U2f extends FormatBase {
|
||||
private $_alg = -7;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\Binary;
|
||||
use WebAuthn\WebAuthnException;
|
||||
namespace lbuchs\WebAuthn\Binary;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
|
||||
/**
|
||||
* Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/ByteBuffer.php
|
||||
|
@ -39,7 +39,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
|
|||
/**
|
||||
* create a ByteBuffer from a base64 url encoded string
|
||||
* @param string $base64url
|
||||
* @return \WebAuthn\Binary\ByteBuffer
|
||||
* @return ByteBuffer
|
||||
*/
|
||||
public static function fromBase64Url($base64url) {
|
||||
$bin = self::_base64url_decode($base64url);
|
||||
|
@ -52,7 +52,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
|
|||
/**
|
||||
* create a ByteBuffer from a base64 url encoded string
|
||||
* @param string $hex
|
||||
* @return \WebAuthn\Binary\ByteBuffer
|
||||
* @return ByteBuffer
|
||||
*/
|
||||
public static function fromHex($hex) {
|
||||
$bin = \hex2bin($hex);
|
||||
|
@ -65,7 +65,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
|
|||
/**
|
||||
* create a random ByteBuffer
|
||||
* @param string $length
|
||||
* @return \WebAuthn\Binary\ByteBuffer
|
||||
* @return ByteBuffer
|
||||
*/
|
||||
public static function randomBuffer($length) {
|
||||
if (\function_exists('random_bytes')) { // >PHP 7.0
|
||||
|
@ -97,6 +97,14 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
|
|||
return \ord(\substr($this->_data, $offset, 1));
|
||||
}
|
||||
|
||||
public function getJson($jsonFlags=0) {
|
||||
$data = \json_decode($this->getBinaryString(), null, 512, $jsonFlags);
|
||||
if (\json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new WebAuthnException(\json_last_error_msg(), WebAuthnException::BYTEBUFFER);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getLength() {
|
||||
return $this->_length;
|
||||
}
|
||||
|
@ -203,7 +211,7 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
|
|||
/**
|
||||
* jsonSerialize interface
|
||||
* return binary data in RFC 1342-Like serialized string
|
||||
* @return \stdClass
|
||||
* @return string
|
||||
*/
|
||||
public function jsonSerialize() {
|
||||
if (ByteBuffer::$useBase64UrlEncoding) {
|
||||
|
@ -231,6 +239,36 @@ class ByteBuffer implements \JsonSerializable, \Serializable {
|
|||
$this->_length = \strlen($this->_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 8 deprecates Serializable-Interface)
|
||||
* @return array
|
||||
*/
|
||||
public function __serialize() {
|
||||
return [
|
||||
'data' => \serialize($this->_data)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* object to string
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->getHex();
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 8 deprecates Serializable-Interface)
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function __unserialize($data) {
|
||||
if ($data && isset($data['data'])) {
|
||||
$this->_data = \unserialize($data['data']);
|
||||
$this->_length = \strlen($this->_data);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------
|
||||
// PROTECTED STATIC
|
||||
// -----------------------
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace WebAuthn\CBOR;
|
||||
use WebAuthn\WebAuthnException;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn\CBOR;
|
||||
use lbuchs\WebAuthn\WebAuthnException;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
|
||||
/**
|
||||
* Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/CborDecoder.php
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright © 2019 Lukas Buchs
|
||||
Copyright © 2018 Thomas Bleeker (CBOR & ByteBuffer part)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace WebAuthn;
|
||||
use WebAuthn\Binary\ByteBuffer;
|
||||
namespace lbuchs\WebAuthn;
|
||||
use lbuchs\WebAuthn\Binary\ByteBuffer;
|
||||
require_once 'WebAuthnException.php';
|
||||
require_once 'Binary/ByteBuffer.php';
|
||||
require_once 'Attestation/AttestationObject.php';
|
||||
|
@ -69,16 +69,20 @@ class WebAuthn {
|
|||
/**
|
||||
* add a root certificate to verify new registrations
|
||||
* @param string $path file path of / directory with root certificates
|
||||
* @param array|null $certFileExtensions if adding a direction, all files with provided extension are added. default: pem, crt, cer, der
|
||||
*/
|
||||
public function addRootCertificates($path) {
|
||||
public function addRootCertificates($path, $certFileExtensions=null) {
|
||||
if (!\is_array($this->_caFiles)) {
|
||||
$this->_caFiles = array();
|
||||
}
|
||||
if ($certFileExtensions === null) {
|
||||
$certFileExtensions = array('pem', 'crt', 'cer', 'der');
|
||||
}
|
||||
$path = \rtrim(\trim($path), '\\/');
|
||||
if (\is_dir($path)) {
|
||||
foreach (\scandir($path) as $ca) {
|
||||
if (\is_file($path . '/' . $ca)) {
|
||||
$this->addRootCertificates($path . '/' . $ca);
|
||||
if (\is_file($path . DIRECTORY_SEPARATOR . $ca) && \in_array(\strtolower(\pathinfo($ca, PATHINFO_EXTENSION)), $certFileExtensions)) {
|
||||
$this->addRootCertificates($path . DIRECTORY_SEPARATOR . $ca);
|
||||
}
|
||||
}
|
||||
} else if (\is_file($path) && !\in_array(\realpath($path), $this->_caFiles)) {
|
||||
|
@ -273,10 +277,11 @@ class WebAuthn {
|
|||
* @param string|ByteBuffer $challenge binary used challange
|
||||
* @param bool $requireUserVerification true, if the device must verify user (e.g. by biometric data or pin)
|
||||
* @param bool $requireUserPresent false, if the device must NOT check user presence (e.g. by pressing a button)
|
||||
* @param bool $failIfRootMismatch false, if there should be no error thrown if root certificate doesn't match
|
||||
* @return \stdClass
|
||||
* @throws WebAuthnException
|
||||
*/
|
||||
public function processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true) {
|
||||
public function processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true) {
|
||||
$clientDataHash = \hash('sha256', $clientDataJSON, true);
|
||||
$clientData = \json_decode($clientDataJSON);
|
||||
$challenge = $challenge instanceof ByteBuffer ? $challenge : new ByteBuffer($challenge);
|
||||
|
@ -318,18 +323,21 @@ class WebAuthn {
|
|||
}
|
||||
|
||||
// 15. If validation is successful, obtain a list of acceptable trust anchors
|
||||
if (is_array($this->_caFiles) && !$attestationObject->validateRootCertificate($this->_caFiles)) {
|
||||
$rootValid = is_array($this->_caFiles) ? $attestationObject->validateRootCertificate($this->_caFiles) : null;
|
||||
if ($failIfRootMismatch && is_array($this->_caFiles) && !$rootValid) {
|
||||
throw new WebAuthnException('invalid root certificate', WebAuthnException::CERTIFICATE_NOT_TRUSTED);
|
||||
}
|
||||
|
||||
// 10. Verify that the User Present bit of the flags in authData is set.
|
||||
if ($requireUserPresent && !$attestationObject->getAuthenticatorData()->getUserPresent()) {
|
||||
$userPresent = $attestationObject->getAuthenticatorData()->getUserPresent();
|
||||
if ($requireUserPresent && !$userPresent) {
|
||||
throw new WebAuthnException('user not present during authentication', WebAuthnException::USER_PRESENT);
|
||||
}
|
||||
|
||||
// 11. If user verification is required for this registration, verify that the User Verified bit of the flags in authData is set.
|
||||
if ($requireUserVerification && !$attestationObject->getAuthenticatorData()->getUserVerified()) {
|
||||
throw new WebAuthnException('user not verificated during authentication', WebAuthnException::USER_VERIFICATED);
|
||||
$userVerified = $attestationObject->getAuthenticatorData()->getUserVerified();
|
||||
if ($requireUserVerification && !$userVerified) {
|
||||
throw new WebAuthnException('user not verified during authentication', WebAuthnException::USER_VERIFICATED);
|
||||
}
|
||||
|
||||
$signCount = $attestationObject->getAuthenticatorData()->getSignCount();
|
||||
|
@ -340,6 +348,7 @@ class WebAuthn {
|
|||
// prepare data to store for future logins
|
||||
$data = new \stdClass();
|
||||
$data->rpId = $this->_rpId;
|
||||
$data->attestationFormat = $attestationObject->getAttestationFormatName();
|
||||
$data->credentialId = $attestationObject->getAuthenticatorData()->getCredentialId();
|
||||
$data->credentialPublicKey = $attestationObject->getAuthenticatorData()->getPublicKeyPem();
|
||||
$data->certificateChain = $attestationObject->getCertificateChain();
|
||||
|
@ -348,6 +357,9 @@ class WebAuthn {
|
|||
$data->certificateSubject = $attestationObject->getCertificateSubject();
|
||||
$data->signatureCounter = $this->_signatureCounter;
|
||||
$data->AAGUID = $attestationObject->getAuthenticatorData()->getAAGUID();
|
||||
$data->rootValid = $rootValid;
|
||||
$data->userPresent = $userPresent;
|
||||
$data->userVerified = $userVerified;
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -453,6 +465,92 @@ class WebAuthn {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads root certificates from FIDO Alliance Metadata Service (MDS) to a specific folder
|
||||
* https://fidoalliance.org/metadata/
|
||||
* @param string $certFolder Folder path to save the certificates in PEM format.
|
||||
* @param bool $deleteCerts=true
|
||||
* @return int number of cetificates
|
||||
* @throws WebAuthnException
|
||||
*/
|
||||
public function queryFidoMetaDataService($certFolder, $deleteCerts=true) {
|
||||
$url = 'https://mds.fidoalliance.org/';
|
||||
$raw = null;
|
||||
if (\function_exists('curl_init')) {
|
||||
$ch = \curl_init($url);
|
||||
\curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
\curl_setopt($ch, CURLOPT_USERAGENT, 'github.com/lbuchs/WebAuthn - A simple PHP WebAuthn server library');
|
||||
$raw = \curl_exec($ch);
|
||||
\curl_close($ch);
|
||||
} else {
|
||||
$raw = \file_get_contents($url);
|
||||
}
|
||||
|
||||
$certFolder = \rtrim(\realpath($certFolder), '\\/');
|
||||
if (!is_dir($certFolder)) {
|
||||
throw new WebAuthnException('Invalid folder path for query FIDO Alliance Metadata Service');
|
||||
}
|
||||
|
||||
if (!\is_string($raw)) {
|
||||
throw new WebAuthnException('Unable to query FIDO Alliance Metadata Service');
|
||||
}
|
||||
|
||||
$jwt = \explode('.', $raw);
|
||||
if (\count($jwt) !== 3) {
|
||||
throw new WebAuthnException('Invalid JWT from FIDO Alliance Metadata Service');
|
||||
}
|
||||
|
||||
if ($deleteCerts) {
|
||||
foreach (\scandir($certFolder) as $ca) {
|
||||
if (\substr($ca, -4) === '.pem') {
|
||||
if (\unlink($certFolder . DIRECTORY_SEPARATOR . $ca) === false) {
|
||||
throw new WebAuthnException('Cannot delete certs in folder for FIDO Alliance Metadata Service');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list($header, $payload, $hash) = $jwt;
|
||||
$payload = Binary\ByteBuffer::fromBase64Url($payload)->getJson();
|
||||
|
||||
$count = 0;
|
||||
if (\is_object($payload) && \property_exists($payload, 'entries') && \is_array($payload->entries)) {
|
||||
foreach ($payload->entries as $entry) {
|
||||
if (\is_object($entry) && \property_exists($entry, 'metadataStatement') && \is_object($entry->metadataStatement)) {
|
||||
$description = $entry->metadataStatement->description ?? null;
|
||||
$attestationRootCertificates = $entry->metadataStatement->attestationRootCertificates ?? null;
|
||||
|
||||
if ($description && $attestationRootCertificates) {
|
||||
|
||||
// create filename
|
||||
$certFilename = \preg_replace('/[^a-z0-9]/i', '_', $description);
|
||||
$certFilename = \trim(\preg_replace('/\_{2,}/i', '_', $certFilename),'_') . '.pem';
|
||||
$certFilename = \strtolower($certFilename);
|
||||
|
||||
// add certificate
|
||||
$certContent = $description . "\n";
|
||||
$certContent .= \str_repeat('-', \mb_strlen($description)) . "\n";
|
||||
|
||||
foreach ($attestationRootCertificates as $attestationRootCertificate) {
|
||||
$count++;
|
||||
$certContent .= "\n-----BEGIN CERTIFICATE-----\n";
|
||||
$certContent .= \chunk_split(\trim($attestationRootCertificate), 64, "\n");
|
||||
$certContent .= "-----END CERTIFICATE-----\n";
|
||||
}
|
||||
|
||||
if (\file_put_contents($certFolder . DIRECTORY_SEPARATOR . $certFilename, $certContent) === false) {
|
||||
throw new WebAuthnException('unable to save certificate from FIDO Alliance Metadata Service');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// PRIVATE
|
||||
// -----------------------------------------------
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
namespace WebAuthn;
|
||||
namespace lbuchs\WebAuthn;
|
||||
|
||||
/**
|
||||
* @author Lukas Buchs
|
||||
|
|
|
@ -54,24 +54,28 @@ foreach ($css_dir as $css_file) {
|
|||
}
|
||||
|
||||
// U2F API + T/HOTP API
|
||||
// u2f - deprecated, should be removed
|
||||
$u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']);
|
||||
$qrprovider = new RobThree\Auth\Providers\Qr\QRServerProvider();
|
||||
$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL, 6, 30, 'sha1', $qrprovider);
|
||||
|
||||
// FIDO2
|
||||
$formats = $GLOBALS['FIDO2_FORMATS'];
|
||||
$WebAuthn = new \WebAuthn\WebAuthn('WebAuthn Library', $_SERVER['HTTP_HOST'], $formats);
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/solo.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/apple.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/nitro.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/yubico.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/hypersecu.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/globalSign.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/googleHardware.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/microsoftTpmCollection.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/huawei.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/trustkey.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/bsi.pem');
|
||||
$WebAuthn = new lbuchs\WebAuthn\WebAuthn('WebAuthn Library', $_SERVER['HTTP_HOST'], $formats);
|
||||
// only include root ca's when dev mode is false, to support testing with chromiums virutal authenticator
|
||||
if (!$DEV_MODE){
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/solo.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/apple.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/nitro.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/yubico.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/hypersecu.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/globalSign.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/googleHardware.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/microsoftTpmCollection.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/huawei.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/trustkey.pem');
|
||||
$WebAuthn->addRootCertificates($_SERVER['DOCUMENT_ROOT'] . '/inc/lib/WebAuthn/rootCertificates/bsi.pem');
|
||||
}
|
||||
|
||||
// Redis
|
||||
$redis = new Redis();
|
||||
|
|
|
@ -1,15 +1,28 @@
|
|||
<?php
|
||||
if (isset($_POST["verify_tfa_login"])) {
|
||||
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST["token"])) {
|
||||
if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST, $WebAuthn)) {
|
||||
$_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username'];
|
||||
$_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role'];
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
unset($_SESSION['pending_mailcow_cc_role']);
|
||||
unset($_SESSION['pending_tfa_method']);
|
||||
header("Location: /user");
|
||||
|
||||
header("Location: /user");
|
||||
} else {
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
unset($_SESSION['pending_mailcow_cc_role']);
|
||||
unset($_SESSION['pending_tfa_method']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET["cancel_tfa_login"])) {
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
unset($_SESSION['pending_mailcow_cc_role']);
|
||||
unset($_SESSION['pending_tfa_method']);
|
||||
|
||||
header("Location: /");
|
||||
}
|
||||
|
||||
if (isset($_POST["quick_release"])) {
|
||||
quarantine('quick_release', $_POST["quick_release"]);
|
||||
}
|
||||
|
|
|
@ -192,11 +192,17 @@ $SHOW_LAST_LOGIN = true;
|
|||
// true = required
|
||||
// false = preferred
|
||||
// string 'required' 'preferred' 'discouraged'
|
||||
$WEBAUTHN_UV_FLAG_REGISTER = false;
|
||||
$WEBAUTHN_UV_FLAG_LOGIN = false:
|
||||
$WEBAUTHN_USER_PRESENT_FLAG = true;
|
||||
|
||||
$FIDO2_UV_FLAG_REGISTER = 'preferred';
|
||||
$FIDO2_UV_FLAG_LOGIN = 'preferred'; // iOS ignores the key via NFC if required - known issue
|
||||
$FIDO2_USER_PRESENT_FLAG = true;
|
||||
|
||||
$FIDO2_FORMATS = array('apple', 'android-key', 'android-safetynet', 'fido-u2f', 'none', 'packed', 'tpm');
|
||||
|
||||
|
||||
// Set visible Rspamd maps in mailcow UI, do not change unless you know what you are doing
|
||||
$RSPAMD_MAPS = array(
|
||||
'regex' => array(
|
||||
|
|
|
@ -117,12 +117,12 @@ if (isset($_GET['query'])) {
|
|||
echo isset($_SESSION['return']) ? json_encode($_SESSION['return']) : $generic_success;
|
||||
}
|
||||
}
|
||||
if (!isset($_POST['attr']) && $category != "fido2-registration") {
|
||||
if (!isset($_POST['attr']) && $category != "fido2-registration" && $category != "webauthn-tfa-registration") {
|
||||
echo $request_incomplete;
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
if ($category != "fido2-registration") {
|
||||
if ($category != "fido2-registration" && $category != "webauthn-tfa-registration") {
|
||||
$attr = (array)json_decode($_POST['attr'], true);
|
||||
}
|
||||
unset($attr['csrf_token']);
|
||||
|
@ -170,6 +170,48 @@ if (isset($_GET['query'])) {
|
|||
exit;
|
||||
}
|
||||
break;
|
||||
case "webauthn-tfa-registration":
|
||||
if (isset($_SESSION["mailcow_cc_role"])) {
|
||||
// parse post data
|
||||
$post = trim(file_get_contents('php://input'));
|
||||
if ($post) $post = json_decode($post);
|
||||
|
||||
// decode base64 strings
|
||||
$clientDataJSON = base64_decode($post->clientDataJSON);
|
||||
$attestationObject = base64_decode($post->attestationObject);
|
||||
|
||||
// process registration data from authenticator
|
||||
try {
|
||||
// processCreate($clientDataJSON, $attestationObject, $challenge, $requireUserVerification=false, $requireUserPresent=true, $failIfRootMismatch=true)
|
||||
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $_SESSION['challenge'], false, true);
|
||||
}
|
||||
catch (Throwable $ex) {
|
||||
// err
|
||||
$return = new stdClass();
|
||||
$return->success = false;
|
||||
$return->msg = $ex->getMessage();
|
||||
echo json_encode($return);
|
||||
exit;
|
||||
}
|
||||
|
||||
// safe authenticator in mysql `tfa` table
|
||||
$_data['tfa_method'] = $post->tfa_method;
|
||||
$_data['key_id'] = $post->key_id;
|
||||
$_data['registration'] = $data;
|
||||
set_tfa($_data);
|
||||
|
||||
// send response
|
||||
$return = new stdClass();
|
||||
$return->success = true;
|
||||
echo json_encode($return);
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
// err - request incomplete
|
||||
echo $request_incomplete;
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
case "time_limited_alias":
|
||||
process_add_return(mailbox('add', 'time_limited_alias', $attr));
|
||||
break;
|
||||
|
@ -350,6 +392,7 @@ if (isset($_GET['query'])) {
|
|||
exit();
|
||||
}
|
||||
switch ($category) {
|
||||
// u2f - deprecated, should be removed
|
||||
case "u2f-registration":
|
||||
header('Content-Type: application/javascript');
|
||||
if (isset($_SESSION["mailcow_cc_role"]) && $_SESSION["mailcow_cc_username"] == $object) {
|
||||
|
@ -366,21 +409,6 @@ if (isset($_GET['query'])) {
|
|||
return;
|
||||
}
|
||||
break;
|
||||
// fido2-registration via GET
|
||||
case "fido2-registration":
|
||||
header('Content-Type: application/json');
|
||||
if (isset($_SESSION["mailcow_cc_role"])) {
|
||||
// Exclude existing CredentialIds, if any
|
||||
$excludeCredentialIds = fido2(array("action" => "get_user_cids"));
|
||||
$createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, true, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $excludeCredentialIds);
|
||||
print(json_encode($createArgs));
|
||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "u2f-authentication":
|
||||
header('Content-Type: application/javascript');
|
||||
if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) {
|
||||
|
@ -403,6 +431,21 @@ if (isset($_GET['query'])) {
|
|||
return;
|
||||
}
|
||||
break;
|
||||
// fido2
|
||||
case "fido2-registration":
|
||||
header('Content-Type: application/json');
|
||||
if (isset($_SESSION["mailcow_cc_role"])) {
|
||||
// Exclude existing CredentialIds, if any
|
||||
$excludeCredentialIds = fido2(array("action" => "get_user_cids"));
|
||||
$createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, true, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $excludeCredentialIds);
|
||||
print(json_encode($createArgs));
|
||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "fido2-get-args":
|
||||
header('Content-Type: application/json');
|
||||
// Login without username, no ids!
|
||||
|
@ -416,6 +459,36 @@ if (isset($_GET['query'])) {
|
|||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||
return;
|
||||
break;
|
||||
// webauthn two factor authentication
|
||||
case "webauthn-tfa-registration":
|
||||
if (isset($_SESSION["mailcow_cc_role"])) {
|
||||
$excludeCredentialIds = null;
|
||||
|
||||
$createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, false, $GLOBALS['WEBAUTHN_UV_FLAG_REGISTER'], true);
|
||||
|
||||
print(json_encode($createArgs));
|
||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||
return;
|
||||
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "webauthn-tfa-get-args":
|
||||
$stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username");
|
||||
$stmt->execute(array(':username' => $_SESSION['pending_mailcow_cc_username']));
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
while($row = array_shift($rows)) {
|
||||
$cids[] = base64_decode($row['keyHandle']);
|
||||
}
|
||||
|
||||
$getArgs = $WebAuthn->getGetArgs($cids, 30, true, true, true, true, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']);
|
||||
$getArgs->publicKey->extensions = array('appid' => "https://".$getArgs->publicKey->rpId);
|
||||
print(json_encode($getArgs));
|
||||
$_SESSION['challenge'] = $WebAuthn->getChallenge();
|
||||
return;
|
||||
break;
|
||||
}
|
||||
if (isset($_SESSION['mailcow_cc_role'])) {
|
||||
switch ($category) {
|
||||
|
|
|
@ -443,9 +443,9 @@
|
|||
"set_tfa": "Definir el mètode d'autenticació de dos factors",
|
||||
"tfa": "Autenticació de dos factors",
|
||||
"totp": "OTP basat en temps (Google Authenticator etc.)",
|
||||
"u2f": "Autenticació U2F",
|
||||
"waiting_usb_auth": "<i>Esperant el dispositiu USB...</i><br><br>Apreta el botó del teu dispositiu USB U2F ara.",
|
||||
"waiting_usb_register": "<i>Esperant el dispositiu USB...</i><br><br>Posa el teu password i confirma el registre del teu U2F apretant el botó del teu dispositiiu USB U2F.",
|
||||
"webauthn": "Autenticació WebAuthn",
|
||||
"waiting_usb_auth": "<i>Esperant el dispositiu USB...</i><br><br>Apreta el botó del teu dispositiu USB WebAuthn ara.",
|
||||
"waiting_usb_register": "<i>Esperant el dispositiu USB...</i><br><br>Posa el teu password i confirma el registre del teu WebAuthn apretant el botó del teu dispositiiu USB WebAuthn.",
|
||||
"yubi_otp": "Autenticació OTP de Yubico"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -454,7 +454,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Parametr pravidel TLS je neplatný",
|
||||
"totp_verification_failed": "TOTP ověření selhalo",
|
||||
"transport_dest_exists": "Transportní cíl \"%s\" již existuje",
|
||||
"u2f_verification_failed": "U2F ověření selhalo: %s",
|
||||
"webauthn": "WebAuthn ověření selhalo: %s",
|
||||
"unknown": "Došlo k neznámé chybě",
|
||||
"unknown_tfa_method": "Neznámá 2FA metoda",
|
||||
"unlimited_quota_acl": "Neomeznou kvótu nepovoluje seznam oprávnění ACL",
|
||||
|
@ -972,7 +972,7 @@
|
|||
"upload_success": "Soubor úspěšně nahrán",
|
||||
"verified_fido2_login": "Ověřené FIDO2 přihlášení",
|
||||
"verified_totp_login": "TOTP přihlášení ověřeno",
|
||||
"verified_u2f_login": "U2F přihlášení ověřeno",
|
||||
"webauthn": "WebAuthn přihlášení ověřeno",
|
||||
"verified_yotp_login": "Yubico OTP přihlášení ověřeno"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -983,7 +983,7 @@
|
|||
"disable_tfa": "Zakázat 2FA do příštího úspěšného přihlášení",
|
||||
"enter_qr_code": "Kód TOTP, pokud zařízení neumí číst QR kódy",
|
||||
"error_code": "Kód chyby",
|
||||
"init_u2f": "Probíhá inicializace, čekejte...",
|
||||
"init_webauthn": "Probíhá inicializace, čekejte...",
|
||||
"key_id": "Identifikátor YubiKey",
|
||||
"key_id_totp": "Identifikátor klíče",
|
||||
"none": "Deaktivovat",
|
||||
|
@ -991,13 +991,13 @@
|
|||
"scan_qr_code": "Prosím načtěte následující kód svou aplikací na ověření nebo zadejte kód ručně.",
|
||||
"select": "Prosím vyberte...",
|
||||
"set_tfa": "Nastavení způsobu dvoufaktorového ověření",
|
||||
"start_u2f_validation": "Zahájit inicializaci",
|
||||
"start_webauthn_validation": "Zahájit inicializaci",
|
||||
"tfa": "Dvoufaktorové ověření (TFA)",
|
||||
"tfa_token_invalid": "Neplatný TFA token",
|
||||
"totp": "Časově založené OTP (Google Authenticator, Authy apod.)",
|
||||
"u2f": "U2F ověření",
|
||||
"waiting_usb_auth": "<i>Čeká se na USB zařízení...</i><br><br>Prosím stiskněte tlačítko na svém U2F USB zařízení.",
|
||||
"waiting_usb_register": "<i>Čeká se na USB zařízení...</i><br><br>Prosím zadejte své heslo výše a potvrďte U2F registraci stiskem tlačítka na svém U2F USB zařízení.",
|
||||
"webauthn": "WebAuthn ověření",
|
||||
"waiting_usb_auth": "<i>Čeká se na USB zařízení...</i><br><br>Prosím stiskněte tlačítko na svém WebAuthn USB zařízení.",
|
||||
"waiting_usb_register": "<i>Čeká se na USB zařízení...</i><br><br>Prosím zadejte své heslo výše a potvrďte WebAuthn registraci stiskem tlačítka na svém WebAuthn USB zařízení.",
|
||||
"yubi_otp": "Yubico OTP ověření"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -426,7 +426,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Politikparameter er ugyldig",
|
||||
"totp_verification_failed": "Bekræftelse af TOTP mislykkedes",
|
||||
"transport_dest_exists": "Transport destination \"%s\" eksisterer",
|
||||
"u2f_verification_failed": "U2F-bekræftelse mislykkedes: %s",
|
||||
"webauthn_verification_failed": "WebAuthn-bekræftelse mislykkedes: %s",
|
||||
"fido2_verification_failed": "Bekræftelse af FIDO2 mislykkedes: %s",
|
||||
"unknown": "Der opstod en ukendt fejl",
|
||||
"unknown_tfa_method": "Ukendt TFA-metode",
|
||||
|
@ -881,7 +881,7 @@
|
|||
"ui_texts": "Gemte ændringer til UI-tekster",
|
||||
"upload_success": "Filen blev uploadet",
|
||||
"verified_totp_login": "Bekræftet TOTP-login",
|
||||
"verified_u2f_login": "Bekræftet U2F-login",
|
||||
"verified_webauthn_login": "Bekræftet WebAuthn-login",
|
||||
"verified_fido2_login": "Bekræftet FIDO2-login",
|
||||
"verified_yotp_login": "Bekræftet Yubico OTP-login"
|
||||
},
|
||||
|
@ -893,7 +893,7 @@
|
|||
"disable_tfa": "Deaktiver TFA indtil næste vellykkede login",
|
||||
"enter_qr_code": "Din TOTP kode hvis din enhed ikke kan scanne QR-koder",
|
||||
"error_code": "Fejl kode",
|
||||
"init_u2f": "Initialiserer, vent venligst...",
|
||||
"init_webauthn": "Initialiserer, vent venligst...",
|
||||
"key_id": "En identifikator til din YubiKey",
|
||||
"key_id_totp": "En identifikator for din nøgle",
|
||||
"none": "Deaktivere",
|
||||
|
@ -901,11 +901,11 @@
|
|||
"scan_qr_code": "Scan venligst følgende kode med din godkendelsesapp, eller indtast koden manuelt.",
|
||||
"select": "Vælg venligst",
|
||||
"set_tfa": "Set 2-faktor godkendelses metoden",
|
||||
"start_u2f_validation": "Start validering",
|
||||
"start_webauthn_validation": "Start validering",
|
||||
"tfa": "2-faktor godkendelse",
|
||||
"tfa_token_invalid": "TFA nøgle ugyldig",
|
||||
"totp": "Tids-baseret OTP (Google Authenticator, Authy, etc.)",
|
||||
"u2f": "U2F godkendelse",
|
||||
"webauthn": "WebAuthn godkendelse",
|
||||
"waiting_usb_auth": "<i>Venter på USB-enhed...</i><br><br>Tryk let på knappen på din USB-enhed nu.",
|
||||
"waiting_usb_register": "<i>Venter på USB-enhed...</i><br><br>Indtast din adgangskode ovenfor, og bekræft din registrering ved at trykke på knappen på din USB-enhed.",
|
||||
"yubi_otp": "Yubico OTP godkendelse"
|
||||
|
|
|
@ -455,7 +455,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Parameter ist ungültig",
|
||||
"totp_verification_failed": "TOTP-Verifizierung fehlgeschlagen",
|
||||
"transport_dest_exists": "Transport-Maps-Ziel \"%s\" existiert bereits",
|
||||
"u2f_verification_failed": "U2F-Verifizierung fehlgeschlagen: %s",
|
||||
"webauthn_verification_failed": "WebAuthn-Verifizierung fehlgeschlagen: %s",
|
||||
"unknown": "Ein unbekannter Fehler trat auf",
|
||||
"unknown_tfa_method": "Unbekannte TFA-Methode",
|
||||
"unlimited_quota_acl": "Unendliche Quota untersagt durch ACL",
|
||||
|
@ -971,7 +971,7 @@
|
|||
"upload_success": "Datei wurde erfolgreich hochgeladen",
|
||||
"verified_fido2_login": "FIDO2-Anmeldung verifiziert",
|
||||
"verified_totp_login": "TOTP-Anmeldung verifiziert",
|
||||
"verified_u2f_login": "U2F-Anmeldung verifiziert",
|
||||
"verified_webauthn_login": "WebAuthn-Anmeldung verifiziert",
|
||||
"verified_yotp_login": "Yubico-OTP-Anmeldung verifiziert"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -982,7 +982,7 @@
|
|||
"disable_tfa": "Deaktiviere 2FA bis zur nächsten erfolgreichen Anmeldung",
|
||||
"enter_qr_code": "Falls Sie den angezeigten QR-Code nicht scannen können, verwenden Sie bitte nachstehenden Sicherheitsschlüssel",
|
||||
"error_code": "Fehlercode",
|
||||
"init_u2f": "Initialisiere, bitte warten...",
|
||||
"init_webauthn": "Initialisiere, bitte warten...",
|
||||
"key_id": "Ein Namen für diesen YubiKey",
|
||||
"key_id_totp": "Ein eindeutiger Name",
|
||||
"none": "Deaktiviert",
|
||||
|
@ -990,11 +990,11 @@
|
|||
"scan_qr_code": "Bitte scannen Sie jetzt den angezeigten QR-Code:",
|
||||
"select": "Bitte auswählen",
|
||||
"set_tfa": "Konfiguriere Zwei-Faktor-Authentifizierungsmethode",
|
||||
"start_u2f_validation": "Starte Validierung",
|
||||
"start_webauthn_validation": "Starte Validierung",
|
||||
"tfa": "Zwei-Faktor-Authentifizierung",
|
||||
"tfa_token_invalid": "TFA-Token ungültig!",
|
||||
"totp": "Time-based-OTP (Google Authenticator etc.)",
|
||||
"u2f": "U2F-Authentifizierung",
|
||||
"webauthn": "WebAuthn-Authentifizierung",
|
||||
"waiting_usb_auth": "<i>Warte auf USB-Gerät...</i><br><br>Bitte jetzt den vorgesehenen Taster des USB-Gerätes berühren.",
|
||||
"waiting_usb_register": "<i>Warte auf USB-Gerät...</i><br><br>Bitte zuerst das obere Passwortfeld ausfüllen und erst dann den vorgesehenen Taster des USB-Gerätes berühren.",
|
||||
"yubi_otp": "Yubico OTP-Authentifizierung"
|
||||
|
|
|
@ -455,7 +455,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Policy parameter is invalid",
|
||||
"totp_verification_failed": "TOTP verification failed",
|
||||
"transport_dest_exists": "Transport destination \"%s\" exists",
|
||||
"u2f_verification_failed": "U2F verification failed: %s",
|
||||
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
||||
"unknown": "An unknown error occurred",
|
||||
"unknown_tfa_method": "Unknown TFA method",
|
||||
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
||||
|
@ -978,7 +978,7 @@
|
|||
"upload_success": "File uploaded successfully",
|
||||
"verified_fido2_login": "Verified FIDO2 login",
|
||||
"verified_totp_login": "Verified TOTP login",
|
||||
"verified_u2f_login": "Verified U2F login",
|
||||
"verified_webauthn_login": "Verified WebAuthn login",
|
||||
"verified_yotp_login": "Verified Yubico OTP login"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -989,7 +989,7 @@
|
|||
"disable_tfa": "Disable TFA until next successful login",
|
||||
"enter_qr_code": "Your TOTP code if your device cannot scan QR codes",
|
||||
"error_code": "Error code",
|
||||
"init_u2f": "Initializing, please wait...",
|
||||
"init_webauthn": "Initializing, please wait...",
|
||||
"key_id": "An identifier for your YubiKey",
|
||||
"key_id_totp": "An identifier for your key",
|
||||
"none": "Deactivate",
|
||||
|
@ -997,11 +997,11 @@
|
|||
"scan_qr_code": "Please scan the following code with your authenticator app or enter the code manually.",
|
||||
"select": "Please select",
|
||||
"set_tfa": "Set two-factor authentication method",
|
||||
"start_u2f_validation": "Start validation",
|
||||
"start_webauthn_validation": "Start validation",
|
||||
"tfa": "Two-factor authentication",
|
||||
"tfa_token_invalid": "TFA token invalid",
|
||||
"totp": "Time-based OTP (Google Authenticator, Authy, etc.)",
|
||||
"u2f": "U2F authentication",
|
||||
"webauthn": "WebAuthn authentication",
|
||||
"waiting_usb_auth": "<i>Waiting for USB device...</i><br><br>Please tap the button on your USB device now.",
|
||||
"waiting_usb_register": "<i>Waiting for USB device...</i><br><br>Please enter your password above and confirm your registration by tapping the button on your USB device.",
|
||||
"yubi_otp": "Yubico OTP authentication"
|
||||
|
|
|
@ -323,7 +323,7 @@
|
|||
"tls_policy_map_parameter_invalid": "El parámetro de póliza no es válido.",
|
||||
"totp_verification_failed": "Verificación TOTP fallida",
|
||||
"transport_dest_exists": "Destino de la regla de transporte ya existe",
|
||||
"u2f_verification_failed": "Verificación U2F fallida: %s",
|
||||
"webauthn_verification_failed": "Verificación WebAuthn fallida: %s",
|
||||
"unknown": "Se produjo un error desconocido",
|
||||
"unknown_tfa_method": "Método TFA desconocido",
|
||||
"unlimited_quota_acl": "Cuota ilimitada restringida por controles administrativos",
|
||||
|
@ -649,7 +649,7 @@
|
|||
"tls_policy_map_entry_deleted": "Regla de póliza de TLS con ID %s ha sido elimindada",
|
||||
"tls_policy_map_entry_saved": "Regla de póliza de TLS \"%s\" ha sido guardada",
|
||||
"verified_totp_login": "Inicio de sesión TOTP verificado",
|
||||
"verified_u2f_login": "Inicio de sesión U2F verificado",
|
||||
"verified_webauthn_login": "Inicio de sesión WebAuthn verificado",
|
||||
"verified_yotp_login": "Inicio de sesión Yubico OTP verificado"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -667,9 +667,9 @@
|
|||
"set_tfa": "Establecer el método de autenticación de dos factores",
|
||||
"tfa": "Autenticación de dos factores",
|
||||
"totp": "OTP basado en tiempo (Google Authenticator, Authy, etc.)",
|
||||
"u2f": "Autenticación U2F",
|
||||
"waiting_usb_auth": "<i>Esperando al dispositivo USB...</i><br><br>Toque el botón en su dispositivo USB U2F ahora.",
|
||||
"waiting_usb_register": "<i>Esperando al dispositivo USB....</i><br><br>Ingrese su contraseña arriba y confirme su registro U2F tocando el botón en su dispositivo USB U2F.",
|
||||
"webauthn": "Autenticación WebAuthn",
|
||||
"waiting_usb_auth": "<i>Esperando al dispositivo USB...</i><br><br>Toque el botón en su dispositivo USB WebAuthn ahora.",
|
||||
"waiting_usb_register": "<i>Esperando al dispositivo USB....</i><br><br>Ingrese su contraseña arriba y confirme su registro WebAuthn tocando el botón en su dispositivo USB WebAuthn.",
|
||||
"yubi_otp": "Yubico OTP"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -370,7 +370,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Käytäntö parametri ei kelpaa",
|
||||
"totp_verification_failed": "TOTP-vahvistus epäonnistui",
|
||||
"transport_dest_exists": "Kuljetuksen määränpää \"%s\" olemassa",
|
||||
"u2f_verification_failed": "U2F vahvistaminen epäonnistui: %s",
|
||||
"webauthn_verification_failed": "WebAuthn vahvistaminen epäonnistui: %s",
|
||||
"unknown": "Ilmeni tuntematon virhe",
|
||||
"unknown_tfa_method": "Tuntematon TFA-menetelmä",
|
||||
"unlimited_quota_acl": "Rajoittamaton kiintiö kielletty ACL",
|
||||
|
@ -754,7 +754,7 @@
|
|||
"ui_texts": "Tallennettu käyttöliittymätekstien muutokset",
|
||||
"upload_success": "Tiedosto ladattu onnistuneesti",
|
||||
"verified_totp_login": "Vahvistettu TOTP-kirjautuminen",
|
||||
"verified_u2f_login": "Vahvistettu U2F kirjautuminen",
|
||||
"verified_webauthn_login": "Vahvistettu WebAuthn kirjautuminen",
|
||||
"verified_yotp_login": "Vahvistettu Yubico OTP kirjautuminen"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -765,7 +765,7 @@
|
|||
"disable_tfa": "Poista TFA käytöstä seuraavaan onnistuneen kirjautumisen jälkeen",
|
||||
"enter_qr_code": "TOTP-koodisi, jos laitteesi ei pysty tarkistamaan QR-koodeja",
|
||||
"error_code": "Virhekoodi",
|
||||
"init_u2f": "Alustetaan, odota...",
|
||||
"init_webauthn": "Alustetaan, odota...",
|
||||
"key_id": "Tunniste YubiKey",
|
||||
"key_id_totp": "Avaimen tunnus",
|
||||
"none": "Poista",
|
||||
|
@ -773,12 +773,12 @@
|
|||
"scan_qr_code": "Tarkista seuraava koodi Authenticator-sovelluksella tai Syötä koodi manuaalisesti.",
|
||||
"select": "Valitse",
|
||||
"set_tfa": "Määritä kaksiosainen todennus menetelmä",
|
||||
"start_u2f_validation": "Aloita oikeellisuus tarkistus",
|
||||
"start_webauthn_validation": "Aloita oikeellisuus tarkistus",
|
||||
"tfa": "Kaksiosainen todennus",
|
||||
"totp": "Aikapohjainen OTP (Google Authenticator, Authy jne.)",
|
||||
"u2f": "U2F todennus",
|
||||
"waiting_usb_auth": "<i>Odotetaan USB-laitetta...</i><br><br>Napauta painiketta U2F USB-laitteessa nyt",
|
||||
"waiting_usb_register": "<i>Odotetaan USB-laitetta...</i><br><br>Anna salasanasi yltä ja vahvista U2F-rekisteröinti napauttamalla painiketta U2F USB-laitteessa.",
|
||||
"webauthn": "WebAuthn todennus",
|
||||
"waiting_usb_auth": "<i>Odotetaan USB-laitetta...</i><br><br>Napauta painiketta WebAuthn USB-laitteessa nyt",
|
||||
"waiting_usb_register": "<i>Odotetaan USB-laitetta...</i><br><br>Anna salasanasi yltä ja vahvista WebAuthn-rekisteröinti napauttamalla painiketta WebAuthn USB-laitteessa.",
|
||||
"yubi_otp": "Yubico OTP-todennus"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -430,7 +430,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Le paramètre Policy est invalide",
|
||||
"totp_verification_failed": "Echec de la vérification TOTP",
|
||||
"transport_dest_exists": "La destination de transport \"%s\" existe",
|
||||
"u2f_verification_failed": "Echec de la vérification U2F: %s",
|
||||
"webauthn_verification_failed": "Echec de la vérification WebAuthn: %s",
|
||||
"fido2_verification_failed": "La vérification FIDO2 a échoué: %s",
|
||||
"unknown": "Une erreur inconnue est survenue",
|
||||
"unknown_tfa_method": "Methode TFA inconnue",
|
||||
|
@ -895,7 +895,7 @@
|
|||
"ui_texts": "Enregistrement des modifications apportées aux textes de l’interface utilisateur",
|
||||
"upload_success": "Fichier téléchargé avec succès",
|
||||
"verified_totp_login": "Authentification TOTP vérifiée",
|
||||
"verified_u2f_login": "Authentification U2F vérifiée",
|
||||
"verified_webauthn_login": "Authentification WebAuthn vérifiée",
|
||||
"verified_fido2_login": "Authentification FIDO2 vérifiée",
|
||||
"verified_yotp_login": "Authentification Yubico OTP vérifiée"
|
||||
},
|
||||
|
@ -907,7 +907,7 @@
|
|||
"disable_tfa": "Désactiver TFA jusqu’à la prochaine ouverture de session réussie",
|
||||
"enter_qr_code": "Votre code TOTP si votre appareil ne peut pas scanner les codes QR",
|
||||
"error_code": "Code d'erreur",
|
||||
"init_u2f": "Initialisation, veuillez patienter...",
|
||||
"init_webauthn": "Initialisation, veuillez patienter...",
|
||||
"key_id": "Un identifiant pour votre Yubikey",
|
||||
"key_id_totp": "Un identifiant pour votre clé",
|
||||
"none": "Désactiver",
|
||||
|
@ -915,13 +915,13 @@
|
|||
"scan_qr_code": "Veuillez scanner le code suivant avec votre application d’authentification ou entrer le code manuellement.",
|
||||
"select": "Veuillez sélectionner",
|
||||
"set_tfa": "Définir une méthode d’authentification à deux facteurs",
|
||||
"start_u2f_validation": "Début de la validation",
|
||||
"start_webauthn_validation": "Début de la validation",
|
||||
"tfa": "Authentification à deux facteurs",
|
||||
"tfa_token_invalid": "Token TFA invalide",
|
||||
"totp": "OTP (One Time Password = Mot de passe à usage unique : Google Authenticator, Authy, etc.)",
|
||||
"u2f": "Authentification U2F",
|
||||
"waiting_usb_auth": "<i>En attente d’un périphérique USB...</i><br><br>S’il vous plaît appuyez maintenant sur le bouton de votre périphérique USB U2F.",
|
||||
"waiting_usb_register": "<i>En attente d’un périphérique USB...</i><br><br>Veuillez entrer votre mot de passe ci-dessus et confirmer votre inscription U2F en appuyant sur le bouton de votre périphérique USB U2F.",
|
||||
"webauthn": "Authentification WebAuthn",
|
||||
"waiting_usb_auth": "<i>En attente d’un périphérique USB...</i><br><br>S’il vous plaît appuyez maintenant sur le bouton de votre périphérique USB WebAuthn.",
|
||||
"waiting_usb_register": "<i>En attente d’un périphérique USB...</i><br><br>Veuillez entrer votre mot de passe ci-dessus et confirmer votre inscription WebAuthn en appuyant sur le bouton de votre périphérique USB WebAuthn.",
|
||||
"yubi_otp": "Authentification OTP Yubico"
|
||||
},
|
||||
"fido2": {
|
||||
|
|
|
@ -449,7 +449,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Policy parameter is invalid",
|
||||
"totp_verification_failed": "TOTP verification failed",
|
||||
"transport_dest_exists": "Transport destination \"%s\" exists",
|
||||
"u2f_verification_failed": "U2F verification failed: %s",
|
||||
"webauthn_verification_failed": "WebAuthn verification failed: %s",
|
||||
"unknown": "An unknown error occurred",
|
||||
"unknown_tfa_method": "Unknown TFA method",
|
||||
"unlimited_quota_acl": "Unlimited quota prohibited by ACL",
|
||||
|
@ -947,7 +947,7 @@
|
|||
"upload_success": "File caricato con successo",
|
||||
"verified_fido2_login": "Verified FIDO2 login",
|
||||
"verified_totp_login": "Verified TOTP login",
|
||||
"verified_u2f_login": "Verified U2F login",
|
||||
"verified_webauthn_login": "Verified WebAuthn login",
|
||||
"verified_yotp_login": "Verified Yubico OTP login"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -958,7 +958,7 @@
|
|||
"disable_tfa": "Disabilita TFA fino al prossimo accesso",
|
||||
"enter_qr_code": "Il codice TOTP se il tuo dispositivo non è in grado di acquisire i codici QR",
|
||||
"error_code": "Codice di errore",
|
||||
"init_u2f": "Inizializzazione, attendere prego...",
|
||||
"init_webauthn": "Inizializzazione, attendere prego...",
|
||||
"key_id": "Identificatore per il tuo YubiKey",
|
||||
"key_id_totp": "Identificatore per la tua chiave",
|
||||
"none": "Disattivato",
|
||||
|
@ -966,12 +966,12 @@
|
|||
"scan_qr_code": "Esegui la scansione del seguente codice con l'applicazione di autenticazione o inserisci manualmente il codice.",
|
||||
"select": "Seleziona",
|
||||
"set_tfa": "Imposta il metodo di autenticazione a due fattori",
|
||||
"start_u2f_validation": "Avvia convalida",
|
||||
"start_webauthn_validation": "Avvia convalida",
|
||||
"tfa": "Autenticazione a due fattori",
|
||||
"totp": "Time-based OTP (Google Authenticator etc.)",
|
||||
"u2f": "Autenticazione U2F",
|
||||
"waiting_usb_auth": "<i>In attesa del device USB...</i><br /><br />Tocca ora il pulsante sul dispositivo U2F USB.",
|
||||
"waiting_usb_register": "<i>In attesa del device USB...</i><br /><br />Inserisci la tua password qui sopra e conferma la tua registrazione U2F toccando il pulsante del dispositivo U2F USB.",
|
||||
"webauthn": "Autenticazione WebAuthn",
|
||||
"waiting_usb_auth": "<i>In attesa del device USB...</i><br /><br />Tocca ora il pulsante sul dispositivo WebAuthn USB.",
|
||||
"waiting_usb_register": "<i>In attesa del device USB...</i><br /><br />Inserisci la tua password qui sopra e conferma la tua registrazione WebAuthn toccando il pulsante del dispositivo WebAuthn USB.",
|
||||
"yubi_otp": "Autenticazione Yubico OTP",
|
||||
"tfa_token_invalid": "Token TFA non valido"
|
||||
},
|
||||
|
|
|
@ -417,7 +417,7 @@
|
|||
"tls_policy_map_parameter_invalid": "유효하지 않은 정책 매개변수",
|
||||
"totp_verification_failed": "TOTP 확인 실패",
|
||||
"transport_dest_exists": "전송 목적지 \"%s\"가 존재합니다.",
|
||||
"u2f_verification_failed": "U2F 검증 실패: %s",
|
||||
"webauthn_verification_failed": "WebAuthn 검증 실패: %s",
|
||||
"unknown": "알 수 없는 오류 발생",
|
||||
"unknown_tfa_method": "알 수 없는 TFA 방식",
|
||||
"unlimited_quota_acl": "ACL에 따라 할당량을 무제한으로 둘 수 없습니다.",
|
||||
|
@ -852,7 +852,7 @@
|
|||
"ui_texts": "Saved changes to UI texts",
|
||||
"upload_success": "File uploaded successfully",
|
||||
"verified_totp_login": "Verified TOTP login",
|
||||
"verified_u2f_login": "Verified U2F login",
|
||||
"verified_webauthn_login": "Verified WebAuthn login",
|
||||
"verified_yotp_login": "Verified Yubico OTP login"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -863,7 +863,7 @@
|
|||
"disable_tfa": "Disable TFA until next successful login",
|
||||
"enter_qr_code": "Your TOTP code if your device cannot scan QR codes",
|
||||
"error_code": "Error code",
|
||||
"init_u2f": "Initializing, please wait...",
|
||||
"init_webauthn": "Initializing, please wait...",
|
||||
"key_id": "An identifier for your YubiKey",
|
||||
"key_id_totp": "An identifier for your key",
|
||||
"none": "Deactivate",
|
||||
|
@ -871,12 +871,12 @@
|
|||
"scan_qr_code": "Please scan the following code with your authenticator app or enter the code manually.",
|
||||
"select": "Please select",
|
||||
"set_tfa": "Set two-factor authentication method",
|
||||
"start_u2f_validation": "Start validation",
|
||||
"start_webauthn_validation": "Start validation",
|
||||
"tfa": "Two-factor authentication",
|
||||
"totp": "Time-based OTP (Google Authenticator, Authy, etc.)",
|
||||
"u2f": "U2F authentication",
|
||||
"waiting_usb_auth": "<i>Waiting for USB device...</i><br><br>Please tap the button on your U2F USB device now.",
|
||||
"waiting_usb_register": "<i>Waiting for USB device...</i><br><br>Please enter your password above and confirm your U2F registration by tapping the button on your U2F USB device.",
|
||||
"webauthn": "WebAuthn authentication",
|
||||
"waiting_usb_auth": "<i>Waiting for USB device...</i><br><br>Please tap the button on your WebAuthn USB device now.",
|
||||
"waiting_usb_register": "<i>Waiting for USB device...</i><br><br>Please enter your password above and confirm your WebAuthn registration by tapping the button on your WebAuthn USB device.",
|
||||
"yubi_otp": "Yubico OTP authentication"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -450,9 +450,9 @@
|
|||
"set_tfa": "Uzstādīt difi faktoru autentifik;acijas metodi",
|
||||
"tfa": "Divu faktoru autentifikācija",
|
||||
"totp": "Uz laiku bāzēta vienreizēja parole (Google Autentifikātors utt.)",
|
||||
"u2f": "U2F autentifikācija",
|
||||
"waiting_usb_auth": "<i>Gaida USB ierīci...</i><br><br>Lūdzu, tagad nospiežiet pogu uz Jūsu U2F USB ierīces.",
|
||||
"waiting_usb_register": "<i>Gaida USB ierīci...</i><br><br>Lūdzu augšā ievadiet Jūsu paroli un apstipriniet U2F reģistrāciju nospiežot pogu uz Jūsu U2F USB ierīces.",
|
||||
"webauthn": "WebAuthn autentifikācija",
|
||||
"waiting_usb_auth": "<i>Gaida USB ierīci...</i><br><br>Lūdzu, tagad nospiežiet pogu uz Jūsu WebAuthn USB ierīces.",
|
||||
"waiting_usb_register": "<i>Gaida USB ierīci...</i><br><br>Lūdzu augšā ievadiet Jūsu paroli un apstipriniet WebAuthn reģistrāciju nospiežot pogu uz Jūsu WebAuthn USB ierīces.",
|
||||
"yubi_otp": "Yubico OTP autentifikators"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -428,7 +428,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Beleidsparameter is ongeldig",
|
||||
"totp_verification_failed": "TOTP-verificatie mislukt",
|
||||
"transport_dest_exists": "Transportbestemming \"%s\" bestaat reeds",
|
||||
"u2f_verification_failed": "U2F-verificatie mislukt: %s",
|
||||
"webauthn_verification_failed": "WebAuthn-verificatie mislukt: %s",
|
||||
"fido2_verification_failed": "FIDO2-verificatie mislukt: %s",
|
||||
"unknown": "Er is een onbekende fout opgetreden",
|
||||
"unknown_tfa_method": "Onbekende tweefactorauthenticatiemethode",
|
||||
|
@ -891,7 +891,7 @@
|
|||
"ui_texts": "Wijzigingen aan labels en teksten zijn opgeslagen",
|
||||
"upload_success": "Bestand succesvol geupload",
|
||||
"verified_totp_login": "TOTP succesvol geverifieerd",
|
||||
"verified_u2f_login": "U2F succesvol geverifieerd",
|
||||
"verified_webauthn_login": "WebAuthn succesvol geverifieerd",
|
||||
"verified_fido2_login": "FIDO2 succesvol geverifieerd",
|
||||
"verified_yotp_login": "Yubico OTP succesvol geverifieerd"
|
||||
},
|
||||
|
@ -903,7 +903,7 @@
|
|||
"disable_tfa": "Pauzeer tweefactorauthenticatie tot de eerstvolgende succesvolle login",
|
||||
"enter_qr_code": "Voer deze code in als je apparaat geen QR-codes kan scannen:",
|
||||
"error_code": "Errorcode",
|
||||
"init_u2f": "Even geduld aub...",
|
||||
"init_webauthn": "Even geduld aub...",
|
||||
"key_id": "Geef deze YubiKey een naam",
|
||||
"key_id_totp": "Geef deze key een naam",
|
||||
"none": "Deactiveer",
|
||||
|
@ -911,13 +911,13 @@
|
|||
"scan_qr_code": "Scan de volgende QR-code met je authenticatie-app:",
|
||||
"select": "Selecteer...",
|
||||
"set_tfa": "Kies methode voor tweefactorauthenticatie",
|
||||
"start_u2f_validation": "Start validatie",
|
||||
"start_webauthn_validation": "Start validatie",
|
||||
"tfa": "Tweefactorauthenticatie",
|
||||
"tfa_token_invalid": "Tweefactorauthenticatietoken is ongeldig",
|
||||
"totp": "TOTP (Step Two, Authy, etc.)",
|
||||
"u2f": "U2F",
|
||||
"waiting_usb_auth": "<i>In afwachting van USB-apparaat...</i><br><br>Druk nu op de knop van je U2F-apparaat.",
|
||||
"waiting_usb_register": "<i>In afwachting van USB-apparaat...</i><br><br>Voer je wachtwoord hierboven in en bevestig de registratie van het U2F-apparaat door op de knop van het apparaat te drukken.",
|
||||
"webauthn": "WebAuthn",
|
||||
"waiting_usb_auth": "<i>In afwachting van USB-apparaat...</i><br><br>Druk nu op de knop van je WebAuthn-apparaat.",
|
||||
"waiting_usb_register": "<i>In afwachting van USB-apparaat...</i><br><br>Voer je wachtwoord hierboven in en bevestig de registratie van het WebAuthn-apparaat door op de knop van het apparaat te drukken.",
|
||||
"yubi_otp": "Yubico OTP"
|
||||
},
|
||||
"fido2": {
|
||||
|
|
|
@ -329,9 +329,9 @@
|
|||
"set_tfa": "Ustaw metodę uwierzytelniania dwuetapowego",
|
||||
"tfa": "Uwierzytelnianie dwuetapowe",
|
||||
"totp": "Time-based OTP (Google Authenticator itd.)",
|
||||
"u2f": "Uwierzytelnianie U2F",
|
||||
"waiting_usb_auth": "<i>Czekam na urządzenie USB...</i><br><br>Wciśnij teraz przycisk na urządzeniu U2F USB.",
|
||||
"waiting_usb_register": "<i> Czekam na urządzenie USB...</i><br><br>Wprowadź swoje hasło powyżej i potwierdź rejestrację U2F przez naciśnięcie przycisku na urządzeniu U2F USB.",
|
||||
"webauthn": "Uwierzytelnianie WebAuthn",
|
||||
"waiting_usb_auth": "<i>Czekam na urządzenie USB...</i><br><br>Wciśnij teraz przycisk na urządzeniu WebAuthn USB.",
|
||||
"waiting_usb_register": "<i> Czekam na urządzenie USB...</i><br><br>Wprowadź swoje hasło powyżej i potwierdź rejestrację WebAuthn przez naciśnięcie przycisku na urządzeniu WebAuthn USB.",
|
||||
"yubi_otp": "Uwierzytelnianie Yubico OTP"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -448,7 +448,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Parametrul politicii este invalid",
|
||||
"totp_verification_failed": "Verificarea TOTP a eșuat",
|
||||
"transport_dest_exists": "Destinația transportului \"%s\" există",
|
||||
"u2f_verification_failed": "Verificarea U2F a eșuat: %s",
|
||||
"webauthn_verification_failed": "Verificarea WebAuthn a eșuat: %s",
|
||||
"fido2_verification_failed": "Verificarea FIDO2 a eșuat: %s",
|
||||
"unknown": "A apărut o eroare necunoscută",
|
||||
"unknown_tfa_method": "Metodă TFA necunoscută",
|
||||
|
@ -928,7 +928,7 @@
|
|||
"ui_texts": "Modificări salvate în textele UI",
|
||||
"upload_success": "Fișier încărcat cu succes",
|
||||
"verified_totp_login": "Autentificarea TOTP verificată",
|
||||
"verified_u2f_login": "Autentificarea U2F verificată",
|
||||
"verified_webauthn_login": "Autentificarea WebAuthn verificată",
|
||||
"verified_fido2_login": "Conectare FIDO2 verificată",
|
||||
"verified_yotp_login": "Autentificarea Yubico OTP verificată"
|
||||
},
|
||||
|
@ -940,7 +940,7 @@
|
|||
"disable_tfa": "Dezactivează TFA până la următoarea conectare reușită",
|
||||
"enter_qr_code": "Codul tău TOTP dacă dispozitivul tău nu poate scana codurile QR",
|
||||
"error_code": "Cod de eroare",
|
||||
"init_u2f": "Inițializare, vă rugăm așteptați...",
|
||||
"init_webauthn": "Inițializare, vă rugăm așteptați...",
|
||||
"key_id": "Un identificator pentru YubiKey",
|
||||
"key_id_totp": "Un identificator pentru cheia ta",
|
||||
"none": "Dezactivează",
|
||||
|
@ -948,13 +948,13 @@
|
|||
"scan_qr_code": "Scanează codul următor cu aplicația ta de autentificare sau introdu manual codul.",
|
||||
"select": "Te rog selectează",
|
||||
"set_tfa": "Setează metoda de autentificare cu doi factori",
|
||||
"start_u2f_validation": "Începi validarea",
|
||||
"start_webauthn_validation": "Începi validarea",
|
||||
"tfa": "Autentificare cu doi factori",
|
||||
"tfa_token_invalid": "Jeton TFA invalid",
|
||||
"totp": "OTP pe bază de timp (Google Authenticator etc.)",
|
||||
"u2f": "Autentificare U2F",
|
||||
"waiting_usb_auth": "<i>În așteptarea dispozitivului USB...</i><br><br>Apasă acum butonul de pe dispozitivul tău USB U2F.",
|
||||
"waiting_usb_register": "<i>În așteptarea dispozitivului USB...</i><br><br>Introdu parola ta mai sus și confirmă înregistrarea ta U2F atingând butonul de pe dispozitivul tău USB U2F.",
|
||||
"webauthn": "Autentificare WebAuthn",
|
||||
"waiting_usb_auth": "<i>În așteptarea dispozitivului USB...</i><br><br>Apasă acum butonul de pe dispozitivul tău USB WebAuthn.",
|
||||
"waiting_usb_register": "<i>În așteptarea dispozitivului USB...</i><br><br>Introdu parola ta mai sus și confirmă înregistrarea ta WebAuthn atingând butonul de pe dispozitivul tău USB WebAuthn.",
|
||||
"yubi_otp": "Autentificare Yubico OTP"
|
||||
},
|
||||
"fido2": {
|
||||
|
|
|
@ -454,7 +454,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Недопустимое значение параметра политики",
|
||||
"totp_verification_failed": "Ошибка валидации TOTP",
|
||||
"transport_dest_exists": "Назначение для отправки \"%s\" уже существует",
|
||||
"u2f_verification_failed": "Ошибка валидации U2F: %s",
|
||||
"webauthn_verification_failed": "Ошибка валидации WebAuthn: %s",
|
||||
"unknown": "Произошла неизвестная ошибка",
|
||||
"unknown_tfa_method": "Неизвестный метод TFA",
|
||||
"unlimited_quota_acl": "Неограниченная квота запрещена политикой доступа",
|
||||
|
@ -973,7 +973,7 @@
|
|||
"upload_success": "Файл загружен успешно",
|
||||
"verified_fido2_login": "Авторизация FIDO2 пройдена",
|
||||
"verified_totp_login": "Авторизация TOTP пройдена",
|
||||
"verified_u2f_login": "Авторизация U2F пройдена",
|
||||
"verified_webauthn_login": "Авторизация WebAuthn пройдена",
|
||||
"verified_yotp_login": "Авторизация Yubico OTP пройдена"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -984,7 +984,7 @@
|
|||
"disable_tfa": "Отключить TFA до следующего успешного входа",
|
||||
"enter_qr_code": "Ваш код TOTP, если устройство не может отсканировать QR-код",
|
||||
"error_code": "Код ошибки",
|
||||
"init_u2f": "Инициализация, пожалуйста, подождите...",
|
||||
"init_webauthn": "Инициализация, пожалуйста, подождите...",
|
||||
"key_id": "Идентификатор YubiKey ключа",
|
||||
"key_id_totp": "Идентификатор TOTP ключа",
|
||||
"none": "Отключить",
|
||||
|
@ -992,11 +992,11 @@
|
|||
"scan_qr_code": "Пожалуйста, отсканируйте QR-код с помощью приложения или введите его вручную.",
|
||||
"select": "Пожалуйста, выберите",
|
||||
"set_tfa": "Задать метод двухфакторной проверки",
|
||||
"start_u2f_validation": "Начать проверку",
|
||||
"start_webauthn_validation": "Начать проверку",
|
||||
"tfa": "Двухфакторная проверка подлинности",
|
||||
"tfa_token_invalid": "Неправильный TFA токен",
|
||||
"totp": "OTP (Authy, Google Authenticator и др.)",
|
||||
"u2f": "U2F аутентификация",
|
||||
"webauthn": "WebAuthn аутентификация",
|
||||
"waiting_usb_auth": "<i>Ожидание устройства USB...</i><br><br>Пожалуйста, нажмите кнопку на USB устройстве сейчас.",
|
||||
"waiting_usb_register": "<i>Ожидание устройства USB...</i><br><br>Пожалуйста, введите пароль выше и подтвердите регистрацию, нажав кнопку на USB устройстве.",
|
||||
"yubi_otp": "Yubico OTP аутентификация"
|
||||
|
|
|
@ -454,7 +454,7 @@
|
|||
"tls_policy_map_parameter_invalid": "Podmienkový parameter mapy TLS pravidiel je neplatný",
|
||||
"totp_verification_failed": "TOTP overenie zlyhalo",
|
||||
"transport_dest_exists": "Transportný cieľ \"%s\" už existuje",
|
||||
"u2f_verification_failed": "U2F overenie zlyhalo: %s",
|
||||
"webauthn_verification_failed": "WebAuthn overenie zlyhalo: %s",
|
||||
"unknown": "Nastala neznáma chyba",
|
||||
"unknown_tfa_method": "Neznáma TFA metóda",
|
||||
"unlimited_quota_acl": "Neobmedzené kvóta je zakázaná cez ACL",
|
||||
|
@ -973,7 +973,7 @@
|
|||
"upload_success": "Súbor úspešne nahratý",
|
||||
"verified_fido2_login": "Overené FIDO2 prihlásenie",
|
||||
"verified_totp_login": "Overené TOTP prihlásenie",
|
||||
"verified_u2f_login": "Overené U2F prihlásenie",
|
||||
"verified_webauthn_login": "Overené WebAuthn prihlásenie",
|
||||
"verified_yotp_login": "Overené Yubico OTP prihlásenie"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -984,7 +984,7 @@
|
|||
"disable_tfa": "Vypnúť TFA do ďalšieho úspešného prihlásenia",
|
||||
"enter_qr_code": "Zadajte váš TOTP kód, ak vaše zariadenie nedokáže skenovať QR kódy",
|
||||
"error_code": "Chyba kódu",
|
||||
"init_u2f": "Inicializácia, prosím čakajte...",
|
||||
"init_webauthn": "Inicializácia, prosím čakajte...",
|
||||
"key_id": "Identifikátor pre váš YubiKey",
|
||||
"key_id_totp": "Identifikátor pre váš kľúč",
|
||||
"none": "Deaktivovať",
|
||||
|
@ -992,11 +992,11 @@
|
|||
"scan_qr_code": "Prosím oskenujte nasledovný kód pomocou vašej autentizačnej aplikácie alebo zadajte kód manuálne.",
|
||||
"select": "Prosím vyberte",
|
||||
"set_tfa": "Nastaviť dvojúrovňovú autentifikačnú metódu",
|
||||
"start_u2f_validation": "Spustiť validáciu",
|
||||
"start_webauthn_validation": "Spustiť validáciu",
|
||||
"tfa": "Dvojúrovňová autentifikácia (TFA)",
|
||||
"tfa_token_invalid": "Neplatný TFA token",
|
||||
"totp": "Časovo-založený OTP (Google Authenticator, Authy, atď.)",
|
||||
"u2f": "U2F autentifikácia",
|
||||
"webauthn": "WebAuthn autentifikácia",
|
||||
"waiting_usb_auth": "<i>Čakanie na USB zariadenie...</i><br><br>Prosím stlačte tlačidlo na vašom USB zariadení.",
|
||||
"waiting_usb_register": "<i>Čakanie na USB zariadenie...</i><br><br>Prosím zadajte vaše heslo a potvrďte registráciu stlačením tlačidla na vašom USB zariadení.",
|
||||
"yubi_otp": "Yubico OTP autentifikácia"
|
||||
|
|
|
@ -441,8 +441,8 @@
|
|||
"tls_policy_map_parameter_invalid": "Policy parameter är ogiltig",
|
||||
"totp_verification_failed": "TOTP-verifiering misslyckades",
|
||||
"transport_dest_exists": "Transportdestinationen \"%s\" existerar redan",
|
||||
"u2f_verification_failed": "U2F-verifiering misslyckades: %s",
|
||||
"fido2_verification_failed": "U2F-verifiering misslyckades: %s",
|
||||
"webauthn_verification_failed": "WebAuthn-verifiering misslyckades: %s",
|
||||
"fido2_verification_failed": "WebAuthn-verifiering misslyckades: %s",
|
||||
"unknown": "Ett fel har inträffat",
|
||||
"unknown_tfa_method": "Okänd TFA method",
|
||||
"unlimited_quota_acl": "På grund av en åtkomstlista tillåts inte en obegränsad kvot",
|
||||
|
@ -911,7 +911,7 @@
|
|||
"ui_texts": "Ändringarna på texter och rubriker i gränssnittet sparade",
|
||||
"upload_success": "Filen har laddats upp",
|
||||
"verified_totp_login": "Verifierad TOTP inloggning",
|
||||
"verified_u2f_login": "Verifierad U2F inloggning",
|
||||
"verified_webauthn_login": "Verifierad WebAuthn inloggning",
|
||||
"verified_fido2_login": "Verifierad FIDO2 inloggning",
|
||||
"verified_yotp_login": "Verifierad Yubico OTP inloggning"
|
||||
},
|
||||
|
@ -923,7 +923,7 @@
|
|||
"disable_tfa": "Inaktivera tvåfaktorsautentisering tills nästa lyckade inloggning",
|
||||
"enter_qr_code": "Om du inte kan skanna den QR-kod som visas, använd säkerhetsnyckeln som visas nedan",
|
||||
"error_code": "Felkod",
|
||||
"init_u2f": "Initierar, vänta...",
|
||||
"init_webauthn": "Initierar, vänta...",
|
||||
"key_id": "En identifierare för din YubiKey",
|
||||
"key_id_totp": "En identifierare för din nyckel",
|
||||
"none": "Avaktivera",
|
||||
|
@ -931,11 +931,11 @@
|
|||
"scan_qr_code": "Skanna nu den QR-kod som visas på skärmen.",
|
||||
"select": "Välj",
|
||||
"set_tfa": "Metod för tvåfaktorsautentisering",
|
||||
"start_u2f_validation": "Startar validering",
|
||||
"start_webauthn_validation": "Startar validering",
|
||||
"tfa": "Tvåfaktorsautentisering",
|
||||
"tfa_token_invalid": "TFA nyckeln är ogiltig!",
|
||||
"totp": "Tidsbaserad OTP (Google Authenticator, Authy, mm)",
|
||||
"u2f": "U2F-autentisering",
|
||||
"webauthn": "WebAuthn-autentisering",
|
||||
"waiting_usb_auth": "<i>Väntar på USB-enhet...</i><br><br>Tryck på knappen på USB-enheten nu.",
|
||||
"waiting_usb_register": "<i>Väntar på USB-enhet...</i><br><br>Vänligen fyll i det övre lösenordsfältet först och tryck sedan på knappen på USB-enheten.",
|
||||
"yubi_otp": "Yubico OTP-autentisering"
|
||||
|
|
|
@ -424,7 +424,7 @@
|
|||
"tls_policy_map_parameter_invalid": "策略参数非法",
|
||||
"totp_verification_failed": "TOTP认证失败",
|
||||
"transport_dest_exists": "传输目标 \"%s\" 已存在",
|
||||
"u2f_verification_failed": "U2F认证失败: %s",
|
||||
"webauthn_verification_failed": "WebAuthn认证失败: %s",
|
||||
"unknown": "发生未知错误",
|
||||
"unknown_tfa_method": "未知TFA方法",
|
||||
"unlimited_quota_acl": "ACL设置禁止了无限配额",
|
||||
|
@ -875,7 +875,7 @@
|
|||
"ui_texts": "已保存UI文本更改",
|
||||
"upload_success": "成功上传文件",
|
||||
"verified_totp_login": "TOTP登录验证成功",
|
||||
"verified_u2f_login": "U2F登录验证成功",
|
||||
"verified_webauthn_login": "WebAuthn登录验证成功",
|
||||
"verified_yotp_login": "Yubico OTP登录验证成功"
|
||||
},
|
||||
"tfa": {
|
||||
|
@ -886,7 +886,7 @@
|
|||
"disable_tfa": "在下一次成功登录前关闭两步验证",
|
||||
"enter_qr_code": "如果你的设备不能扫描QR码,输入此TOTP码",
|
||||
"error_code": "错误码",
|
||||
"init_u2f": "初始化中,请等待...",
|
||||
"init_webauthn": "初始化中,请等待...",
|
||||
"key_id": "你的YubiKey的标识",
|
||||
"key_id_totp": "你的密钥的标识",
|
||||
"none": "禁用",
|
||||
|
@ -894,12 +894,12 @@
|
|||
"scan_qr_code": "请用你认证应用扫描或手动输入此码。",
|
||||
"select": "请选择",
|
||||
"set_tfa": "设置两步验证方法",
|
||||
"start_u2f_validation": "开始认证",
|
||||
"start_webauthn_validation": "开始认证",
|
||||
"tfa": "两步验证(2FA)",
|
||||
"totp": "TOTP认证 (Google Authenticator、Authy等)",
|
||||
"u2f": "U2F认证",
|
||||
"waiting_usb_auth": "<i>等待USB设备...</i><br><br>现在请触碰你的U2F USB设备上的按钮。",
|
||||
"waiting_usb_register": "<i>等待USB设备...</i><br><br>请在上方输入你的密码并请触碰你的U2F USB设备上的按钮以确认注册U2F设备。",
|
||||
"webauthn": "WebAuthn认证",
|
||||
"waiting_usb_auth": "<i>等待USB设备...</i><br><br>现在请触碰你的WebAuthn USB设备上的按钮。",
|
||||
"waiting_usb_register": "<i>等待USB设备...</i><br><br>请在上方输入你的密码并请触碰你的WebAuthn USB设备上的按钮以确认注册WebAuthn设备。",
|
||||
"yubi_otp": "Yubico OTP认证"
|
||||
},
|
||||
"user": {
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<div class="col-sm-9 col-xs-7">
|
||||
<select data-style="btn btn-sm dropdown-toggle bs-placeholder btn-default" data-width="fit" id="selectTFA" class="selectpicker" title="{{ lang.tfa.select }}">
|
||||
<option value="yubi_otp">{{ lang.tfa.yubi_otp }}</option>
|
||||
<option value="u2f">{{ lang.tfa.u2f }}</option>
|
||||
<option value="webauthn">{{ lang.tfa.webauthn }}</option>
|
||||
<option value="totp">{{ lang.tfa.totp }}</option>
|
||||
<option value="none">{{ lang.tfa.none }}</option>
|
||||
</select>
|
||||
|
|
|
@ -160,7 +160,7 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$(window).load(function() {
|
||||
$(".overlay").hide();
|
||||
});
|
||||
|
@ -181,32 +181,52 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
$('#u2f_status_auth').html('<p><i class="bi bi-arrow-repeat icon-spin"></i> ' + lang_tfa.init_u2f + '</p>');
|
||||
$('#webauthn_status_auth').html('<p><i class="bi bi-arrow-repeat icon-spin"></i> ' + lang_tfa.init_u2f + '</p>');
|
||||
$('#ConfirmTFAModal').on('shown.bs.modal', function(){
|
||||
$(this).find('input[name=token]').focus();
|
||||
// If U2F
|
||||
if(document.getElementById("u2f_auth_data") !== null) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
dataType: 'script',
|
||||
url: "/api/v1/get/u2f-authentication/{{ pending_mailcow_cc_username|url_encode(true)|default('null') }}",
|
||||
complete: function(data){
|
||||
$('#u2f_status_auth').html(lang_tfa.waiting_usb_auth);
|
||||
data;
|
||||
setTimeout(function() {
|
||||
console.log("Ready to authenticate");
|
||||
u2f.sign(appId, challenge, registeredKeys, function(data) {
|
||||
var form = document.getElementById('u2f_auth_form');
|
||||
var auth = document.getElementById('u2f_auth_data');
|
||||
console.log("Authenticate callback", data);
|
||||
auth.value = JSON.stringify(data);
|
||||
form.submit();
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
// If WebAuthn
|
||||
if(document.getElementById("webauthn_auth_data") !== null) {
|
||||
// Check Browser support
|
||||
if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {
|
||||
window.alert('Browser not supported for WebAuthn.');
|
||||
return;
|
||||
}
|
||||
|
||||
window.fetch("/api/v1/get/webauthn-tfa-get-args", {method:'GET',cache:'no-cache'}).then(response => {
|
||||
return response.json();
|
||||
}).then(json => {
|
||||
console.log(json);
|
||||
if (json.success === false) throw new Error();
|
||||
|
||||
recursiveBase64StrToArrayBuffer(json);
|
||||
return json;
|
||||
}).then(getCredentialArgs => {
|
||||
console.log(getCredentialArgs);
|
||||
return navigator.credentials.get(getCredentialArgs);
|
||||
}).then(cred => {
|
||||
console.log(cred);
|
||||
return {
|
||||
id: cred.rawId ? arrayBufferToBase64(cred.rawId) : null,
|
||||
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
||||
authenticatorData: cred.response.authenticatorData ? arrayBufferToBase64(cred.response.authenticatorData) : null,
|
||||
signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null
|
||||
};
|
||||
}).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) {
|
||||
console.log(AuthenticatorAttestationResponse);
|
||||
|
||||
var form = document.getElementById('webauthn_auth_form');
|
||||
var auth = document.getElementById('webauthn_auth_data');
|
||||
console.log("Authenticate callback", AuthenticatorAttestationResponse);
|
||||
auth.value = AuthenticatorAttestationResponse;
|
||||
form.submit();
|
||||
}).catch(function(err) {
|
||||
if (typeof err.message === 'undefined') {
|
||||
mailcow_alert_box(lang_fido2.fido2_validation_failed, "danger");
|
||||
} else {
|
||||
mailcow_alert_box(lang_fido2.fido2_validation_failed + ":<br><i>" + err.message + "</i>", "danger");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#ConfirmTFAModal').on('hidden.bs.modal', function(){
|
||||
$.ajax({
|
||||
|
@ -327,46 +347,62 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||
});
|
||||
$("option:selected").prop("selected", false);
|
||||
}
|
||||
if ($(this).val() == "u2f") {
|
||||
$('#U2FModal').modal('show');
|
||||
$("option:selected").prop("selected", false);
|
||||
$("#start_u2f_register").click(function(){
|
||||
$('#u2f_return_code').html('');
|
||||
$('#u2f_return_code').hide();
|
||||
$('#u2f_status_reg').html('<p><i class="bi bi-arrow-repeat icon-spin"></i> ' + lang_tfa.init_u2f + '</p>');
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
cache: false,
|
||||
dataType: 'script',
|
||||
url: "/api/v1/get/u2f-registration/{{ mailcow_cc_username|url_encode(true)|default('null') }}",
|
||||
complete: function(data){
|
||||
data;
|
||||
setTimeout(function() {
|
||||
console.log("Ready to register");
|
||||
$('#u2f_status_reg').html(lang_tfa.waiting_usb_register);
|
||||
u2f.register(appId, registerRequests, registeredKeys, function(deviceResponse) {
|
||||
var form = document.getElementById('u2f_reg_form');
|
||||
var reg = document.getElementById('u2f_register_data');
|
||||
console.log("Register callback: ", data);
|
||||
if (deviceResponse.errorCode && deviceResponse.errorCode != 0) {
|
||||
var u2f_return_code = document.getElementById('u2f_return_code');
|
||||
u2f_return_code.style.display = u2f_return_code.style.display === 'none' ? '' : null;
|
||||
if (deviceResponse.errorCode == "4") {
|
||||
deviceResponse.errorCode = "4 - The presented device is not eligible for this request. For a registration request this may mean that the token is already registered, and for a sign request it may mean that the token does not know the presented key handle";
|
||||
}
|
||||
else if (deviceResponse.errorCode == "5") {
|
||||
deviceResponse.errorCode = "5 - Timeout reached before request could be satisfied.";
|
||||
}
|
||||
u2f_return_code.innerHTML = lang_tfa.error_code + ': ' + deviceResponse.errorCode + ' ' + lang_tfa.reload_retry;
|
||||
return;
|
||||
if ($(this).val() == "webauthn") {
|
||||
// check if Browser is supported
|
||||
if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {
|
||||
window.alert('Browser not supported.');
|
||||
return;
|
||||
}
|
||||
|
||||
// show modal
|
||||
$('#WebAuthnModal').modal('show');
|
||||
$("option:selected").prop("selected", false);
|
||||
|
||||
$("#start_webauthn_register").click(() => {
|
||||
var key_id = document.getElementsByName('key_id')[1].value;
|
||||
|
||||
// fetch WebAuthn CreateArgs
|
||||
window.fetch("/api/v1/get/webauthn-tfa-registration/{{ mailcow_cc_username|url_encode(true)|default('null') }}", {method:'GET',cache:'no-cache'}).then(response => {
|
||||
return response.json();
|
||||
}).then(json => {
|
||||
console.log(json);
|
||||
|
||||
if (json.success === false) throw new Error(json.msg);
|
||||
|
||||
recursiveBase64StrToArrayBuffer(json);
|
||||
|
||||
return json;
|
||||
}).then(createCredentialArgs => {
|
||||
return navigator.credentials.create(createCredentialArgs);
|
||||
}).then(cred => {
|
||||
return {
|
||||
clientDataJSON: cred.response.clientDataJSON ? arrayBufferToBase64(cred.response.clientDataJSON) : null,
|
||||
attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null,
|
||||
key_id: key_id,
|
||||
tfa_method: "webauthn"
|
||||
};
|
||||
}).then(JSON.stringify).then(AuthenticatorAttestationResponse => {
|
||||
console.log(AuthenticatorAttestationResponse);
|
||||
|
||||
return window.fetch("/api/v1/add/webauthn-tfa-registration", {method:'POST', body: AuthenticatorAttestationResponse, cache:'no-cache'});
|
||||
}).then(response => {
|
||||
return response.json();
|
||||
}).then(json => {
|
||||
console.log(json);
|
||||
|
||||
if (json.success) {
|
||||
console.log("success");
|
||||
window.location.reload();
|
||||
} else {
|
||||
throw new Error(json.msg);
|
||||
}
|
||||
reg.value = JSON.stringify(deviceResponse);
|
||||
form.submit();
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}).catch(function(err) {
|
||||
console.log(err);
|
||||
var webauthn_return_code = document.getElementById('webauthn_return_code');
|
||||
webauthn_return_code.style.display = webauthn_return_code.style.display === 'none' ? '' : null;
|
||||
webauthn_return_code.innerHTML = lang_tfa.error_code + ': ' + err + ' ' + lang_tfa.reload_retry;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
if ($(this).val() == "none") {
|
||||
$('#DisableTFAModal').modal('show');
|
||||
|
|
|
@ -37,15 +37,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="U2FModal" tabindex="-1" role="dialog" aria-labelledby="U2FModalLabel">
|
||||
<div class="modal fade" id="WebAuthnModal" tabindex="-1" role="dialog" aria-labelledby="WebAuthnModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
|
||||
<h3 class="modal-title">{{ lang.tfa.u2f }}</h3>
|
||||
<h3 class="modal-title">{{ lang.tfa.webauthn }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form role="form" method="post" id="u2f_reg_form">
|
||||
<form role="form" method="post" id="webauthn_reg_form">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="key_id" placeholder="{{ lang.tfa.key_id }}" autocomplete="off" required>
|
||||
</div>
|
||||
|
@ -54,18 +54,18 @@
|
|||
</div>
|
||||
<hr>
|
||||
<center>
|
||||
<div style="cursor:pointer" id="start_u2f_register">
|
||||
<div style="cursor:pointer" id="start_webauthn_register">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24">
|
||||
<path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path>
|
||||
</svg>
|
||||
<p>{{ lang.tfa.start_u2f_validation }}</p>
|
||||
<p>{{ lang.tfa.start_webauthn_validation }}</p>
|
||||
<hr>
|
||||
</div>
|
||||
</center>
|
||||
<p id="u2f_status_reg"></p>
|
||||
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
||||
<input type="hidden" name="token" id="u2f_register_data"/>
|
||||
<input type="hidden" name="tfa_method" value="u2f">
|
||||
<p id="webauthn_status_reg"></p>
|
||||
<div class="alert alert-danger" style="display:none" id="webauthn_return_code"></div>
|
||||
<input type="hidden" name="token" id="webauthn_register_data"/>
|
||||
<input type="hidden" name="tfa_method" value="webauthn">
|
||||
<input type="hidden" name="set_tfa"/><br/>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -154,24 +154,6 @@
|
|||
<button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-sm btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if pending_tfa_method == 'u2f' %}
|
||||
<form role="form" method="post" id="u2f_auth_form">
|
||||
<center>
|
||||
<div id="start_u2f_confirmation">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24">
|
||||
<path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path>
|
||||
</svg>
|
||||
<p>{{ lang.tfa.start_u2f_validation }}</p>
|
||||
<hr>
|
||||
</div>
|
||||
</center>
|
||||
<p id="u2f_status_auth"></p>
|
||||
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
|
||||
<input type="hidden" name="token" id="u2f_auth_data"/>
|
||||
<input type="hidden" name="tfa_method" value="u2f">
|
||||
<input type="hidden" name="verify_tfa_login"/><br/>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if pending_tfa_method == 'totp' %}
|
||||
<form role="form" method="post">
|
||||
<div class="form-group">
|
||||
|
@ -187,6 +169,44 @@
|
|||
{% if pending_tfa_method == 'hotp' %}
|
||||
<div class="empty"></div>
|
||||
{% endif %}
|
||||
|
||||
{% if pending_tfa_method == 'webauthn' %}
|
||||
<form role="form" method="post" id="webauthn_auth_form">
|
||||
<center>
|
||||
<div id="start_webauthn_confirmation">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24">
|
||||
<path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path>
|
||||
</svg>
|
||||
<p>{{ lang.tfa.start_webauthn_validation }}</p>
|
||||
<hr>
|
||||
</div>
|
||||
</center>
|
||||
<p id="webauthn_status_auth"></p>
|
||||
<div class="alert alert-danger" style="display:none" id="webauthn_return_code"></div>
|
||||
<input type="hidden" name="token" id="webauthn_auth_data"/>
|
||||
<input type="hidden" name="tfa_method" value="webauthn">
|
||||
<input type="hidden" name="verify_tfa_login"/><br/>
|
||||
</form>
|
||||
{% endif %}
|
||||
{# leave this here to support older keys that used u2f for registration #}
|
||||
{% if pending_tfa_method == 'u2f' %}
|
||||
<form role="form" method="post" id="webauthn_auth_form">
|
||||
<center>
|
||||
<div id="start_webauthn_confirmation">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24">
|
||||
<path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path>
|
||||
</svg>
|
||||
<p>{{ lang.tfa.start_webauthn_validation }}</p>
|
||||
<hr>
|
||||
</div>
|
||||
</center>
|
||||
<p id="webauthn_status_auth"></p>
|
||||
<div class="alert alert-danger" style="display:none" id="webauthn_return_code"></div>
|
||||
<input type="hidden" name="token" id="webauthn_auth_data"/>
|
||||
<input type="hidden" name="tfa_method" value="webauthn">
|
||||
<input type="hidden" name="verify_tfa_login"/><br/>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue