Compare commits

5 Commits

Author SHA1 Message Date
dqj
7248372cd8 Passkey err msg i18n 2026-02-05 10:22:07 +09:00
dqj
1d2f4dfc9b Improve UISDK css for mobile 2026-02-04 23:52:30 +09:00
dqj
03ce446b8a Updated JS in main branch 2026-02-04 13:09:22 +09:00
dqj
89ea8a48fc Merge pull request 'Improved UI SDk and modal demo' (#1) from feature/improve_UI_SDK into main
Reviewed-on: #1
2026-01-18 21:48:54 +09:00
dqj
055d59f979 Improved UI SDk and modal demo 2026-01-18 21:48:19 +09:00
8 changed files with 1409 additions and 394 deletions

View File

@@ -46,7 +46,6 @@
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. --> <!--? 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/config.js"></script>
<script src="files/jquery.js"></script>
<script src="files/popper.js"></script> <script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script> <script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script> <script src="files/perfect-scrollbar.js"></script>
@@ -169,27 +168,25 @@
window.location.href = "login.html"; window.location.href = "login.html";
} }
} }
$('#user_id').html(user_id); document.getElementById('user_id').textContent = user_id;
listDevices(); listDevices();
} }
async function setSessionStatus(){ async function setSessionStatus(){
const sessionOk = await validSession(); const sessionOk = await validSession();
const sessionStatusEl = document.getElementById('session_status');
if(sessionOk){ if(sessionOk){
$('#session_status').html(getI18NText(i18n_messages, 'msg_session_status_ok')) sessionStatusEl.textContent = getI18NText(i18n_messages, 'msg_session_status_ok');
}else{ }else{
$('#session_status').html(getI18NText(i18n_messages, 'msg_session_status_fail')) sessionStatusEl.textContent = getI18NText(i18n_messages, 'msg_session_status_fail');
} }
} }
async function addDevice(display_name){ 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); const result = await registerFido2(user_id, display_name?display_name:'dis_'+user_id);
if(result.status === 'ok'){ 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(document.getElementById('msg_register_ok').textContent);
alert($('#msg_register_ok').html());
listDevices(); listDevices();
}else{ }else{
errProcessFido2(result) errProcessFido2(result)
@@ -213,17 +210,17 @@
async function listDevices(){ async function listDevices(){
const result = await listUserDevicesFido2() const result = await listUserDevicesFido2()
$("#devices_list").html(""); const devicesListEl = document.getElementById('devices_list');
devicesListEl.innerHTML = "";
if("ok" === result.status){ if("ok" === result.status){
let lst_html = ""
for(let dev of result.devices){ for(let dev of result.devices){
var dev_desc = dev.desc var dev_desc = dev.desc
if(!dev_desc || 0 === dev_desc.length){ if(!dev_desc || 0 === dev_desc.length){
let parser = new UAParser(dev.userAgent); const parser = new UAParser(dev.userAgent);
const osName = parser.getOS().name;
if(parser.getOS().name){ if(osName){
dev_desc = parser.getDevice().model + ',' + parser.getOS().name + ',' + parser.getBrowser().name; dev_desc = parser.getDevice().model + ',' + osName + ',' + parser.getBrowser().name;
}else{ }else{
dev_desc = dev.userAgent; dev_desc = dev.userAgent;
} }
@@ -231,14 +228,35 @@
var date = new Date(dev.registered_time); var date = new Date(dev.registered_time);
lst_html += '<tr><td><strong>'+ dev_desc +'</strong></td><td>'+ const row = document.createElement('tr');
date.toLocaleString()+'</td><td>'+
"<a style=\"padding-left: 0px;\" class=\"dropdown-item\" href=\"javascript:delDevice('"+dev.device_id+"');\">"+ const tdDevice = document.createElement('td');
'<i class="bx bx-trash me-1"></i><span id="title_del"> '+ getI18NText(i18n_messages, 'title_del') +'</span></a></div></td></tr>' const strong = document.createElement('strong');
strong.textContent = dev_desc;
tdDevice.appendChild(strong);
const tdTime = document.createElement('td');
tdTime.textContent = date.toLocaleString();
const tdAction = document.createElement('td');
const link = document.createElement('a');
link.className = 'dropdown-item';
link.href = "javascript:delDevice('" + dev.device_id + "');";
const icon = document.createElement('i');
icon.className = 'bx bx-trash me-1';
const span = document.createElement('span');
span.textContent = ' ' + getI18NText(i18n_messages, 'title_del');
link.appendChild(icon);
link.appendChild(span);
tdAction.appendChild(link);
row.appendChild(tdDevice);
row.appendChild(tdTime);
row.appendChild(tdAction);
devicesListEl.appendChild(row);
} }
$("#devices_list").html(lst_html);
}else{ }else{
const msg = getI18NErrorMessage(result.errorMessage); //fido2LibErrMsgLanguages.japanese const msg = getI18NErrorMessage(result.errorMessage);
alert(msg?msg:result.errorMessage); alert(msg?msg:result.errorMessage);
} }
} }

View File

@@ -1,12 +1,12 @@
/** /**
* amiPro utils * amiPro utils - jQuery-free version
*/ */
'use strict'; 'use strict';
function setI18NText(i18n_map){ function setI18NText(i18n_map){
for (const key of i18n_map.keys()) { for (const key of i18n_map.keys()) {
const elm = $("#"+key); const elm = document.getElementById(key);
if(elm){ if(elm){
const lang = window.navigator.language; const lang = window.navigator.language;
var elem = i18n_map.get(key) var elem = i18n_map.get(key)
@@ -17,7 +17,7 @@ function setI18NText(i18n_map){
} }
if(!msg)msg = key+"-"+lang if(!msg)msg = key+"-"+lang
$("#"+key).html(msg); elm.textContent = msg;
} }
} }
} }

View File

@@ -1,7 +1,18 @@
/**
*
* @file dfido2-lib.js
* @description FIDO2 library of amipro FIDO2 Server
* @version 2025-12-12
* @author Amipro Co., Ltd. (https://www.amipro.me/)
* @license Copyright (c) Amipro Co., Ltd. All rights reserved.
*/
const DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION = 'fido2_user_session' const DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION = 'fido2_user_session'
const DFIDO2_LIB_LOCALSTG_NAME_REGISTERED = 'dfido2_lib_registered' const DFIDO2_LIB_LOCALSTG_NAME_REGISTERED = 'dfido2_lib_registered'
const DFIDO2_LIB_LOCALSTG_NAME_SVR_URL = 'dfido2_lib_svr_url' const DFIDO2_LIB_LOCALSTG_NAME_SVR_URL = 'dfido2_lib_svr_url'
let configuredServerUrl = null;
/** ===APIs=== */ /** ===APIs=== */
if(!localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)){ if(!localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)){
@@ -9,9 +20,25 @@ if(!localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL)){
} }
function setFidoServerURL(url){ function setFidoServerURL(url){
if (!url || !url.startsWith('https://')) {
throw new Error('serverUrl must be a valid HTTPS URL');
}
configuredServerUrl = url;
localStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL, url); localStorage.setItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL, url);
} }
function getServerUrl() {
if (configuredServerUrl) {
return configuredServerUrl;
}
const stored = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL);
if (stored) {
configuredServerUrl = stored;
return stored;
}
return 'https://fido2.amipro.me';
}
function canTryAutoAuthentication(){ function canTryAutoAuthentication(){
//const session_text = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION) //const session_text = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_USER_SESSION)
//alert('canTryAuth:'+session_text+"|"+(null != localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED))) //alert('canTryAuth:'+session_text+"|"+(null != localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_REGISTERED)))
@@ -67,7 +94,7 @@ async function listUserDevicesFido2(rpId = null) {
req.rp = { id: rpId }; req.rp = { id: rpId };
} }
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/dvc/lst", { const response = await fetch(getServerUrl() + "/usr/dvc/lst", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -104,7 +131,7 @@ async function delUserDeviceFido2(device_id, rpId = null) {
req.rp = { id: rpId }; req.rp = { id: rpId };
} }
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/dvc/rm", { const response = await fetch(getServerUrl() + "/usr/dvc/rm", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -154,7 +181,7 @@ async function validSession(rpId = null) {
req.rp = { id: rpId }; req.rp = { id: rpId };
} }
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/validsession", { const response = await fetch(getServerUrl() + "/usr/validsession", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -177,7 +204,7 @@ async function logoutFido2UserSession(){
const session_data = JSON.parse(session_text) const session_data = JSON.parse(session_text)
let req = {session: session_data['session'], username: session_data['uid']} let req = {session: session_data['session'], username: session_data['uid']}
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/usr/delsession", { const response = await fetch(getServerUrl() + "/usr/delsession", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -192,7 +219,7 @@ async function logoutFido2UserSession(){
async function getRegistrationUser(reg_session_id){ async function getRegistrationUser(reg_session_id){
try { try {
let req = {session_id: reg_session_id} let req = {session_id: reg_session_id}
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/reg/username", { const response = await fetch(getServerUrl() + "/reg/username", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -223,16 +250,64 @@ function errMessageFido2(result){
case fido2LibErrCodes.timeout: case fido2LibErrCodes.timeout:
rtn=getI18NErrorMessage('Fido2LibErr103:'); rtn=getI18NErrorMessage('Fido2LibErr103:');
break; break;
case fido2LibErrCodes.invalid_state:
rtn=getI18NErrorMessage('Fido2LibErr105:');
break;
case fido2LibErrCodes.not_allowed:
rtn=getI18NErrorMessage('Fido2LibErr107:');
break;
case fido2LibErrCodes.abort:
rtn=getI18NErrorMessage('Fido2LibErr108:');
break;
case fido2LibErrCodes.not_supported:
rtn=getI18NErrorMessage('Fido2LibErr109:');
break;
case fido2LibErrCodes.security:
rtn=getI18NErrorMessage('Fido2LibErr110:');
break;
case fido2LibErrCodes.network:
rtn=getI18NErrorMessage('Fido2LibErr111:');
break;
case fido2LibErrCodes.constraint:
rtn=getI18NErrorMessage('Fido2LibErr112:');
break;
case fido2LibErrCodes.not_readable:
rtn=getI18NErrorMessage('Fido2LibErr113:');
break;
case fido2LibErrCodes.encoding:
rtn=getI18NErrorMessage('Fido2LibErr114:');
break;
case fido2LibErrCodes.data_error:
rtn=getI18NErrorMessage('Fido2LibErr115:');
break;
default: default:
rtn=result.errorMessage?result.errorMessage:getI18NErrorMessage('Fido2LibErr104:'); rtn=result.errorMessage?result.errorMessage:getI18NErrorMessage('Fido2LibErr104:');
} }
}else if(result.name && "InvalidStateError" === result.name){ }else if(result.name && "InvalidStateError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr105:'); rtn=getI18NErrorMessage('Fido2LibErr105:');
}else if(result.name && "NotAllowedError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr107:');
}else if(result.name && "AbortError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr108:');
}else if(result.name && "NotSupportedError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr109:');
}else if(result.name && "SecurityError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr110:');
}else if(result.name && "NetworkError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr111:');
}else if(result.name && "ConstraintError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr112:');
}else if(result.name && "NotReadableError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr113:');
}else if(result.name && "EncodingError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr114:');
}else if(result.name && "DataError" === result.name){
rtn=getI18NErrorMessage('Fido2LibErr115:');
}else if(result.errorMessage){ }else if(result.errorMessage){
const msg = getI18NErrorMessage(result.errorMessage); const msg = getI18NErrorMessage(result.errorMessage);
rtn=msg?msg:result.errorMessage; rtn=msg?msg:result.errorMessage;
}else{ }else{
rtn=getI18NErrorMessage(i18n_messages, 'Fido2LibErr104:'); rtn=getI18NErrorMessage('Fido2LibErr104:');
} }
return rtn; return rtn;
@@ -241,7 +316,17 @@ function errMessageFido2(result){
const fido2LibErrCodes = { const fido2LibErrCodes = {
user_canceled : -101, user_canceled : -101,
timeout : -102, timeout : -102,
unknown : -999 unknown : -999,
invalid_state : -103,
not_allowed : -104,
abort : -105,
not_supported : -106,
security : -107,
network : -108,
constraint : -109,
not_readable : -110,
encoding : -111,
data_error : -112
} }
const errMsgs = new Map(); const errMsgs = new Map();
@@ -271,11 +356,23 @@ errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr118:', 'Signature is not
errMsgs.get(fido2LibErrMsgLanguages.english).set('SvrErr119:', 'No user session!'); 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('SvrErr120:', 'User has reached the device limit!');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr101:', 'Your browser does not support FIDO2.'); errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr101:', 'Your browser does not support FIDO2/WebAuthn.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr102:', 'The user canceled.'); errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr102:', 'The operation was canceled by the user.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr103:', 'The process timeout.'); errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr103:', 'The operation timed out.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr104:', 'System error.'); errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr104:', 'A system error occurred.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr105:', 'The same authenticator cannot be registered again.'); errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr105:', 'The authenticator contains credentials that are already registered with this website.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr106:', 'Another request is already in progress.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr107:', 'The operation was not allowed.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr108:', 'The operation was aborted.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr109:', 'This operation is not supported on your device.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr110:', 'A security error occurred.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr111:', 'A network error occurred.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr112:', 'The operation failed due to a constraint violation.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr113:', 'Could not read the credential.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr114:', 'The data format is invalid.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr115:', 'A data error occurred.');
errMsgs.get(fido2LibErrMsgLanguages.english).set('Fido2LibErr106:', 'Another request is already in progress.');
errMsgs.set(fido2LibErrMsgLanguages.japanese, new Map()); errMsgs.set(fido2LibErrMsgLanguages.japanese, new Map());
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr101:', '登録されていないエンタープライズ認証デバイス aaguid!'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr101:', '登録されていないエンタープライズ認証デバイス aaguid!');
@@ -297,11 +394,23 @@ errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr118:', '署名は base6
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr119:', 'ユーザーセッションがありません!'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr119:', 'ユーザーセッションがありません!');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr120:', 'ユーザーはデバイスの制限数に達しました!'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('SvrErr120:', 'ユーザーはデバイスの制限数に達しました!');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr101:', 'お使いのブラウザは FIDO2 をサポートしていません。'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr101:', 'お使いのブラウザは FIDO2/WebAuthn をサポートしていません。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr102:', 'ユーザーがキャンセルました。'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr102:', 'ユーザーによって操作がキャンセルされました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr103:', 'プロセスがタイムアウトしました。'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr103:', '操作がタイムアウトしました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr104:', 'システムエラー。'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr104:', 'システムエラーが発生しました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr105:', '同じ認証デバイスを再登録することはできません。'); errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr105:', 'この認証デバイスには、すでにこの网站に登録されている憑拠が含まれています。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr106:', '別のリクエストがすでに進行中です。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr107:', '操作が許可されませんでした。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr108:', '操作が中止されました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr109:', 'この機器ではこの操作はサポートされていません。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr110:', 'セキュリティエラーが発生しました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr111:', 'ネットワークエラーが発生しました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr112:', '制約違反のため操作に失敗しました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr113:', '憑拠を読み取れませんでした。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr114:', 'データ形式が無効です。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr115:', 'データエラーが発生しました。');
errMsgs.get(fido2LibErrMsgLanguages.japanese).set('Fido2LibErr106:', '別のリクエストがすでに進行中です。');
errMsgs.set(fido2LibErrMsgLanguages.chinese_cn, new Map()); errMsgs.set(fido2LibErrMsgLanguages.chinese_cn, new Map());
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr101:', '未注册的企业认证器 aaguid!'); errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr101:', '未注册的企业认证器 aaguid!');
@@ -323,11 +432,23 @@ errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr118:', '签名不是
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr119:', '未建立用户会话!'); errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr119:', '未建立用户会话!');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr120:', '用户已达到设备限制数!'); errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('SvrErr120:', '用户已达到设备限制数!');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr101:', '您的浏览器不支持FIDO2.'); errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr106:', '另一个请求正在进行中。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr102:', '用户取消了操作。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr101:', '您的浏览器不支持FIDO2/WebAuthn。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr102:', '操作已被用户取消。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr103:', '操作超时。'); errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr103:', '操作超时。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr104:', '系统错误。'); errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr104:', '发生系统错误。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr105:', '无法再次注册相同的认证器。'); errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr105:', '您的认证器包含已在此网站注册的凭据。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr106:', '另一个请求正在进行中。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr107:', '操作不被允许。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr108:', '操作已中止。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr109:', '您的设备不支持此操作。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr110:', '发生安全错误。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr111:', '发生网络错误。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr112:', '由于约束冲突,操作失败。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr113:', '无法读取凭据。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr114:', '数据格式无效。');
errMsgs.get(fido2LibErrMsgLanguages.chinese_cn).set('Fido2LibErr115:', '发生数据错误。');
/** /**
* *
@@ -410,7 +531,7 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr
attestationOptions.rp = { id: rpId } attestationOptions.rp = { id: rpId }
} }
const svrUrl = localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) const svrUrl = getServerUrl()
const response = await fetch(svrUrl + "/attestation/options", { const response = await fetch(svrUrl + "/attestation/options", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
@@ -445,7 +566,7 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr
attResult.transports = res.response.getTransports(); attResult.transports = res.response.getTransports();
} }
const result = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/attestation/result", { const result = await fetch(getServerUrl() + "/attestation/result", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -471,6 +592,7 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr
} catch (err) { } catch (err) {
var errRtn = {status:'failed', errorMessage: err.message}; var errRtn = {status:'failed', errorMessage: err.message};
if(err.name) errRtn.name = err.name if(err.name) errRtn.name = err.name
if(err.name && 'NotAllowedError' === err.name){ if(err.name && 'NotAllowedError' === err.name){
const nowtm = (new Date()).getTime() const nowtm = (new Date()).getTime()
if(nowtm > process_time_limit){ if(nowtm > process_time_limit){
@@ -478,7 +600,27 @@ async function doAttestation(username, displayName, rpId, userVerification = 'pr
}else{ }else{
errRtn.errCode = fido2LibErrCodes.user_canceled errRtn.errCode = fido2LibErrCodes.user_canceled
} }
}else errRtn.errCode = fido2LibErrCodes.unknown } else if(err.name && 'InvalidStateError' === err.name){
errRtn.errCode = fido2LibErrCodes.invalid_state
} else if(err.name && 'AbortError' === err.name){
errRtn.errCode = fido2LibErrCodes.abort
} else if(err.name && 'NotSupportedError' === err.name){
errRtn.errCode = fido2LibErrCodes.not_supported
} else if(err.name && 'SecurityError' === err.name){
errRtn.errCode = fido2LibErrCodes.security
} else if(err.name && 'NetworkError' === err.name){
errRtn.errCode = fido2LibErrCodes.network
} else if(err.name && 'ConstraintError' === err.name){
errRtn.errCode = fido2LibErrCodes.constraint
} else if(err.name && 'NotReadableError' === err.name){
errRtn.errCode = fido2LibErrCodes.not_readable
} else if(err.name && 'EncodingError' === err.name){
errRtn.errCode = fido2LibErrCodes.encoding
} else if(err.name && 'DataError' === err.name){
errRtn.errCode = fido2LibErrCodes.data_error
} else {
errRtn.errCode = fido2LibErrCodes.unknown
}
return errRtn; return errRtn;
} finally { } finally {
@@ -524,7 +666,7 @@ async function doAssertion(username = null, rpId = null, userVerification = 'pre
authnOptions.rp = { id: rpId }; authnOptions.rp = { id: rpId };
} }
const response = await fetch(localStorage.getItem(DFIDO2_LIB_LOCALSTG_NAME_SVR_URL) + "/assertion/options", { const response = await fetch(getServerUrl() + "/assertion/options", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -566,7 +708,7 @@ async function doAssertion(username = null, rpId = null, userVerification = 'pre
userHandle: _toBase64URL(btoa(_bufferToString(cred.response.userHandle))) //_toBase64URL(btoa(_bufferToString(cred.response.userHandle))) 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", { const res = await fetch(getServerUrl() + "/assertion/result", {
method: "POST", method: "POST",
cache: "no-cache", cache: "no-cache",
headers: { headers: {
@@ -589,6 +731,7 @@ async function doAssertion(username = null, rpId = null, userVerification = 'pre
} catch (err) { } catch (err) {
var errRtn = {status:'failed', errorMessage: err.message}; var errRtn = {status:'failed', errorMessage: err.message};
if(err.name) errRtn.name = err.name if(err.name) errRtn.name = err.name
if(err.name && 'NotAllowedError' === err.name){ if(err.name && 'NotAllowedError' === err.name){
const nowtm = (new Date()).getTime() const nowtm = (new Date()).getTime()
if(nowtm > process_time_limit){ if(nowtm > process_time_limit){
@@ -596,7 +739,27 @@ async function doAssertion(username = null, rpId = null, userVerification = 'pre
}else{ }else{
errRtn.errCode = fido2LibErrCodes.user_canceled errRtn.errCode = fido2LibErrCodes.user_canceled
} }
}else errRtn.errCode = fido2LibErrCodes.unknown } else if(err.name && 'InvalidStateError' === err.name){
errRtn.errCode = fido2LibErrCodes.invalid_state
} else if(err.name && 'AbortError' === err.name){
errRtn.errCode = fido2LibErrCodes.abort
} else if(err.name && 'NotSupportedError' === err.name){
errRtn.errCode = fido2LibErrCodes.not_supported
} else if(err.name && 'SecurityError' === err.name){
errRtn.errCode = fido2LibErrCodes.security
} else if(err.name && 'NetworkError' === err.name){
errRtn.errCode = fido2LibErrCodes.network
} else if(err.name && 'ConstraintError' === err.name){
errRtn.errCode = fido2LibErrCodes.constraint
} else if(err.name && 'NotReadableError' === err.name){
errRtn.errCode = fido2LibErrCodes.not_readable
} else if(err.name && 'EncodingError' === err.name){
errRtn.errCode = fido2LibErrCodes.encoding
} else if(err.name && 'DataError' === err.name){
errRtn.errCode = fido2LibErrCodes.data_error
} else {
errRtn.errCode = fido2LibErrCodes.unknown
}
return errRtn; return errRtn;
} finally { } finally {

View File

@@ -16,6 +16,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
flex-wrap: wrap;
} }
.fido2-sdk-logo { .fido2-sdk-logo {
@@ -86,33 +87,9 @@
border-radius: 6px; border-radius: 6px;
background-color: #e7f1ff; background-color: #e7f1ff;
border-left: 4px solid #0d6efd; border-left: 4px solid #0d6efd;
} }
.fido2-sdk-standalone { .fido2-sdk-table .text-danger {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.fido2-sdk-standalone .container {
max-width: 1000px;
}
.fido2-sdk-standalone .card {
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
border: none;
}
.fido2-sdk-standalone .card-header {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-bottom: 2px solid #dee2e6;
}
.fido2-sdk-standalone .card-body {
background-color: #ffffff;
}
.fido2-sdk-table .text-danger {
color: #dc3545 !important; color: #dc3545 !important;
text-decoration: none; text-decoration: none;
transition: all 0.2s ease; transition: all 0.2s ease;
@@ -134,15 +111,11 @@
@media (max-width: 768px) { @media (max-width: 768px) {
.fido2-sdk-modal .modal-dialog { .fido2-sdk-modal .modal-dialog {
margin: 10px; margin: 10px;
max-width: calc(100% - 20px); max-width: calc(100% - 20px);
} }
.fido2-sdk-standalone { .fido2-sdk-table th,
padding: 20px 10px;
}
.fido2-sdk-table th,
.fido2-sdk-table td { .fido2-sdk-table td {
padding: 8px 12px; padding: 8px 12px;
font-size: 14px; font-size: 14px;
@@ -156,6 +129,25 @@
.fido2-sdk-logo { .fido2-sdk-logo {
max-height: 30px; max-height: 30px;
} }
.fido2-sdk-status-badge {
width: 100%;
order: 3;
margin-top: 8px;
}
.fido2-sdk-header .btn-close {
order: 2;
margin-left: auto;
}
.fido2-sdk-header .modal-title {
order: 1;
}
.fido2-sdk-header .fido2-sdk-logo {
order: 0;
}
} }
.fido2-sdk-loading { .fido2-sdk-loading {

File diff suppressed because it is too large Load Diff

View File

@@ -47,7 +47,6 @@
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. --> <!--? 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/config.js"></script>
<script src="files/jquery.js"></script>
<script src="files/popper.js"></script> <script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script> <script src="files/bootstrap.js"></script>
<script src="files/perfect-scrollbar.js"></script> <script src="files/perfect-scrollbar.js"></script>
@@ -167,13 +166,12 @@
} }
async function authenticate(){ async function authenticate(){
var uid = $('#uid').val() var uid = document.getElementById('uid').value
if(uid && 0==uid.length)uid=null if(uid && 0==uid.length)uid=null
const result = await authenticateFido2(uid) const result = await authenticateFido2(uid)
if(result.status === 'ok'){ if(result.status === 'ok'){
//Set your logged in status and jump to user's top page.
location.href = 'devices.html?uid='+result.username location.href = 'devices.html?uid='+result.username
}else{ }else{
errProcessFido2(result) errProcessFido2(result)
@@ -181,9 +179,9 @@
} }
function checkInput(){ function checkInput(){
const uid = $('#uid').val() const uid = document.getElementById('uid').value
if(!uid || 0>=uid.length){ if(!uid || 0>=uid.length){
alert($('#msg_uid_input').html()) alert(document.getElementById('msg_uid_input').textContent)
return false; return false;
}else return true; }else return true;
} }

View File

@@ -177,9 +177,8 @@
.log-type-error { .log-type-error {
color: #f48771; color: #f48771;
} }
</style> </style>
<script src="files/jquery.js"></script>
<script src="files/popper.js"></script> <script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script> <script src="files/bootstrap.js"></script>
<script src="files/dfido2-lib.js"></script> <script src="files/dfido2-lib.js"></script>
@@ -366,6 +365,36 @@
lang_map.set("ja", "テーマの色とスタイルのカスタマイズ"); lang_map.set("ja", "テーマの色とスタイルのカスタマイズ");
i18n_messages.set("msg_feature_7", lang_map); i18n_messages.set("msg_feature_7", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Login Default");
lang_map.set("zh-CN", "登录缺省");
lang_map.set("ja", "ログイン デフォルト");
i18n_messages.set("msg_code_login_default", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Login Custom");
lang_map.set("zh-CN", "登录定制");
lang_map.set("ja", "ログイン カスタム");
i18n_messages.set("msg_code_login_custom", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Device Manager Default");
lang_map.set("zh-CN", "设备管理缺省");
lang_map.set("ja", "デバイス管理 デフォルト");
i18n_messages.set("msg_code_device_default", lang_map);
lang_map = new Map();
lang_map.set("en-US", "Device Manager Custom");
lang_map.set("zh-CN", "设备管理定制");
lang_map.set("ja", "デバイス管理 カスタム");
i18n_messages.set("msg_code_device_custom", lang_map);
lang_map = new Map();
lang_map.set("en-US", "JS Import Examples:");
lang_map.set("zh-CN", "JS 引入示例:");
lang_map.set("ja", "JS インポート例:");
i18n_messages.set("msg_js_import_examples", lang_map);
lang_map = new Map(); lang_map = new Map();
lang_map.set("en-US", "Multi-language support (English/Japanese/Chinese)"); lang_map.set("en-US", "Multi-language support (English/Japanese/Chinese)");
lang_map.set("zh-CN", "多语言支持(英/日/中)"); lang_map.set("zh-CN", "多语言支持(英/日/中)");
@@ -510,41 +539,107 @@
<div class="demo-section"> <div class="demo-section">
<h2 id="msg_section_code_title">💻 Code Examples</h2> <h2 id="msg_section_code_title">💻 Code Examples</h2>
<p id="msg_code_simple">Simplest integration:</p>
<p id="msg_js_import_examples">JS Import Examples:</p>
<div class="code-block"> <div class="code-block">
<code>Fido2UIManager.renderLogin({ <code>&lt;script src="files/popper.js"&gt;&lt;/script&gt;<br>
container: '#login-container', &lt;script src="files/bootstrap.js"&gt;&lt;/script&gt;<br>
mode: 'modal', &lt;script src="files/dfido2-lib.js"&gt;&lt;/script&gt;<br>
serverUrl: SERVER_URL &lt;script src="files/fido2-ui-sdk.js"&gt;&lt;/script&gt;<br>
&lt;script src="files/amipro_utils.js"&gt;&lt;/script&gt;</code>
</div>
<p id="msg_code_login_default">Login Default:</p>
<div class="code-block">
<code>// Login Default<br>
Fido2UIManager.renderLogin({<br>
&nbsp;&nbsp;container: '#login-container',<br>
&nbsp;&nbsp;mode: 'modal',<br>
&nbsp;&nbsp;serverUrl: SERVER_URL,<br>
&nbsp;&nbsp;language: CURRENT_LANG<br>
});</code> });</code>
</div> </div>
<p id="msg_code_theme">With theme customization:</p> <p id="msg_code_login_custom">Login Custom:</p>
<div class="code-block"> <div class="code-block">
<code>Fido2UIManager.renderLogin({ <code>// Login with Custom Theme<br>
container: '#login-container', Fido2UIManager.renderLogin({<br>
mode: 'modal', &nbsp;&nbsp;container: '#login-container',<br>
serverUrl: SERVER_URL, &nbsp;&nbsp;mode: 'modal',<br>
language: 'ja', &nbsp;&nbsp;serverUrl: SERVER_URL,<br>
theme: { &nbsp;&nbsp;language: 'ja',<br>
primaryColor: '#ce59d9', &nbsp;&nbsp;theme: {<br>
backgroundColor: '#faf5ff' &nbsp;&nbsp;&nbsp;&nbsp;logo: 'files/favicon.ico',<br>
}, &nbsp;&nbsp;&nbsp;&nbsp;primaryColor: '#ce59d9',<br>
features: { &nbsp;&nbsp;&nbsp;&nbsp;backgroundColor: '#faf5ff',<br>
enablePasswordLogin: true, &nbsp;&nbsp;&nbsp;&nbsp;textColor: '#6b21a8',<br>
autoShowPassword: true &nbsp;&nbsp;&nbsp;&nbsp;borderRadius: '12px'<br>
} &nbsp;&nbsp;},<br>
&nbsp;&nbsp;features: {<br>
&nbsp;&nbsp;&nbsp;&nbsp;autoAuth: false,<br>
&nbsp;&nbsp;&nbsp;&nbsp;enablePasswordLogin: true,<br>
&nbsp;&nbsp;&nbsp;&nbsp;autoShowPassword: true,<br>
&nbsp;&nbsp;&nbsp;&nbsp;maxPasswordAttempts: 3<br>
&nbsp;&nbsp;},<br>
&nbsp;&nbsp;callbacks: {<br>
&nbsp;&nbsp;&nbsp;&nbsp;onFido2Success: function(username, session) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Handle login success<br>
&nbsp;&nbsp;&nbsp;&nbsp;},<br>
&nbsp;&nbsp;&nbsp;&nbsp;onFido2Error: function(error) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Handle error<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;}<br>
});</code> });</code>
</div> </div>
<p id="msg_code_device">Device Manager:</p> <p id="msg_code_device_default">Device Manager Default:</p>
<div class="code-block"> <div class="code-block">
<code>Fido2UIManager.renderDeviceManager({ <code>// Device Manager Default<br>
userId: currentUserId, Fido2UIManager.renderDeviceManager({<br>
container: '#device-container', &nbsp;&nbsp;userId: currentUserId,<br>
mode: 'modal', &nbsp;&nbsp;container: '#device-container',<br>
serverUrl: SERVER_URL, &nbsp;&nbsp;mode: 'modal',<br>
language: 'ja' &nbsp;&nbsp;serverUrl: SERVER_URL,<br>
&nbsp;&nbsp;language: CURRENT_LANG<br>
});</code>
</div>
<p id="msg_code_device_custom">Device Manager Custom:</p>
<div class="code-block">
<code>// Device Manager with Custom Style<br>
Fido2UIManager.renderDeviceManager({<br>
&nbsp;&nbsp;userId: currentUserId,<br>
&nbsp;&nbsp;container: '#device-container',<br>
&nbsp;&nbsp;mode: 'modal',<br>
&nbsp;&nbsp;serverUrl: SERVER_URL,<br>
&nbsp;&nbsp;language: 'ja',<br>
&nbsp;&nbsp;theme: {<br>
&nbsp;&nbsp;&nbsp;&nbsp;logo: 'files/favicon.ico',<br>
&nbsp;&nbsp;&nbsp;&nbsp;primaryColor: '#ce59d9',<br>
&nbsp;&nbsp;&nbsp;&nbsp;backgroundColor: '#faf5ff',<br>
&nbsp;&nbsp;&nbsp;&nbsp;textColor: '#6b21a8',<br>
&nbsp;&nbsp;&nbsp;&nbsp;borderRadius: '12px'<br>
&nbsp;&nbsp;},<br>
&nbsp;&nbsp;features: {<br>
&nbsp;&nbsp;&nbsp;&nbsp;showAddButton: true,<br>
&nbsp;&nbsp;&nbsp;&nbsp;showDeleteButton: true,<br>
&nbsp;&nbsp;&nbsp;&nbsp;showUserInfo: true,<br>
&nbsp;&nbsp;&nbsp;&nbsp;showSessionStatus: true<br>
&nbsp;&nbsp;},<br>
&nbsp;&nbsp;callbacks: {<br>
&nbsp;&nbsp;&nbsp;&nbsp;onInit: function(manager) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Manager initialized<br>
&nbsp;&nbsp;&nbsp;&nbsp;},<br>
&nbsp;&nbsp;&nbsp;&nbsp;onDeviceAdded: function(device) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Device added<br>
&nbsp;&nbsp;&nbsp;&nbsp;},<br>
&nbsp;&nbsp;&nbsp;&nbsp;onDeviceDeleted: function(deviceId) {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Device deleted<br>
&nbsp;&nbsp;&nbsp;&nbsp;},<br>
&nbsp;&nbsp;&nbsp;&nbsp;onClose: function() {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Manager closed<br>
&nbsp;&nbsp;&nbsp;&nbsp;}<br>
&nbsp;&nbsp;}<br>
});</code> });</code>
</div> </div>
</div> </div>
@@ -585,7 +680,7 @@
return 'en-US'; return 'en-US';
} }
const CURRENT_LANG = getBrowserLanguage();//'https://fido2.amipro.me'; const CURRENT_LANG = getBrowserLanguage();
function enableDeviceManagementButtons() { function enableDeviceManagementButtons() {
document.getElementById('btn-manage-devices').disabled = false; document.getElementById('btn-manage-devices').disabled = false;
@@ -602,6 +697,7 @@
} }
function logout() { function logout() {
Fido2UIManager.logout();
currentUserId = null; currentUserId = null;
isLoggedIn = false; isLoggedIn = false;
log('success', getI18NText(i18n_messages, 'msg_logout_success')); log('success', getI18NText(i18n_messages, 'msg_logout_success'));
@@ -825,5 +921,16 @@
https://amipro.me/fido2_top.html https://amipro.me/fido2_top.html
</a> </a>
</p> </p>
<!--
CSP (Content Security Policy) 示例 - 生产环境部署建议添加:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://your-server.com;">
-->
</body> </body>
</html> </html>

View File

@@ -149,9 +149,8 @@
margin: 0; margin: 0;
color: #333; color: #333;
} }
</style> </style>
<script src="files/jquery.js"></script>
<script src="files/popper.js"></script> <script src="files/popper.js"></script>
<script src="files/bootstrap.js"></script> <script src="files/bootstrap.js"></script>
<script src="files/dfido2-lib.js"></script> <script src="files/dfido2-lib.js"></script>