Moved ito Gitea
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.DS_Store
|
||||||
7
.well-known/apple-app-site-association
Normal file
7
.well-known/apple-app-site-association
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"webcredentials":{
|
||||||
|
"apps":[
|
||||||
|
"RQQ6N82NA8.dqj.dFido2LibDemo"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
5
.well-known/apple-app-site-association.bak
Normal file
5
.well-known/apple-app-site-association.bak
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"webcredentials":{
|
||||||
|
"apps":["TWH42HVGKA.dqj.dFido2LibDemo"]
|
||||||
|
}
|
||||||
|
}
|
||||||
25
.well-known/assetlinks.json
Normal file
25
.well-known/assetlinks.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"relation" : [
|
||||||
|
"delegate_permission/common.handle_all_urls",
|
||||||
|
"delegate_permission/common.get_login_creds"
|
||||||
|
],
|
||||||
|
"target" : {
|
||||||
|
"namespace" : "web",
|
||||||
|
"site" : "https://test.amipro.me"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"relation" : [
|
||||||
|
"delegate_permission/common.handle_all_urls",
|
||||||
|
"delegate_permission/common.get_login_creds"
|
||||||
|
],
|
||||||
|
"target" : {
|
||||||
|
"namespace" : "android_app",
|
||||||
|
"package_name" : "dqj.dfido2lib.demo",
|
||||||
|
"sha256_cert_fingerprints" : [
|
||||||
|
"08:A0:7E:3C:6A:40:E1:6E:BB:DC:F1:79:0E:1B:A4:4D:24:6F:DE:D2:1A:DC:D2:13:CD:A2:0A:AA:1D:48:D8:EC"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
406
devices.html
Normal file
406
devices.html
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html
|
||||||
|
lang="en-US"
|
||||||
|
class="light-style"
|
||||||
|
>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<title>Devices - amiPro sample site</title>
|
||||||
|
|
||||||
|
<meta name="description" content="" />
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Icons. Uncomment required icon fonts -->
|
||||||
|
<link rel="stylesheet" href="files/boxicons.css?v=20230405" />
|
||||||
|
|
||||||
|
<!-- Core CSS -->
|
||||||
|
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
|
||||||
|
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
|
||||||
|
<link rel="stylesheet" href="files/demo.css" />
|
||||||
|
|
||||||
|
<!-- Vendors CSS -->
|
||||||
|
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
|
||||||
|
|
||||||
|
<!-- Page CSS -->
|
||||||
|
|
||||||
|
<!-- Helpers -->
|
||||||
|
<script src="files/helpers.js"></script>
|
||||||
|
|
||||||
|
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
|
||||||
|
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
|
||||||
|
<script src="files/config.js"></script>
|
||||||
|
|
||||||
|
<script src="files/jquery.js"></script>
|
||||||
|
<script src="files/popper.js"></script>
|
||||||
|
<script src="files/bootstrap.js"></script>
|
||||||
|
<script src="files/perfect-scrollbar.js"></script>
|
||||||
|
<script src="files/menu.js"></script>
|
||||||
|
<script src="files/main.js"></script>
|
||||||
|
|
||||||
|
<script src="files/ua-parser.js"></script>
|
||||||
|
|
||||||
|
<script src="files/amipro_utils.js?v=20230401402"></script>
|
||||||
|
<script src="files/dfido2-lib.js?v=20230918"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var user_id, reg_session_id;
|
||||||
|
|
||||||
|
const i18n_messages = new Map();
|
||||||
|
|
||||||
|
var lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Add device");
|
||||||
|
lang_map.set("zh-CN", "添加设备");
|
||||||
|
lang_map.set("ja", "デバイスを追加");
|
||||||
|
i18n_messages.set("btn_add", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "My devices");
|
||||||
|
lang_map.set("zh-CN", "我的设备");
|
||||||
|
lang_map.set("ja", "マイデバイス");
|
||||||
|
i18n_messages.set("my_devices", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Device registered");
|
||||||
|
lang_map.set("zh-CN", "添加设备成功");
|
||||||
|
lang_map.set("ja", "デバイス登録完了。");
|
||||||
|
i18n_messages.set("msg_register_ok", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Device");
|
||||||
|
lang_map.set("zh-CN", "设备");
|
||||||
|
lang_map.set("ja", "デバイス");
|
||||||
|
i18n_messages.set("title_device", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Registered time");
|
||||||
|
lang_map.set("zh-CN", "添加时间");
|
||||||
|
lang_map.set("ja", "登録時間");
|
||||||
|
i18n_messages.set("title_time", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Actions");
|
||||||
|
lang_map.set("zh-CN", "操作");
|
||||||
|
lang_map.set("ja", "操作");
|
||||||
|
i18n_messages.set("title_act", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Delete");
|
||||||
|
lang_map.set("zh-CN", "删除");
|
||||||
|
lang_map.set("ja", "削除");
|
||||||
|
i18n_messages.set("title_del", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Contact us");
|
||||||
|
lang_map.set("zh-CN", "联系我们");
|
||||||
|
lang_map.set("ja", "お問い合わせ");
|
||||||
|
i18n_messages.set("title_contact", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Log out");
|
||||||
|
lang_map.set("zh-CN", "登出");
|
||||||
|
lang_map.set("ja", "ログアウト");
|
||||||
|
i18n_messages.set("title_logout", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "No devices, please add.");
|
||||||
|
lang_map.set("zh-CN", "无设备,请添加。");
|
||||||
|
lang_map.set("ja", "デバイスがなし、追加してください。");
|
||||||
|
i18n_messages.set("title_empty_list", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Deleted device.");
|
||||||
|
lang_map.set("zh-CN", "设备删除成功。");
|
||||||
|
lang_map.set("ja", "デバイスを削除しました。");
|
||||||
|
i18n_messages.set("msg_deldev_ok", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Do you want to delete this device?");
|
||||||
|
lang_map.set("zh-CN", "确认删除此设备吗?");
|
||||||
|
lang_map.set("ja", "デバイスを削除しますか?");
|
||||||
|
i18n_messages.set("msg_confirm_deldev", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "The fido2 session is good.");
|
||||||
|
lang_map.set("zh-CN", "FIDO2会话正常。");
|
||||||
|
lang_map.set("ja", "FIDO2セッションは正常です。");
|
||||||
|
i18n_messages.set("msg_session_status_ok", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "The fido2 session is invalid. You can refuse accessing to your site's pages.");
|
||||||
|
lang_map.set("zh-CN", "FIDO2会话无效。您可以拒绝访问您网站的页面。");
|
||||||
|
lang_map.set("ja", "FIDO2セッションは無効です。あなたのサイトのページにアクセスすることを拒否できます。");
|
||||||
|
i18n_messages.set("msg_session_status_fail", lang_map);
|
||||||
|
|
||||||
|
window.onload = async function() {
|
||||||
|
setI18NText(i18n_messages)
|
||||||
|
|
||||||
|
|
||||||
|
setSessionStatus();
|
||||||
|
|
||||||
|
let url = new URL(window.location.href);
|
||||||
|
let params = url.searchParams;
|
||||||
|
reg_session_id = params.get("rid");
|
||||||
|
user_id = params.get("uid");
|
||||||
|
|
||||||
|
if(reg_session_id && 0 < reg_session_id.length){
|
||||||
|
const reg_unm_json = await getRegistrationUser(reg_session_id);
|
||||||
|
user_id = ''
|
||||||
|
if(reg_unm_json && reg_unm_json.status === 'ok'){
|
||||||
|
user_id = reg_unm_json.username;
|
||||||
|
await addDevice(reg_unm_json.displayname)
|
||||||
|
}else{
|
||||||
|
window.location.href = "login.html";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('#user_id').html(user_id);
|
||||||
|
listDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setSessionStatus(){
|
||||||
|
const sessionOk = await validSession();
|
||||||
|
if(sessionOk){
|
||||||
|
$('#session_status').html(getI18NText(i18n_messages, 'msg_session_status_ok'))
|
||||||
|
}else{
|
||||||
|
$('#session_status').html(getI18NText(i18n_messages, 'msg_session_status_fail'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addDevice(display_name){
|
||||||
|
//You can change the display name or ask the user to input it, if the display_name is null.
|
||||||
|
const result = await registerFido2(user_id, display_name?display_name:'dis_'+user_id);
|
||||||
|
|
||||||
|
if(result.status === 'ok'){
|
||||||
|
//You can try to insert user_id into your user db now if this user_id does not exist in your db.
|
||||||
|
|
||||||
|
alert($('#msg_register_ok').html());
|
||||||
|
listDevices();
|
||||||
|
}else{
|
||||||
|
errProcessFido2(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
setSessionStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delDevice(device_id){
|
||||||
|
if(window.confirm(getI18NText(i18n_messages, 'msg_confirm_deldev'))){
|
||||||
|
const result = await delUserDeviceFido2(device_id)
|
||||||
|
if("ok" === result.status){
|
||||||
|
alert(getI18NText(i18n_messages, 'msg_deldev_ok'));
|
||||||
|
listDevices();
|
||||||
|
}else{
|
||||||
|
const msg = getI18NErrorMessage(result.errorMessage);
|
||||||
|
alert(msg?msg:result.errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listDevices(){
|
||||||
|
const result = await listUserDevicesFido2()
|
||||||
|
$("#devices_list").html("");
|
||||||
|
if("ok" === result.status){
|
||||||
|
let lst_html = ""
|
||||||
|
|
||||||
|
for(let dev of result.devices){
|
||||||
|
var dev_desc = dev.desc
|
||||||
|
if(!dev_desc || 0 === dev_desc.length){
|
||||||
|
let parser = new UAParser(dev.userAgent);
|
||||||
|
|
||||||
|
if(parser.getOS().name){
|
||||||
|
dev_desc = parser.getDevice().model + ',' + parser.getOS().name + ',' + parser.getBrowser().name;
|
||||||
|
}else{
|
||||||
|
dev_desc = dev.userAgent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var date = new Date(dev.registered_time);
|
||||||
|
|
||||||
|
lst_html += '<tr><td><strong>'+ dev_desc +'</strong></td><td>'+
|
||||||
|
date.toLocaleString()+'</td><td>'+
|
||||||
|
"<a style=\"padding-left: 0px;\" class=\"dropdown-item\" href=\"javascript:delDevice('"+dev.device_id+"');\">"+
|
||||||
|
'<i class="bx bx-trash me-1"></i><span id="title_del"> '+ getI18NText(i18n_messages, 'title_del') +'</span></a></div></td></tr>'
|
||||||
|
}
|
||||||
|
$("#devices_list").html(lst_html);
|
||||||
|
}else{
|
||||||
|
const msg = getI18NErrorMessage(result.errorMessage); //fido2LibErrMsgLanguages.japanese
|
||||||
|
alert(msg?msg:result.errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="msg_register_ok" style="display:none;">Device registered</div>
|
||||||
|
|
||||||
|
<!-- Layout wrapper -->
|
||||||
|
<div class="layout-wrapper layout-content-navbar">
|
||||||
|
<div class="layout-container">
|
||||||
|
<!-- Layout container -->
|
||||||
|
<div class="layout-page">
|
||||||
|
<!-- Navbar -->
|
||||||
|
|
||||||
|
<nav
|
||||||
|
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
|
||||||
|
id="layout-navbar"
|
||||||
|
>
|
||||||
|
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-3 me-xl-0 d-xl-none">
|
||||||
|
<a class="nav-item nav-link px-0 me-xl-4" href="javascript:void(0)">
|
||||||
|
<i class="bx bx-menu bx-sm"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
|
||||||
|
<!-- Search -->
|
||||||
|
<div class="navbar-nav align-items-center">
|
||||||
|
<div class="nav-item d-flex align-items-center">
|
||||||
|
<i class="bx bx-user fs-4 lh-0"></i>
|
||||||
|
WELCOME <span id="session_status" style="padding-left: 20px;"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /Search -->
|
||||||
|
|
||||||
|
<ul class="navbar-nav flex-row align-items-center ms-auto">
|
||||||
|
|
||||||
|
<!-- User -->
|
||||||
|
<li class="nav-item navbar-dropdown dropdown-user dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle hide-arrow" href="javascript:void(0);" data-bs-toggle="dropdown">
|
||||||
|
<div class="avatar avatar-online">
|
||||||
|
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end">
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="#">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="flex-shrink-0 me-3">
|
||||||
|
<div class="avatar avatar-online">
|
||||||
|
<img src="files/avatar.png" alt class="w-px-40 h-auto rounded-circle" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<span class="fw-semibold d-block" id="user_id">User</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="login.html">
|
||||||
|
<i class="bx bx-power-off me-2"></i>
|
||||||
|
<span class="align-middle" id="title_logout">Log Out</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<!--/ User -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- / Navbar -->
|
||||||
|
|
||||||
|
<!-- Content wrapper -->
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<!-- Content -->
|
||||||
|
|
||||||
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
|
|
||||||
|
<!-- Basic Bootstrap Table -->
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header" id="my_devices">My devices</h5>
|
||||||
|
<button style="width:30%;float: right;margin-left: 66%;" class="btn btn-info" onclick="addDevice();"
|
||||||
|
id="btn_add">Add device</button>
|
||||||
|
<!-- button style="width:30%;float: right;margin-left: 66%;" class="btn btn-info" onclick="listDevices();"
|
||||||
|
id="btn_add">List</button -->
|
||||||
|
<div class="table-responsive text-nowrap">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th id="title_device">Device</th>
|
||||||
|
<th id="title_time">Registered time</th>
|
||||||
|
<th id="title_act">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="table-border-bottom-0" id="devices_list">
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" style="text-align: center;" id="title_empty_list"></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--/ Basic Bootstrap Table -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- / Content -->
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="content-footer footer bg-footer-theme">
|
||||||
|
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
|
||||||
|
<div class="mb-2 mb-md-0">
|
||||||
|
©
|
||||||
|
<script>
|
||||||
|
document.write(new Date().getFullYear());
|
||||||
|
</script>
|
||||||
|
<a href="https://www.amipro.me" target="_blank" class="footer-link fw-bolder">amiPro</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="mailto:sales@amipro.me?subject=contact"
|
||||||
|
target="_blank"
|
||||||
|
class="footer-link me-4"
|
||||||
|
id="title_contact"
|
||||||
|
>Contact</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<!-- / Footer -->
|
||||||
|
|
||||||
|
<div class="content-backdrop fade"></div>
|
||||||
|
</div>
|
||||||
|
<!-- Content wrapper -->
|
||||||
|
</div>
|
||||||
|
<!-- / Layout page -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Overlay -->
|
||||||
|
<div class="layout-overlay layout-menu-toggle"></div>
|
||||||
|
</div>
|
||||||
|
<!-- / Layout wrapper -->
|
||||||
|
|
||||||
|
<!-- div class="buy-now">
|
||||||
|
<a
|
||||||
|
href="https://themeselection.com/products/sneat-bootstrap-html-admin-template/"
|
||||||
|
target="_blank"
|
||||||
|
class="btn btn-danger btn-buy-now"
|
||||||
|
>Upgrade to Pro</a
|
||||||
|
>
|
||||||
|
</div -->
|
||||||
|
|
||||||
|
<!-- Page JS -->
|
||||||
|
|
||||||
|
<!-- Place this tag in your head or just before your close body tag. -->
|
||||||
|
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
36
files/amipro_utils.js
Normal file
36
files/amipro_utils.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* amiPro utils
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function setI18NText(i18n_map){
|
||||||
|
for (const key of i18n_map.keys()) {
|
||||||
|
const elm = $("#"+key);
|
||||||
|
if(elm){
|
||||||
|
const lang = window.navigator.language;
|
||||||
|
var elem = i18n_map.get(key)
|
||||||
|
var msg = null
|
||||||
|
if(elem){
|
||||||
|
msg = elem.get(lang)
|
||||||
|
if(!msg)msg=elem.get('en-US');
|
||||||
|
}
|
||||||
|
if(!msg)msg = key+"-"+lang
|
||||||
|
|
||||||
|
$("#"+key).html(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getI18NText(i18n_map, key){
|
||||||
|
const lang = window.navigator.language;
|
||||||
|
var elem = i18n_map.get(key)
|
||||||
|
var msg = null
|
||||||
|
if(elem){
|
||||||
|
msg = elem.get(lang)
|
||||||
|
if(!msg)msg=elem.get('en-US');
|
||||||
|
}
|
||||||
|
if(!msg)msg = key+"-"+lang
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
BIN
files/avatar.png
Normal file
BIN
files/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
809
files/bootstrap.js
vendored
Normal file
809
files/bootstrap.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6511
files/boxicons.css
Normal file
6511
files/boxicons.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
files/boxicons/boxicons.eot
Normal file
BIN
files/boxicons/boxicons.eot
Normal file
Binary file not shown.
1551
files/boxicons/boxicons.svg
Normal file
1551
files/boxicons/boxicons.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.1 MiB |
BIN
files/boxicons/boxicons.ttf
Normal file
BIN
files/boxicons/boxicons.ttf
Normal file
Binary file not shown.
BIN
files/boxicons/boxicons.woff
Normal file
BIN
files/boxicons/boxicons.woff
Normal file
Binary file not shown.
BIN
files/boxicons/boxicons.woff2
Normal file
BIN
files/boxicons/boxicons.woff2
Normal file
Binary file not shown.
27
files/config.js
Normal file
27
files/config.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Config
|
||||||
|
* -------------------------------------------------------------------------------------
|
||||||
|
* ! IMPORTANT: Make sure you clear the browser local storage In order to see the config changes in the template.
|
||||||
|
* ! To clear local storage: (https://www.leadshook.com/help/how-to-clear-local-storage-in-google-chrome-browser/).
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// JS global variables
|
||||||
|
let config = {
|
||||||
|
colors: {
|
||||||
|
primary: '#696cff',
|
||||||
|
secondary: '#8592a3',
|
||||||
|
success: '#71dd37',
|
||||||
|
info: '#03c3ec',
|
||||||
|
warning: '#ffab00',
|
||||||
|
danger: '#ff3e1d',
|
||||||
|
dark: '#233446',
|
||||||
|
black: '#000',
|
||||||
|
white: '#fff',
|
||||||
|
body: '#f4f5fb',
|
||||||
|
headingColor: '#566a7f',
|
||||||
|
axisColor: '#a1acb8',
|
||||||
|
borderColor: '#eceef1'
|
||||||
|
}
|
||||||
|
};
|
||||||
16643
files/core.css
Normal file
16643
files/core.css
Normal file
File diff suppressed because one or more lines are too long
107
files/demo.css
Normal file
107
files/demo.css
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* demo.css
|
||||||
|
* File include item demo only specific css only
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.menu .app-brand.demo {
|
||||||
|
height: 64px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-brand-logo.demo svg {
|
||||||
|
width: 22px;
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-brand-text.demo {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
text-transform: lowercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ! For .layout-navbar-fixed added fix padding top tpo .layout-page */
|
||||||
|
/* Detached navbar */
|
||||||
|
.layout-navbar-fixed .layout-wrapper:not(.layout-horizontal):not(.layout-without-menu) .layout-page {
|
||||||
|
padding-top: 76px !important;
|
||||||
|
}
|
||||||
|
/* Default navbar */
|
||||||
|
.layout-navbar-fixed .layout-wrapper:not(.layout-without-menu) .layout-page {
|
||||||
|
padding-top: 64px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navbar page z-index issue solution */
|
||||||
|
.content-wrapper .navbar {
|
||||||
|
z-index: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Content
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.demo-blocks > * {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-inline-spacing > * {
|
||||||
|
margin: 1rem 0.375rem 0 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ? .demo-vertical-spacing class is used to have vertical margins between elements. To remove margin-top from the first-child, use .demo-only-element class with .demo-vertical-spacing class. For example, we have used this class in forms-input-groups.html file. */
|
||||||
|
.demo-vertical-spacing > * {
|
||||||
|
margin-top: 1rem !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.demo-vertical-spacing.demo-only-element > :first-child {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-vertical-spacing-lg > * {
|
||||||
|
margin-top: 1.875rem !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.demo-vertical-spacing-lg.demo-only-element > :first-child {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-vertical-spacing-xl > * {
|
||||||
|
margin-top: 5rem !important;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
.demo-vertical-spacing-xl.demo-only-element > :first-child {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rtl-only {
|
||||||
|
display: none !important;
|
||||||
|
text-align: left !important;
|
||||||
|
direction: ltr !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir='rtl'] .rtl-only {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Layout demo
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
.layout-demo-wrapper {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
.layout-demo-placeholder img {
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
.layout-demo-info {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
616
files/dfido2-lib.js
Normal file
616
files/dfido2-lib.js
Normal file
@@ -0,0 +1,616 @@
|
|||||||
|
const DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION = 'fido2_user_session'
|
||||||
|
const DFIDO2_LIB_LOCALSTG_NAME_REGISTERED = 'dfido2_lib_registered'
|
||||||
|
const DFIDO2_LIB_LOCALSTG_NAME_SVR_URL = 'dfido2_lib_svr_url'
|
||||||
|
|
||||||
|
/** ===APIs=== */
|
||||||
|
|
||||||
|
if(!localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)){
|
||||||
|
localStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL, 'https://fido2.amipro.me')
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFidoServerURL(url){
|
||||||
|
localStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canTryAutoAuthentication(){
|
||||||
|
//const session_text = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
||||||
|
//alert('canTryAuth:'+session_text+"|"+(null != localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED)))
|
||||||
|
return null != localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} userId
|
||||||
|
* @param {String} rpId
|
||||||
|
*/
|
||||||
|
async function authenticateFido2(userId = null, rpId = null) {
|
||||||
|
var result
|
||||||
|
result = await doAssertion(userId, rpId);
|
||||||
|
if(result.status === 'ok'){
|
||||||
|
sessionStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION,
|
||||||
|
JSON.stringify({session:result.session, uid:result.username}))
|
||||||
|
localStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED, new Date());
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} userId
|
||||||
|
* @param {String} rpId
|
||||||
|
*/
|
||||||
|
async function registerFido2(userId, userDisplay, rpId = null) {
|
||||||
|
if (isWebAuthnSupported()) {
|
||||||
|
const result = await doAttestation(userId, userDisplay, rpId);
|
||||||
|
if(result.status === 'ok'){
|
||||||
|
localStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED, new Date());
|
||||||
|
sessionStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION, JSON.stringify({session:result.session, uid:result.username}))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}else return {status:'failed', errorMessage: getI18NErrorMessage('Fido2LibErr101:')}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} rpId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function listUserDevicesFido2(rpId = null) {
|
||||||
|
try {
|
||||||
|
const session_text = sessionStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
||||||
|
if(!session_text) return {status:'ok', devices:[]}
|
||||||
|
|
||||||
|
const session_data = JSON.parse(session_text)
|
||||||
|
|
||||||
|
let req = {session: session_data.session}
|
||||||
|
if (rpId && 0 < rpId.length) {
|
||||||
|
req.rp = { id: rpId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/dvc/lst", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(req)
|
||||||
|
});
|
||||||
|
const resp = await response.json();
|
||||||
|
if ('ok' === resp.status && resp.session === session_data.session) {
|
||||||
|
return {status:'ok', devices:resp.devices}
|
||||||
|
} else {
|
||||||
|
return {status:'failed', errorMessage: resp.errorMessage}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
let msg = err.message ? err.message : err;
|
||||||
|
//console.error("Assertion err: ", err);
|
||||||
|
var errRtn = {status:'failed', errorMessage: msg};
|
||||||
|
if(err.name) errRtn.name = err.name
|
||||||
|
return errRtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delUserDeviceFido2(device_id, rpId = null) {
|
||||||
|
try {
|
||||||
|
const session_text = sessionStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
||||||
|
const session_data = JSON.parse(session_text)
|
||||||
|
|
||||||
|
let req = {session: session_data.session, device_id: device_id}
|
||||||
|
if (rpId && 0 < rpId.length) {
|
||||||
|
req.rp = { id: rpId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/dvc/rm", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(req)
|
||||||
|
});
|
||||||
|
const resp = await response.json();
|
||||||
|
|
||||||
|
return resp
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
let msg = err.message ? err.message : err;
|
||||||
|
//console.error("Assertion err: ", err);
|
||||||
|
var errRtn = {status:'failed', errorMessage: msg};
|
||||||
|
if(err.name) errRtn.name = err.name
|
||||||
|
return errRtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSessionId() {
|
||||||
|
var rtn = null
|
||||||
|
try {
|
||||||
|
const session_text = sessionStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
||||||
|
if(session_text){
|
||||||
|
const session_data = JSON.parse(session_text)
|
||||||
|
|
||||||
|
rtn = session_data.session
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtn
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validSession(rpId = null) {
|
||||||
|
try {
|
||||||
|
const session_text = sessionStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
||||||
|
if(!session_text) return false
|
||||||
|
|
||||||
|
const session_data = JSON.parse(session_text)
|
||||||
|
|
||||||
|
let req = {session: session_data.session}
|
||||||
|
if (rpId && 0 < rpId.length) {
|
||||||
|
req.rp = { id: rpId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/validsession", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(req)
|
||||||
|
});
|
||||||
|
const resp = await response.json();
|
||||||
|
|
||||||
|
return resp.status === 'ok'
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logoutFido2UserSession(){
|
||||||
|
const session_text = sessionStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
|
||||||
|
if(!session_text) return
|
||||||
|
|
||||||
|
const session_data = JSON.parse(session_text)
|
||||||
|
let req = {session: session_data['session'], username: session_data['uid']}
|
||||||
|
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/delsession", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(req)
|
||||||
|
});
|
||||||
|
|
||||||
|
sessionStorage.removeItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getRegistrationUser(reg_session_id){
|
||||||
|
try {
|
||||||
|
let req = {session_id: reg_session_id}
|
||||||
|
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/reg/username", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(req)
|
||||||
|
});
|
||||||
|
const resp = await response.json();
|
||||||
|
|
||||||
|
return resp.username
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function errProcessFido2(result){
|
||||||
|
alert(errMessageFido2(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
function errMessageFido2(result){
|
||||||
|
var rtn
|
||||||
|
if(result.errCode && fido2LibErrCodes.unknown != result.errCode ){
|
||||||
|
switch (result.errCode){
|
||||||
|
case fido2LibErrCodes.user_canceled:
|
||||||
|
rtn=getI18NErrorMessage('Fido2LibErr102:');
|
||||||
|
break;
|
||||||
|
case fido2LibErrCodes.timeout:
|
||||||
|
rtn=getI18NErrorMessage('Fido2LibErr103:');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rtn=result.errorMessage?result.errorMessage:getI18NErrorMessage('Fido2LibErr104:');
|
||||||
|
}
|
||||||
|
}else if(result.name && "InvalidStateError" === result.name){
|
||||||
|
rtn=getI18NErrorMessage('Fido2LibErr105:');
|
||||||
|
}else if(result.errorMessage){
|
||||||
|
const msg = getI18NErrorMessage(result.errorMessage);
|
||||||
|
rtn=msg?msg:result.errorMessage;
|
||||||
|
}else{
|
||||||
|
rtn=getI18NErrorMessage(i18n_messages, 'Fido2LibErr104:');
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fido2LibErrCodes = {
|
||||||
|
user_canceled : -101,
|
||||||
|
timeout : -102,
|
||||||
|
unknown : -999
|
||||||
|
}
|
||||||
|
|
||||||
|
const errMsgs = new Map();
|
||||||
|
const fido2LibErrMsgLanguages = {
|
||||||
|
english: 'en-US',
|
||||||
|
japanese: 'ja',
|
||||||
|
chinese_cn: 'zh-CN',
|
||||||
|
//chinese_tw: 'zh-TW',
|
||||||
|
}
|
||||||
|
errMsgs.set(fido2LibErrMsgLanguages.english, new Map());
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr101:', 'Unregistered enterprise authenticator aaguid!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr102:', 'Unable to authenticate with a unique device binding key from another device!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr103:', 'Unable to verify signature!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr104:', 'Key not found!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr105:', 'Username does not exist!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr106:', 'Unique Device ID is null!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr107:', '/attestation/result request body has no ID field!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr108:', 'ID field is not Base64Url encoded in /attestation/result request body!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr109:', '/attestation/result request body has no TYPE field!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr110:', 'TYPE field is not a DOMString in /attestation/result request body!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr111:', 'The TYPE field is not a public key in the /attestation/result request body!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr112:', 'ID field is not a DOMString in /attestation/result request body!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr115:', 'authenticatorData not found!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr116:', 'authenticatorData is not base64 URL encoded!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr117:', 'Signature not found!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr118:', 'Signature is not base64 URL encoded!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr119:', 'No user session!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr120:', 'User has reached the device limit!');
|
||||||
|
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr101:', 'Your browser does not support FIDO2.');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr102:', 'The user canceled.');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr103:', 'The process timeout.');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr104:', 'System error.');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr105:', 'The same authenticator cannot be registered again.');
|
||||||
|
|
||||||
|
errMsgs.set(fido2LibErrMsgLanguages.japanese, new Map());
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr101:', '登録されていないエンタープライズ認証デバイス aaguid!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr102:', '別のデバイスからの一意のデバイス バインド キーで認証できません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr103:', '署名を認証できません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr104:', 'キーが見つかりません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr105:', 'ユーザー名は存在しません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr106:', '固有のデバイス ID が null です!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr107:', '/attestation/result request の本文に ID フィールドがありません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr108:', 'ID フィールドは、/attestation/result リクエストの本文でエンコードされた Base64Url ではありません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr109:', '/attestation/result リクエストのボディに TYPE フィールドがありません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr110:', 'TYPE フィールドは、/attestation/result リクエストの本文の DOMString ではありません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr111:', 'TYPE フィールドは、/attestation/result リクエストの本文の公開鍵ではありません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr112:', 'ID フィールドは、/attestation/result リクエストの本文の DOMString ではありません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr115:', 'authenticatorData が見つかりません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr116:', 'authenticatorData は base64 URL エンコードされていません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr117:', '署名が見つかりません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr118:', '署名は base64 URL エンコードされていません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr119:', 'ユーザーセッションがありません!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr120:', 'ユーザーはデバイスの制限数に達しました!');
|
||||||
|
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr101:', 'お使いのブラウザは FIDO2 をサポートしていません。');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr102:', 'ユーザーがキャンセルしました。');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr103:', 'プロセスがタイムアウトしました。');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr104:', 'システムエラー。');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr105:', '同じ認証デバイスを再登録することはできません。');
|
||||||
|
|
||||||
|
errMsgs.set(fido2LibErrMsgLanguages.chinese_cn, new Map());
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr101:', '未注册的企业认证器 aaguid!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr102:', '无法使用来自其他设备的唯一设备绑定密钥进行身份验证!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr103:', '无法验证签名!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr104:', '认证Key未找到!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr105:', '用户名不存在!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr106:', 'Unique Device ID 为 null!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr107:', '/attestation/result请求体没有ID字段!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr108:', 'ID字段不是/attestation/result请求体中编码的Base64Url!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr109:', '/attestation/result请求体没有TYPE字段!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr110:', '/attestation/result 请求正文中的 TYPE 字段不是 DOMString!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr111:', 'TYPE字段不是/attestation/result请求体中的公钥!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr112:', 'ID 字段不是 /attestation/result 请求体中的 DOMString!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr115:', 'authenticatorData 未找到!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr116:', 'authenticatorData 不是 base64 URL 编码!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr117:', '未找到签名!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr118:', '签名不是 base64 URL 编码!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr119:', '未建立用户会话!');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr120:', '用户已达到设备限制数!');
|
||||||
|
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr101:', '您的浏览器不支持FIDO2.');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr102:', '用户取消了操作。');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr103:', '操作超时。');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr104:', '系统错误。');
|
||||||
|
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr105:', '无法再次注册相同的认证器。');
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {String} errorMessage
|
||||||
|
* @param {errMsgLanguages} language
|
||||||
|
*/
|
||||||
|
function getI18NErrorMessage(errorMessage, language = null){
|
||||||
|
var lang = language ? language : window.navigator.language
|
||||||
|
var msgs = errMsgs.get(lang)
|
||||||
|
if(!msgs)msgs = errMsgs.get(fido2LibErrMsgLanguages.english)
|
||||||
|
if(errorMessage){
|
||||||
|
const msgHeader = 0<errorMessage.indexOf(':')?errorMessage.substring(0, errorMessage.indexOf(':')):errorMessage
|
||||||
|
const msg = msgs.get(msgHeader+":")
|
||||||
|
return msg?msgHeader+":"+msg:errorMessage;
|
||||||
|
} else return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ===utils=== */
|
||||||
|
|
||||||
|
function isWebAuthnSupported() {
|
||||||
|
if (window.PublicKeyCredential) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makePublicKey(attOptsResp) {
|
||||||
|
if (attOptsResp.excludeCredentials) {
|
||||||
|
attOptsResp.excludeCredentials = attOptsResp.excludeCredentials.map(
|
||||||
|
function (cred) {
|
||||||
|
cred.id = _base64ToArrayBuffer(_fromBase64URL(cred.id));
|
||||||
|
cred.transports = ["internal", "usb", "ble", "nfc"];
|
||||||
|
return cred;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//console.log("Attestation Options:");
|
||||||
|
//console.log(attOptsResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys = {
|
||||||
|
publicKey: {
|
||||||
|
attestation: attOptsResp.attestation,
|
||||||
|
authenticatorSelection: attOptsResp.authenticatorSelection,
|
||||||
|
excludeCredentials: attOptsResp.excludeCredentials,
|
||||||
|
rp: attOptsResp.rp,
|
||||||
|
user: {
|
||||||
|
id: _stringToArrayBuffer(attOptsResp.user.id), //_base64ToArrayBuffer(_fromBase64URL(attOptsResp.user.id)),
|
||||||
|
name: attOptsResp.user.name,
|
||||||
|
displayName: attOptsResp.user.displayName,
|
||||||
|
},
|
||||||
|
pubKeyCredParams: attOptsResp.pubKeyCredParams,
|
||||||
|
timeout: attOptsResp.timeout,
|
||||||
|
challenge: _base64ToArrayBuffer(_fromBase64URL(attOptsResp.challenge)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doAttestation(username, displayName, rpId, userVerification = 'preferred') {
|
||||||
|
var process_time_limit = Number.MAX_SAFE_INTEGER
|
||||||
|
try {
|
||||||
|
const attestationOptions = {
|
||||||
|
username: username,
|
||||||
|
displayName: encodeURIComponent(displayName),
|
||||||
|
authenticatorSelection: {
|
||||||
|
//authenticatorAttachment: "platform",
|
||||||
|
userVerification: userVerification,
|
||||||
|
requireResidentKey: false,
|
||||||
|
},
|
||||||
|
//attestation: "none",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rpId && 0 < rpId.length) {
|
||||||
|
attestationOptions.rp = { id: rpId }
|
||||||
|
}
|
||||||
|
|
||||||
|
const svrUrl = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)
|
||||||
|
const response = await fetch(svrUrl + "/attestation/options", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(attestationOptions),
|
||||||
|
});
|
||||||
|
|
||||||
|
const resp = await response.json();
|
||||||
|
if (resp.status === "failed") {
|
||||||
|
return {status:'failed', errorMessage: resp.errorMessage}
|
||||||
|
} else {
|
||||||
|
process_time_limit = (new Date()).getTime() + resp.timeout;
|
||||||
|
const res = await navigator.credentials.create(makePublicKey(resp));
|
||||||
|
if (res) {
|
||||||
|
let attResult = {
|
||||||
|
id: res.id,
|
||||||
|
rawId: _toBase64URL(btoa(_bufferToString(res.rawId)))
|
||||||
|
,
|
||||||
|
type: "public-key",
|
||||||
|
response: {
|
||||||
|
clientDataJSON: _toBase64URL(btoa(_bufferToString(res.response.clientDataJSON)))
|
||||||
|
,
|
||||||
|
attestationObject: _toBase64URL(btoa(_bufferToString(res.response.attestationObject)))
|
||||||
|
,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/attestation/result", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(attResult),
|
||||||
|
});
|
||||||
|
|
||||||
|
const respResult = await result.json();
|
||||||
|
if (respResult) {
|
||||||
|
if (respResult.status === "ok") {
|
||||||
|
return respResult
|
||||||
|
} else {
|
||||||
|
return {status:'failed', errorMessage: respResult.errorMessage}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {status:'failed', errorMessage: 'Fido2LibErr999:Svr result error'}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {status:'failed', errorMessage: 'Fido2LibErr999:Undefined Result'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
var errRtn = {status:'failed', errorMessage: err.message};
|
||||||
|
if(err.name) errRtn.name = err.name
|
||||||
|
if(err.name && 'NotAllowedError' === err.name){
|
||||||
|
const nowtm = (new Date()).getTime()
|
||||||
|
if(nowtm > process_time_limit){
|
||||||
|
errRtn.errCode = fido2LibErrCodes.timeout
|
||||||
|
}else{
|
||||||
|
errRtn.errCode = fido2LibErrCodes.user_canceled
|
||||||
|
}
|
||||||
|
}else errRtn.errCode = fido2LibErrCodes.unknown
|
||||||
|
|
||||||
|
return errRtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doAssertion(username = null, rpId = null, userVerification = 'preferred') {
|
||||||
|
var process_time_limit = Number.MAX_SAFE_INTEGER
|
||||||
|
try {
|
||||||
|
let authnOptions;
|
||||||
|
/*if (!username) {
|
||||||
|
authnOptions = {
|
||||||
|
authenticatorSelection: {
|
||||||
|
//authenticatorAttachment: "platform",
|
||||||
|
userVerification: "discouraged"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
authnOptions = {
|
||||||
|
username: username,
|
||||||
|
authenticatorSelection: {
|
||||||
|
//authenticatorAttachment: "platform",
|
||||||
|
userVerification: "preferred"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}*/
|
||||||
|
authnOptions = {
|
||||||
|
username: username,
|
||||||
|
authenticatorSelection: {
|
||||||
|
//authenticatorAttachment: "platform",
|
||||||
|
userVerification: userVerification
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rpId && 0 < rpId.length) {
|
||||||
|
authnOptions.rp = { id: rpId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/assertion/options", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(authnOptions)
|
||||||
|
});
|
||||||
|
const resp = await response.json();
|
||||||
|
if ('ok' === resp.status) {
|
||||||
|
process_time_limit = (new Date()).getTime() + resp.timeout;
|
||||||
|
resp.allowCredentials = resp.allowCredentials || [];
|
||||||
|
let mappedAllowCreds = resp.allowCredentials.map(x => {
|
||||||
|
return {
|
||||||
|
id: _base64ToArrayBuffer(_fromBase64URL(x.id)),
|
||||||
|
type: x.type,
|
||||||
|
transports: x.transports // can set like ['internal', 'usb'] to override server settings
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const cred = await navigator.credentials.get({
|
||||||
|
publicKey: {
|
||||||
|
challenge: _base64ToArrayBuffer(_fromBase64URL(resp.challenge)),
|
||||||
|
timeout: resp.timeout,
|
||||||
|
rpId: resp.rpId,
|
||||||
|
userVerification: resp.userVerification,
|
||||||
|
allowCredentials: mappedAllowCreds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cred) {
|
||||||
|
let authRequest = {
|
||||||
|
id: cred.id,
|
||||||
|
rawId: Array.from(new Uint8Array(cred.rawId)),
|
||||||
|
type: cred.type,
|
||||||
|
response: {
|
||||||
|
authenticatorData: _toBase64URL(btoa(_bufferToString(cred.response.authenticatorData))),
|
||||||
|
clientDataJSON: _toBase64URL(btoa(_bufferToString(cred.response.clientDataJSON))),
|
||||||
|
signature: _toBase64URL(btoa(_bufferToString(cred.response.signature))),
|
||||||
|
userHandle: _toBase64URL(btoa(_bufferToString(cred.response.userHandle))) //_toBase64URL(btoa(_bufferToString(cred.response.userHandle)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/assertion/result", {
|
||||||
|
method: "POST",
|
||||||
|
cache: "no-cache",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(authRequest)
|
||||||
|
});
|
||||||
|
const result = await res.json();
|
||||||
|
if (result.status === 'ok') {
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
return {status:'failed', errorMessage: result.errorMessage}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {status:'failed', errorMessage: 'Fido2LibErr999:Undefined Result'};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {status:'failed', errorMessage: resp.errorMessage}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
var errRtn = {status:'failed', errorMessage: err.message};
|
||||||
|
if(err.name) errRtn.name = err.name
|
||||||
|
if(err.name && 'NotAllowedError' === err.name){
|
||||||
|
const nowtm = (new Date()).getTime()
|
||||||
|
if(nowtm > process_time_limit){
|
||||||
|
errRtn.errCode = fido2LibErrCodes.timeout
|
||||||
|
}else{
|
||||||
|
errRtn.errCode = fido2LibErrCodes.user_canceled
|
||||||
|
}
|
||||||
|
}else errRtn.errCode = fido2LibErrCodes.unknown
|
||||||
|
|
||||||
|
return errRtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _toBase64URL(s) {
|
||||||
|
return (s = (s = (s = s.split("=")[0]).replace(/\+/g, "-")).replace(/\//g, "_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _base64ToArrayBuffer(base64) {
|
||||||
|
var binary_string = window.atob(base64);
|
||||||
|
var len = binary_string.length;
|
||||||
|
var bytes = new Uint8Array(len);
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
bytes[i] = binary_string.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _stringToArrayBuffer(src) {
|
||||||
|
return (new Uint8Array([].map.call(src, function (c) {
|
||||||
|
return c.charCodeAt(0)
|
||||||
|
}))).buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _fromBase64URL(s) {
|
||||||
|
var chk = (s = s.replace(/-/g, "+").replace(/_/g, "/")).length % 4;
|
||||||
|
if (chk) {
|
||||||
|
if (1 === chk) throw new Error("Base64url string is wrong.");
|
||||||
|
s += new Array(5 - chk).join("=");
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _bufferToString(s) {
|
||||||
|
return new Uint8Array(s).reduce((s, e) => s + String.fromCodePoint(e), "");
|
||||||
|
}
|
||||||
BIN
files/favicon.ico
Normal file
BIN
files/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
101
files/helpers.js
Normal file
101
files/helpers.js
Normal file
File diff suppressed because one or more lines are too long
112
files/jquery.js
vendored
Normal file
112
files/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
118
files/main.js
Normal file
118
files/main.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* Main
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
let menu, animate;
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
// Initialize menu
|
||||||
|
//-----------------
|
||||||
|
|
||||||
|
let layoutMenuEl = document.querySelectorAll('#layout-menu');
|
||||||
|
layoutMenuEl.forEach(function (element) {
|
||||||
|
menu = new Menu(element, {
|
||||||
|
orientation: 'vertical',
|
||||||
|
closeChildren: false
|
||||||
|
});
|
||||||
|
// Change parameter to true if you want scroll animation
|
||||||
|
window.Helpers.scrollToActive((animate = false));
|
||||||
|
window.Helpers.mainMenu = menu;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize menu togglers and bind click on each
|
||||||
|
let menuToggler = document.querySelectorAll('.layout-menu-toggle');
|
||||||
|
menuToggler.forEach(item => {
|
||||||
|
item.addEventListener('click', event => {
|
||||||
|
event.preventDefault();
|
||||||
|
window.Helpers.toggleCollapsed();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display menu toggle (layout-menu-toggle) on hover with delay
|
||||||
|
let delay = function (elem, callback) {
|
||||||
|
let timeout = null;
|
||||||
|
elem.onmouseenter = function () {
|
||||||
|
// Set timeout to be a timer which will invoke callback after 300ms (not for small screen)
|
||||||
|
if (!Helpers.isSmallScreen()) {
|
||||||
|
timeout = setTimeout(callback, 300);
|
||||||
|
} else {
|
||||||
|
timeout = setTimeout(callback, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
elem.onmouseleave = function () {
|
||||||
|
// Clear any timers set to timeout
|
||||||
|
document.querySelector('.layout-menu-toggle').classList.remove('d-block');
|
||||||
|
clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if (document.getElementById('layout-menu')) {
|
||||||
|
delay(document.getElementById('layout-menu'), function () {
|
||||||
|
// not for small screen
|
||||||
|
if (!Helpers.isSmallScreen()) {
|
||||||
|
document.querySelector('.layout-menu-toggle').classList.add('d-block');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display in main menu when menu scrolls
|
||||||
|
let menuInnerContainer = document.getElementsByClassName('menu-inner'),
|
||||||
|
menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0];
|
||||||
|
if (menuInnerContainer.length > 0 && menuInnerShadow) {
|
||||||
|
menuInnerContainer[0].addEventListener('ps-scroll-y', function () {
|
||||||
|
if (this.querySelector('.ps__thumb-y').offsetTop) {
|
||||||
|
menuInnerShadow.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
menuInnerShadow.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init helpers & misc
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
// Init BS Tooltip
|
||||||
|
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||||
|
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||||
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Accordion active class
|
||||||
|
const accordionActiveFunction = function (e) {
|
||||||
|
if (e.type == 'show.bs.collapse' || e.type == 'show.bs.collapse') {
|
||||||
|
e.target.closest('.accordion-item').classList.add('active');
|
||||||
|
} else {
|
||||||
|
e.target.closest('.accordion-item').classList.remove('active');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const accordionTriggerList = [].slice.call(document.querySelectorAll('.accordion'));
|
||||||
|
const accordionList = accordionTriggerList.map(function (accordionTriggerEl) {
|
||||||
|
accordionTriggerEl.addEventListener('show.bs.collapse', accordionActiveFunction);
|
||||||
|
accordionTriggerEl.addEventListener('hide.bs.collapse', accordionActiveFunction);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto update layout based on screen size
|
||||||
|
window.Helpers.setAutoUpdate(true);
|
||||||
|
|
||||||
|
// Toggle Password Visibility
|
||||||
|
window.Helpers.initPasswordToggle();
|
||||||
|
|
||||||
|
// Speech To Text
|
||||||
|
window.Helpers.initSpeechToText();
|
||||||
|
|
||||||
|
// Manage menu expanded/collapsed with templateCustomizer & local storage
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
// If current layout is horizontal OR current window screen is small (overlay menu) than return from here
|
||||||
|
if (window.Helpers.isSmallScreen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If current layout is vertical and current window screen is > small
|
||||||
|
|
||||||
|
// Auto update menu collapsed/expanded based on the themeConfig
|
||||||
|
window.Helpers.setCollapsed(true, false);
|
||||||
|
})();
|
||||||
101
files/menu.js
Normal file
101
files/menu.js
Normal file
File diff suppressed because one or more lines are too long
68
files/page-auth.css
Normal file
68
files/page-auth.css
Normal file
File diff suppressed because one or more lines are too long
211
files/perfect-scrollbar.css
Normal file
211
files/perfect-scrollbar.css
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* Container style
|
||||||
|
*/
|
||||||
|
.ps {
|
||||||
|
overflow: hidden !important;
|
||||||
|
overflow-anchor: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
touch-action: auto;
|
||||||
|
-ms-touch-action: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scrollbar rail styles
|
||||||
|
*/
|
||||||
|
.ps__rail-x {
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: background-color 0.2s linear, opacity 0.2s linear;
|
||||||
|
-webkit-transition: background-color 0.2s linear, opacity 0.2s linear;
|
||||||
|
height: 15px;
|
||||||
|
/* there must be 'bottom' or 'top' for ps__rail-x */
|
||||||
|
bottom: 0px;
|
||||||
|
/* please don't change 'position' */
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-y {
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
transition: background-color 0.2s linear, opacity 0.2s linear;
|
||||||
|
-webkit-transition: background-color 0.2s linear, opacity 0.2s linear;
|
||||||
|
width: 15px;
|
||||||
|
/* there must be 'right' or 'left' for ps__rail-y */
|
||||||
|
right: 0;
|
||||||
|
/* please don't change 'position' */
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps--active-x > .ps__rail-x,
|
||||||
|
.ps--active-y > .ps__rail-y {
|
||||||
|
display: block;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps:hover > .ps__rail-x,
|
||||||
|
.ps:hover > .ps__rail-y,
|
||||||
|
.ps--focus > .ps__rail-x,
|
||||||
|
.ps--focus > .ps__rail-y,
|
||||||
|
.ps--scrolling-x > .ps__rail-x,
|
||||||
|
.ps--scrolling-y > .ps__rail-y {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps .ps__rail-x:hover,
|
||||||
|
.ps .ps__rail-y:hover,
|
||||||
|
.ps .ps__rail-x:focus,
|
||||||
|
.ps .ps__rail-y:focus,
|
||||||
|
.ps .ps__rail-x.ps--clicking,
|
||||||
|
.ps .ps__rail-y.ps--clicking {
|
||||||
|
background-color: #eee;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scrollbar thumb styles
|
||||||
|
*/
|
||||||
|
.ps__thumb-x {
|
||||||
|
background-color: #aaa;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background-color 0.2s linear, height 0.2s ease-in-out;
|
||||||
|
-webkit-transition: background-color 0.2s linear, height 0.2s ease-in-out;
|
||||||
|
height: 6px;
|
||||||
|
/* there must be 'bottom' for ps__thumb-x */
|
||||||
|
bottom: 2px;
|
||||||
|
/* please don't change 'position' */
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__thumb-y {
|
||||||
|
background-color: #aaa;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background-color 0.2s linear, width 0.2s ease-in-out;
|
||||||
|
-webkit-transition: background-color 0.2s linear, width 0.2s ease-in-out;
|
||||||
|
width: 6px;
|
||||||
|
/* there must be 'right' for ps__thumb-y */
|
||||||
|
right: 2px;
|
||||||
|
/* please don't change 'position' */
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-x:hover > .ps__thumb-x,
|
||||||
|
.ps__rail-x:focus > .ps__thumb-x,
|
||||||
|
.ps__rail-x.ps--clicking .ps__thumb-x {
|
||||||
|
background-color: #999;
|
||||||
|
height: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-y:hover > .ps__thumb-y,
|
||||||
|
.ps__rail-y:focus > .ps__thumb-y,
|
||||||
|
.ps__rail-y.ps--clicking .ps__thumb-y {
|
||||||
|
background-color: #999;
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MS supports */
|
||||||
|
@supports (-ms-overflow-style: none) {
|
||||||
|
.ps {
|
||||||
|
overflow: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
|
||||||
|
.ps {
|
||||||
|
overflow: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ps {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-x {
|
||||||
|
height: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-y {
|
||||||
|
width: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-x,
|
||||||
|
.ps__rail-y,
|
||||||
|
.ps__thumb-x,
|
||||||
|
.ps__thumb-y {
|
||||||
|
border-radius: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-x:hover,
|
||||||
|
.ps__rail-x:focus,
|
||||||
|
.ps__rail-x.ps--clicking,
|
||||||
|
.ps__rail-x:hover > .ps__thumb-x,
|
||||||
|
.ps__rail-x:focus > .ps__thumb-x,
|
||||||
|
.ps__rail-x.ps--clicking > .ps__thumb-x {
|
||||||
|
height: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__rail-y:hover,
|
||||||
|
.ps__rail-y:focus,
|
||||||
|
.ps__rail-y.ps--clicking,
|
||||||
|
.ps__rail-y:hover > .ps__thumb-y,
|
||||||
|
.ps__rail-y:focus > .ps__thumb-y,
|
||||||
|
.ps__rail-y.ps--clicking > .ps__thumb-y {
|
||||||
|
width: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__thumb-x {
|
||||||
|
height: 0.25rem;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ps__thumb-y {
|
||||||
|
width: 0.25rem;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light-style .ps__thumb-x,
|
||||||
|
.light-style .ps__thumb-y {
|
||||||
|
background-color: rgba(67, 89, 113, 0.4);
|
||||||
|
}
|
||||||
|
.light-style .ps__rail-x:hover,
|
||||||
|
.light-style .ps__rail-y:hover,
|
||||||
|
.light-style .ps__rail-x:focus,
|
||||||
|
.light-style .ps__rail-y:focus,
|
||||||
|
.light-style .ps__rail-x.ps--clicking,
|
||||||
|
.light-style .ps__rail-y.ps--clicking {
|
||||||
|
background-color: rgba(67, 89, 113, 0.2);
|
||||||
|
}
|
||||||
|
.light-style .ps__rail-x:hover > .ps__thumb-x,
|
||||||
|
.light-style .ps__rail-y:hover > .ps__thumb-y,
|
||||||
|
.light-style .ps__rail-x:focus > .ps__thumb-x,
|
||||||
|
.light-style .ps__rail-y:focus > .ps__thumb-y,
|
||||||
|
.light-style .ps__rail-x.ps--clicking > .ps__thumb-x,
|
||||||
|
.light-style .ps__rail-y.ps--clicking > .ps__thumb-y {
|
||||||
|
background-color: rgba(67, 89, 113, 0.7);
|
||||||
|
}
|
||||||
|
.light-style .ps-inverted .ps__rail-x:hover,
|
||||||
|
.light-style .ps-inverted .ps__rail-y:hover,
|
||||||
|
.light-style .ps-inverted .ps__rail-x:focus,
|
||||||
|
.light-style .ps-inverted .ps__rail-y:focus,
|
||||||
|
.light-style .ps-inverted .ps__rail-x.ps--clicking,
|
||||||
|
.light-style .ps-inverted .ps__rail-y.ps--clicking {
|
||||||
|
background-color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
.light-style .ps-inverted .ps__thumb-x,
|
||||||
|
.light-style .ps-inverted .ps__thumb-y {
|
||||||
|
background-color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
.light-style .ps-inverted .ps__rail-x:hover > .ps__thumb-x,
|
||||||
|
.light-style .ps-inverted .ps__rail-y:hover > .ps__thumb-y,
|
||||||
|
.light-style .ps-inverted .ps__rail-x:focus > .ps__thumb-x,
|
||||||
|
.light-style .ps-inverted .ps__rail-y:focus > .ps__thumb-y,
|
||||||
|
.light-style .ps-inverted .ps__rail-x.ps--clicking > .ps__thumb-x,
|
||||||
|
.light-style .ps-inverted .ps__rail-y.ps--clicking > .ps__thumb-y {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (-moz-appearance: none) {
|
||||||
|
#both-scrollbars-example {
|
||||||
|
max-width: 1080px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
112
files/perfect-scrollbar.js
Normal file
112
files/perfect-scrollbar.js
Normal file
File diff suppressed because one or more lines are too long
112
files/popper.js
Normal file
112
files/popper.js
Normal file
File diff suppressed because one or more lines are too long
806
files/theme-default.css
Normal file
806
files/theme-default.css
Normal file
File diff suppressed because one or more lines are too long
1212
files/ua-parser.js
Normal file
1212
files/ua-parser.js
Normal file
File diff suppressed because it is too large
Load Diff
238
login.html
Normal file
238
login.html
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html
|
||||||
|
lang="en-US"
|
||||||
|
class="light-style customizer-hide"
|
||||||
|
>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<title>Login - amiPro sample site </title>
|
||||||
|
|
||||||
|
<meta name="description" content="" />
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" type="image/x-icon" href="files/favicon.ico" />
|
||||||
|
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Icons. Uncomment required icon fonts -->
|
||||||
|
<!-- link rel="stylesheet" href="../assets/vendor/fonts/boxicons.css" / -->
|
||||||
|
|
||||||
|
<!-- Core CSS -->
|
||||||
|
<link rel="stylesheet" href="files/core.css" class="template-customizer-core-css" />
|
||||||
|
<link rel="stylesheet" href="files/theme-default.css" class="template-customizer-theme-css" />
|
||||||
|
<link rel="stylesheet" href="files/demo.css" />
|
||||||
|
|
||||||
|
<!-- Vendors CSS -->
|
||||||
|
<link rel="stylesheet" href="files/perfect-scrollbar.css" />
|
||||||
|
|
||||||
|
<!-- Page CSS -->
|
||||||
|
<!-- Page -->
|
||||||
|
<link rel="stylesheet" href="files/page-auth.css" />
|
||||||
|
<!-- Helpers -->
|
||||||
|
<script src="files/helpers.js"></script>
|
||||||
|
|
||||||
|
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
|
||||||
|
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
|
||||||
|
<script src="files/config.js"></script>
|
||||||
|
|
||||||
|
<script src="files/jquery.js"></script>
|
||||||
|
<script src="files/popper.js"></script>
|
||||||
|
<script src="files/bootstrap.js"></script>
|
||||||
|
<script src="files/perfect-scrollbar.js"></script>
|
||||||
|
<script src="files/menu.js"></script>
|
||||||
|
<script src="files/main.js"></script>
|
||||||
|
|
||||||
|
<script src="files/amipro_utils.js?v=20230414"></script>
|
||||||
|
<script src="files/dfido2-lib.js?v=2025092701"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
<!--
|
||||||
|
// For stand alone: 'https://local.dqj-macpro.com'
|
||||||
|
// For proxy: set 'https://mac-air-m2.dqj-home.com'
|
||||||
|
-->
|
||||||
|
setFidoServerURL('https://mac-air-m2.dqj-home.com');//'https://fido2.amipro.me');
|
||||||
|
|
||||||
|
const i18n_messages = new Map();
|
||||||
|
|
||||||
|
var lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Welcome to amiPro sample site!");
|
||||||
|
lang_map.set("zh-CN", "欢迎来到 amiPro 示例网站!");
|
||||||
|
lang_map.set("ja", "amiPro サンプルサイトへようこそ!");
|
||||||
|
i18n_messages.set("msg_welcome", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Please sign in to your account and register your devices.");
|
||||||
|
lang_map.set("zh-CN", "请登录您的帐户并注册设备。");
|
||||||
|
lang_map.set("ja", "アカウントにサインインして、デバイスを登録してください。");
|
||||||
|
i18n_messages.set("msg_intro", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "USER ID");
|
||||||
|
lang_map.set("zh-CN", "用户ID");
|
||||||
|
lang_map.set("ja", "ユーザーID");
|
||||||
|
i18n_messages.set("msg_uid", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "PASSWORD");
|
||||||
|
lang_map.set("zh-CN", "密码");
|
||||||
|
lang_map.set("ja", "パスワード");
|
||||||
|
i18n_messages.set("msg_pw", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Any password can sign in");
|
||||||
|
lang_map.set("zh-CN", "任意密码可登录");
|
||||||
|
lang_map.set("ja", "任意のパスワードでログインできます");
|
||||||
|
i18n_messages.set("msg_pw_intro", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Sign in");
|
||||||
|
lang_map.set("zh-CN", "登录");
|
||||||
|
lang_map.set("ja", "ログイン");
|
||||||
|
i18n_messages.set("msg_sign_in", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Input User ID, please!");
|
||||||
|
lang_map.set("zh-CN", "请输入用户ID!");
|
||||||
|
lang_map.set("ja", "ユーザーIDを入力してください!");
|
||||||
|
i18n_messages.set("msg_uid_input", lang_map);
|
||||||
|
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "Passwordless login");
|
||||||
|
lang_map.set("zh-CN", "无密码登录");
|
||||||
|
lang_map.set("ja", "パスワードレス ログイン");
|
||||||
|
i18n_messages.set("title_fido2_login", lang_map);
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
logoutFido2UserSession();
|
||||||
|
|
||||||
|
if(canTryAutoAuthentication()){
|
||||||
|
lang_map = new Map();
|
||||||
|
lang_map.set("en-US", "If automatic login fails, enter your user ID and click Passwordless login.");
|
||||||
|
lang_map.set("zh-CN", "如果自动登录失败,请输入用户ID并点击“无密码登录”。");
|
||||||
|
lang_map.set("ja", "自動ログインに失敗した場合は、ユーザーIDを入力して「パスワードレス ログイン」をクリックしてください。");
|
||||||
|
i18n_messages.set("msg_intro", lang_map);
|
||||||
|
setI18NText(i18n_messages)
|
||||||
|
authenticate()
|
||||||
|
}else{
|
||||||
|
setI18NText(i18n_messages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function authenticate(){
|
||||||
|
var uid = $('#uid').val()
|
||||||
|
if(uid && 0==uid.length)uid=null
|
||||||
|
|
||||||
|
const result = await authenticateFido2(uid)
|
||||||
|
|
||||||
|
if(result.status === 'ok'){
|
||||||
|
//Set your logged in status and jump to user's top page.
|
||||||
|
location.href = 'devices.html?uid='+result.username
|
||||||
|
}else{
|
||||||
|
errProcessFido2(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkInput(){
|
||||||
|
const uid = $('#uid').val()
|
||||||
|
if(!uid || 0>=uid.length){
|
||||||
|
alert($('#msg_uid_input').html())
|
||||||
|
return false;
|
||||||
|
}else return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Content -->
|
||||||
|
|
||||||
|
<div class="container-xxl">
|
||||||
|
<div class="authentication-wrapper authentication-basic container-p-y">
|
||||||
|
<div class="authentication-inner">
|
||||||
|
<!-- Register -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="app-brand justify-content-center">
|
||||||
|
<a href="https://www.amiPro.me/" class="app-brand-link gap-2">
|
||||||
|
<img style="width:160px;" src="files/favicon.ico"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<!-- /Logo -->
|
||||||
|
<h4 class="mb-2" id="msg_welcome">Welcome to amiPro sample site!</h4>
|
||||||
|
<p class="mb-4" id="msg_intro">Please sign-in to your account and start the adventure</p>
|
||||||
|
|
||||||
|
<div id="msg_uid_input" style="display:none;">Input User ID, please!</div>
|
||||||
|
|
||||||
|
<form class="mb-3" action="devices.html" method="GET" onsubmit="return checkInput();">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label" id="msg_uid" name="uid">User ID</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="uid"
|
||||||
|
name="uid"
|
||||||
|
placeholder="Enter your User ID"
|
||||||
|
autofocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 form-password-toggle">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<label class="form-label" for="password" id="msg_pw">Password</label>
|
||||||
|
<small id="msg_pw_intro">Any password can login</small>
|
||||||
|
</div>
|
||||||
|
<div class="input-group input-group-merge">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
class="form-control"
|
||||||
|
name="password"
|
||||||
|
placeholder="············"
|
||||||
|
aria-describedby="password"
|
||||||
|
/>
|
||||||
|
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<button class="btn btn-primary d-grid w-100" style="background-color: #ce59d9;border-color: #ce59d9;" id="msg_sign_in">Sign in</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p class="text-center" id="fido_btn">
|
||||||
|
<a href="javascript:authenticate(true);">
|
||||||
|
<span id="title_fido2_login">Passwordless login</span>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /Register -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- / Content -->
|
||||||
|
|
||||||
|
<!-- div class="buy-now">
|
||||||
|
<a
|
||||||
|
href="https://themeselection.com/products/sneat-bootstrap-html-admin-template/"
|
||||||
|
target="_blank"
|
||||||
|
class="btn btn-danger btn-buy-now"
|
||||||
|
>Change language</a
|
||||||
|
>
|
||||||
|
</div -->
|
||||||
|
|
||||||
|
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
102
top.html.noused
Normal file
102
top.html.noused
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>A sample site for FIOD2-Note server integration</title>
|
||||||
|
<script src="jquery.js"></script>
|
||||||
|
<script src="files/dfido2-lib.js?v=2023040205"></script>
|
||||||
|
<script>
|
||||||
|
//for debug
|
||||||
|
//alert(getI18NErrorMessage("SvrErr107:No ID field in the body of /attestation/result test", errMsgLanguages.japanese))
|
||||||
|
|
||||||
|
async function register() {
|
||||||
|
var uid = $('#uid').val();
|
||||||
|
if (!uid || 0 == uid.length) {
|
||||||
|
alert('User Idを入力してください(Please input User Id).');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var udis = $('#udis').val();
|
||||||
|
if (!udis || 0 == udis.length) {
|
||||||
|
alert('表示名を入力してください(Please input User display).');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const result = await registerFido2(uid, udis);
|
||||||
|
|
||||||
|
if(result.status === 'ok'){
|
||||||
|
alert('デバイス登録完了。(device registered)');
|
||||||
|
}else{
|
||||||
|
errProcessReg(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function authenticateWithUid() {
|
||||||
|
var uid = $('#uid').val();
|
||||||
|
if (!uid || 0 == uid.length) {
|
||||||
|
alert('User Idを入力してください(Please input User Id.');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await authenticateFido2(uid);
|
||||||
|
|
||||||
|
if(result.status === 'ok'){
|
||||||
|
alert('認証成功。(Authenticated)');
|
||||||
|
}else{
|
||||||
|
errProcessAuth(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function authenticate() {
|
||||||
|
const result = await authenticateFido2();
|
||||||
|
|
||||||
|
if(result.status === 'ok'){
|
||||||
|
alert('認証成功。(Authenticated)' + (result.username?' User:'+result.username:''));
|
||||||
|
}else{
|
||||||
|
errProcessAuth(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function errProcessAuth(result){
|
||||||
|
if(result.errorMessage){
|
||||||
|
if(result.name && 'NotAllowedError' === result.name){
|
||||||
|
alert('ユーザーがキャンセルしたか、プロセスがタイムアウトしました。\n(The user canceled, or the process timeout.).');
|
||||||
|
}else if(result.errorMessage.startsWith('SvrErr104:')){
|
||||||
|
alert('このデバイスを登録していません、登録してください。\n(You have not registered this device, please register it.).');
|
||||||
|
}else{
|
||||||
|
const msg = getI18NErrorMessage(result.errorMessage, errMsgLanguages.japanese);
|
||||||
|
alert(msg?msg:result.errorMessage);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
alert('システムエラー(System error).');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function errProcessReg(result){
|
||||||
|
if(result.errCode){
|
||||||
|
switch (result.errCode){
|
||||||
|
case fido2LibErrCodes.user_canceled:
|
||||||
|
alert('ユーザーがキャンセルしました。(the user canceled)');
|
||||||
|
break;
|
||||||
|
case fido2LibErrCodes.timeout:
|
||||||
|
alert('プロセスがタイムアウトしました。(the process timeout)');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
alert(result.errorMessage?result.errorMessage:'システムエラー(System error).');
|
||||||
|
}
|
||||||
|
}else if(result.errorMessage){
|
||||||
|
const msg = getI18NErrorMessage(result.errorMessage, fido2LibErrMsgLanguages.japanese);
|
||||||
|
alert(msg?msg:result.errorMessage);
|
||||||
|
}else{
|
||||||
|
alert('システムエラー(System error).');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<br><br>
|
||||||
|
<h4>User Id: <input id='uid' name='uid'></h4>
|
||||||
|
<h4>表示名(User display): <input id='udis' name='udis'></h4>
|
||||||
|
<button onclick="register();" style="font-size:large">FIDO2デバイス登録(register FIDO2)</button><br><br>
|
||||||
|
|
||||||
|
<button onclick="authenticate();" style="font-size:large">UserId自動発見認証(authenticate discovery)</button><br><br>
|
||||||
|
<button onclick="authenticateWithUid();" style="font-size:large">UserId指定認証(authenticate)</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user