899 lines
32 KiB
HTML
899 lines
32 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en-US">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>FIDO2 UI SDK - Modal Demo</title>
|
||
|
||
<link rel="stylesheet" href="files/core.css">
|
||
<link rel="stylesheet" href="files/theme-default.css">
|
||
<link rel="stylesheet" href="files/demo.css">
|
||
<link rel="stylesheet" href="files/boxicons.css">
|
||
<link rel="stylesheet" href="files/fido2-ui-sdk.css">
|
||
|
||
<style>
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
margin: 0;
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.demo-header {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 12px;
|
||
padding: 40px;
|
||
margin-bottom: 30px;
|
||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.demo-header h1 {
|
||
color: #333;
|
||
margin: 0 0 10px;
|
||
font-size: 32px;
|
||
}
|
||
|
||
.demo-header p {
|
||
color: #666;
|
||
margin: 0 0 20px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.demo-section {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 12px;
|
||
padding: 30px;
|
||
margin-bottom: 30px;
|
||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.demo-section h2 {
|
||
color: #333;
|
||
margin: 0 0 20px;
|
||
font-size: 24px;
|
||
}
|
||
|
||
.demo-section p {
|
||
color: #666;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.demo-btn {
|
||
padding: 12px 32px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
border: none;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
margin-right: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.demo-btn-primary {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
}
|
||
|
||
.demo-btn-primary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
||
}
|
||
|
||
.demo-btn-secondary {
|
||
background: #f8f9fa;
|
||
color: #333;
|
||
border: 1px solid #dee2e6;
|
||
}
|
||
|
||
.demo-btn-secondary:hover {
|
||
background: #e9ecef;
|
||
}
|
||
|
||
.demo-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
transform: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
.code-block {
|
||
background: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.code-block code {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
color: #333;
|
||
}
|
||
|
||
.feature-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
|
||
.feature-list li {
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid #e9ecef;
|
||
color: #666;
|
||
}
|
||
|
||
.feature-list li:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.feature-list i {
|
||
color: #28a745;
|
||
margin-right: 10px;
|
||
}
|
||
|
||
#log-container {
|
||
background: #1e1e1e;
|
||
color: #d4d4d4;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
max-height: 300px;
|
||
overflow-y: auto;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.log-entry {
|
||
padding: 4px 0;
|
||
border-bottom: 1px solid #333;
|
||
}
|
||
|
||
.log-entry:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.log-time {
|
||
color: #569cd6;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.log-type-info {
|
||
color: #4ec9b0;
|
||
}
|
||
|
||
.log-type-success {
|
||
color: #6a9955;
|
||
}
|
||
|
||
.log-type-error {
|
||
color: #f48771;
|
||
}
|
||
</style>
|
||
|
||
<script src="files/popper.js"></script>
|
||
<script src="files/bootstrap.js"></script>
|
||
<script src="files/dfido2-lib.js"></script>
|
||
<script src="files/fido2-ui-sdk.js"></script>
|
||
<script src="files/amipro_utils.js"></script>
|
||
|
||
<script>
|
||
const i18n_messages = new Map();
|
||
|
||
var lang_map = new Map();
|
||
lang_map.set("en-US", "🔐 FIDO2 UI SDK - Modal Demo");
|
||
lang_map.set("zh-CN", "🔐 FIDO2 UI SDK - 模态框演示");
|
||
lang_map.set("ja", "🔐 FIDO2 UI SDK - モーダル デモ");
|
||
i18n_messages.set("msg_title", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Demonstrate how to use FIDO2 UI SDK to manage devices in a popup window");
|
||
lang_map.set("zh-CN", "演示如何使用 FIDO2 UI SDK 在弹出窗口中管理设备");
|
||
lang_map.set("ja", "FIDO2 UI SDK を使用してポップアップ ウィンドウでデバイスを管理する方法をデモします");
|
||
i18n_messages.set("msg_subtitle", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "👤 Step1: Login");
|
||
lang_map.set("zh-CN", "👤 步骤1:登录");
|
||
lang_map.set("ja", "👤 ステップ1:ログイン");
|
||
i18n_messages.set("msg_section_login_title", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Click the button below to open the login modal:");
|
||
lang_map.set("zh-CN", "点击下面的按钮打开登录模态框:");
|
||
lang_map.set("ja", "以下のボタンを押してログインモーダルを開いてください:");
|
||
i18n_messages.set("msg_login_desc", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Passkey Login");
|
||
lang_map.set("zh-CN", "Passkey 登录");
|
||
lang_map.set("ja", "パスキーログイン");
|
||
i18n_messages.set("msg_btn_passkey_login", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Custom Style");
|
||
lang_map.set("zh-CN", "自定义样式");
|
||
lang_map.set("ja", "カスタム スタイル");
|
||
i18n_messages.set("msg_btn_password_login", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Logout");
|
||
lang_map.set("zh-CN", "退出登录");
|
||
lang_map.set("ja", "ログアウト");
|
||
i18n_messages.set("msg_btn_logout", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Logged out successfully");
|
||
lang_map.set("zh-CN", "退出登录成功");
|
||
lang_map.set("ja", "ログアウトしました");
|
||
i18n_messages.set("msg_logout_success", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Please login first to access device management");
|
||
lang_map.set("zh-CN", "请先登录以访问设备管理");
|
||
lang_map.set("ja", "デバイス管理にアクセスするにはまずログインしてください");
|
||
i18n_messages.set("msg_login_required", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Login successful");
|
||
lang_map.set("zh-CN", "登录成功");
|
||
lang_map.set("ja", "ログイン成功");
|
||
i18n_messages.set("msg_login_success", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Opening login modal...");
|
||
lang_map.set("zh-CN", "打开登录模态框...");
|
||
lang_map.set("ja", "ログインモーダルを開いています...");
|
||
i18n_messages.set("msg_log_open_login", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Opening password login modal...");
|
||
lang_map.set("zh-CN", "打开密码登录模态框...");
|
||
lang_map.set("ja", "パスワードログイン模ーダルを開いています...");
|
||
i18n_messages.set("msg_log_open_password_login", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "📌 Step2: Device Management");
|
||
lang_map.set("zh-CN", "📌 步骤2:设备管理");
|
||
lang_map.set("ja", "📌 ステップ2: デバイス管理");
|
||
i18n_messages.set("msg_section_quick_title", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Login first by Step1, then manage your FIDO2 devices");
|
||
lang_map.set("zh-CN", "先在步骤1登录,再管理您的FIDO2设备");
|
||
lang_map.set("ja", "まずステップ1でログインして、FIDO2デバイスを管理します");
|
||
i18n_messages.set("msg_quick_desc", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Manage Devices");
|
||
lang_map.set("zh-CN", "管理设备");
|
||
lang_map.set("ja", "デバイス管理");
|
||
i18n_messages.set("msg_btn_manage", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Custom Style");
|
||
lang_map.set("zh-CN", "自定义样式");
|
||
lang_map.set("ja", "カスタム スタイル");
|
||
i18n_messages.set("msg_btn_custom", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "💻 Code Examples");
|
||
lang_map.set("zh-CN", "💻 代码示例");
|
||
lang_map.set("ja", "💻 コード例");
|
||
i18n_messages.set("msg_section_code_title", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Simplest integration:");
|
||
lang_map.set("zh-CN", "最简单的集成方式:");
|
||
lang_map.set("ja", "最も簡単な統合:");
|
||
i18n_messages.set("msg_code_simple", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "With theme customization:");
|
||
lang_map.set("zh-CN", "带主题定制:");
|
||
lang_map.set("ja", "テーマのカスタマイズあり:");
|
||
i18n_messages.set("msg_code_theme", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "✨ Features");
|
||
lang_map.set("zh-CN", "✨ 功能特性");
|
||
lang_map.set("ja", "✨ 機能");
|
||
i18n_messages.set("msg_section_features_title", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Popup mode, stay on current page");
|
||
lang_map.set("zh-CN", "弹出窗口模式,不离开当前页面");
|
||
lang_map.set("ja", "ポップアップ モードで現在のページに留まる");
|
||
i18n_messages.set("msg_feature_1", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Auto FIDO2/Passkey authentication");
|
||
lang_map.set("zh-CN", "自动 FIDO2/Passkey 认证");
|
||
lang_map.set("ja", "自動 FIDO2/Passkey 認証");
|
||
i18n_messages.set("msg_feature_2", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Password login fallback option");
|
||
lang_map.set("zh-CN", "密码登录回退选项");
|
||
lang_map.set("ja", "パスワード ログイン フォールバック");
|
||
i18n_messages.set("msg_feature_3", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Add/delete FIDO2 devices support");
|
||
lang_map.set("zh-CN", "支持添加/删除 FIDO2 设备");
|
||
lang_map.set("ja", "FIDO2 デバイスの追加/削除サポート");
|
||
i18n_messages.set("msg_feature_4", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Real-time device list display");
|
||
lang_map.set("zh-CN", "实时显示设备列表");
|
||
lang_map.set("ja", "リアルタイム デバイスリスト表示");
|
||
i18n_messages.set("msg_feature_5", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Session status monitoring");
|
||
lang_map.set("zh-CN", "会话状态监控");
|
||
lang_map.set("ja", "セッション ステータス監視");
|
||
i18n_messages.set("msg_feature_6", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Theme color and style customization");
|
||
lang_map.set("zh-CN", "主题色和样式定制");
|
||
lang_map.set("ja", "テーマの色とスタイルのカスタマイズ");
|
||
i18n_messages.set("msg_feature_7", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Multi-language support (English/Japanese/Chinese)");
|
||
lang_map.set("zh-CN", "多语言支持(英/日/中)");
|
||
lang_map.set("ja", "多言語対応(英/日/中)");
|
||
i18n_messages.set("msg_feature_8", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Event callback system");
|
||
lang_map.set("zh-CN", "事件回调系统");
|
||
lang_map.set("ja", "イベント コールバック システム");
|
||
i18n_messages.set("msg_feature_9", lang_map);
|
||
lang_map.set("en-US", "Event callback system");
|
||
lang_map.set("zh-CN", "事件回调系统");
|
||
lang_map.set("ja", "イベント コールバック システム");
|
||
i18n_messages.set("msg_feature_9", 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", "📊 Event Log");
|
||
lang_map.set("zh-CN", "📊 事件日志");
|
||
lang_map.set("ja", "📊 イベント ログ");
|
||
i18n_messages.set("msg_section_log_title", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Clear Log");
|
||
lang_map.set("zh-CN", "清空日志");
|
||
lang_map.set("ja", "ログをクリア");
|
||
i18n_messages.set("msg_btn_clear", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Opening default device manager...");
|
||
lang_map.set("zh-CN", "打开默认设备管理器...");
|
||
lang_map.set("ja", "デフォルト デバイス マネージャーを開いています...");
|
||
i18n_messages.set("msg_log_open_default", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Opening custom style device manager...");
|
||
lang_map.set("zh-CN", "打开自定义样式设备管理器...");
|
||
lang_map.set("ja", "カスタム スタイル デバイス マネージャーを開いています...");
|
||
i18n_messages.set("msg_log_open_custom", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Device manager initialized successfully");
|
||
lang_map.set("zh-CN", "设备管理器初始化完成");
|
||
lang_map.set("ja", "デバイス マネージャーの初期化が完了しました");
|
||
i18n_messages.set("msg_log_init", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Custom style device manager initialized successfully");
|
||
lang_map.set("zh-CN", "自定义样式设备管理器初始化完成");
|
||
lang_map.set("ja", "カスタム スタイル デバイス マネージャーの初期化が完了しました");
|
||
i18n_messages.set("msg_log_init_custom", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Device added successfully");
|
||
lang_map.set("zh-CN", "设备添加成功");
|
||
lang_map.set("ja", "デバイスの追加が完了しました");
|
||
i18n_messages.set("msg_log_device_added", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Device deleted successfully");
|
||
lang_map.set("zh-CN", "设备删除成功");
|
||
lang_map.set("ja", "デバイスの削除が完了しました");
|
||
i18n_messages.set("msg_log_device_deleted", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Device list loaded, total");
|
||
lang_map.set("zh-CN", "设备列表加载完成,共");
|
||
lang_map.set("ja", "デバイスリストが読み込まれました。合計");
|
||
i18n_messages.set("msg_log_devices_loaded", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "devices");
|
||
lang_map.set("zh-CN", "个设备");
|
||
lang_map.set("ja", "デバイス");
|
||
i18n_messages.set("msg_log_devices_suffix", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "An error occurred");
|
||
lang_map.set("zh-CN", "发生错误");
|
||
lang_map.set("ja", "エラーが発生しました");
|
||
i18n_messages.set("msg_log_error", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Password attempts exhausted");
|
||
lang_map.set("zh-CN", "密码尝试次数已用完");
|
||
lang_map.set("ja", "パスワード試行回数が上限に達しました");
|
||
i18n_messages.set("msg_password_exhausted", lang_map);
|
||
|
||
lang_map = new Map();
|
||
lang_map.set("en-US", "Device manager closed");
|
||
lang_map.set("zh-CN", "设备管理器已关闭");
|
||
lang_map.set("ja", "デバイス マネージャーが閉じられました");
|
||
i18n_messages.set("msg_log_closed", lang_map);
|
||
|
||
window.onload = function() {
|
||
if (window.location.hostname === '127.0.0.1') {
|
||
alert('本地测试请使用 localhost');
|
||
}
|
||
setI18NText(i18n_messages);
|
||
};
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="demo-header">
|
||
<h1 id="msg_title">🔐 FIDO2 UI SDK - Modal Demo</h1>
|
||
<p id="msg_subtitle">Demonstrate how to use FIDO2 UI SDK to manage devices in a popup window</p>
|
||
</div>
|
||
|
||
<div class="demo-section">
|
||
<h2 id="msg_section_login_title">👤 Step1: Login</h2>
|
||
<p id="msg_login_desc">Click the button below to open the login modal:</p>
|
||
<button class="demo-btn demo-btn-primary" onclick="openPasskeyLogin()">
|
||
<i class="bx bx-key"></i> <span id="msg_btn_passkey_login">Passkey Login</span>
|
||
</button>
|
||
<button class="demo-btn demo-btn-secondary" onclick="openPasswordLogin()">
|
||
<i class="bx bx-palette"></i> <span id="msg_btn_password_login">Custom Style</span>
|
||
</button>
|
||
<button class="demo-btn demo-btn-secondary" id="btn-logout" onclick="logout()" disabled>
|
||
<i class="bx bx-log-out"></i> <span id="msg_btn_logout">Logout</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="demo-section">
|
||
<h2 id="msg_section_quick_title">📌 Step2: Device Management</h2>
|
||
<p id="msg_quick_desc">Step1: Login first, then manage your FIDO2 devices</p>
|
||
<button class="demo-btn demo-btn-primary" id="btn-manage-devices" onclick="openDeviceManager()" disabled>
|
||
<i class="bx bx-device"></i> <span id="msg_btn_manage">Manage Devices</span>
|
||
</button>
|
||
<button class="demo-btn demo-btn-secondary" id="btn-custom-style" onclick="openCustomDeviceManager()" disabled>
|
||
<i class="bx bx-palette"></i> <span id="msg_btn_custom">Custom Style</span>
|
||
</button>
|
||
<p id="msg_login_required" style="color: #999; margin-top: 10px;">Please login first to access device management</p>
|
||
</div>
|
||
|
||
<div class="demo-section">
|
||
<h2 id="msg_section_log_title">📊 Event Log</h2>
|
||
<div id="log-container"></div>
|
||
<button class="demo-btn demo-btn-secondary" onclick="clearLog()" style="margin-top: 15px;">
|
||
<span id="msg_btn_clear">Clear Log</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="demo-section">
|
||
<h2 id="msg_section_code_title">💻 Code Examples</h2>
|
||
|
||
<p id="msg_js_import_examples">JS Import Examples:</p>
|
||
<div class="code-block">
|
||
<code><script src="files/popper.js"></script><br>
|
||
<script src="files/bootstrap.js"></script><br>
|
||
<script src="files/dfido2-lib.js"></script><br>
|
||
<script src="files/fido2-ui-sdk.js"></script><br>
|
||
<script src="files/amipro_utils.js"></script></code>
|
||
</div>
|
||
|
||
<p id="msg_code_login_default">Login Default:</p>
|
||
<div class="code-block">
|
||
<code>// Login Default<br>
|
||
Fido2UIManager.renderLogin({<br>
|
||
container: '#login-container',<br>
|
||
mode: 'modal',<br>
|
||
serverUrl: SERVER_URL,<br>
|
||
language: CURRENT_LANG<br>
|
||
});</code>
|
||
</div>
|
||
|
||
<p id="msg_code_login_custom">Login Custom:</p>
|
||
<div class="code-block">
|
||
<code>// Login with Custom Theme<br>
|
||
Fido2UIManager.renderLogin({<br>
|
||
container: '#login-container',<br>
|
||
mode: 'modal',<br>
|
||
serverUrl: SERVER_URL,<br>
|
||
language: 'ja',<br>
|
||
theme: {<br>
|
||
logo: 'files/favicon.ico',<br>
|
||
primaryColor: '#ce59d9',<br>
|
||
backgroundColor: '#faf5ff',<br>
|
||
textColor: '#6b21a8',<br>
|
||
borderRadius: '12px'<br>
|
||
},<br>
|
||
features: {<br>
|
||
autoAuth: false,<br>
|
||
enablePasswordLogin: true,<br>
|
||
autoShowPassword: true,<br>
|
||
maxPasswordAttempts: 3<br>
|
||
},<br>
|
||
callbacks: {<br>
|
||
onFido2Success: function(username, session) {<br>
|
||
// Handle login success<br>
|
||
},<br>
|
||
onFido2Error: function(error) {<br>
|
||
// Handle error<br>
|
||
}<br>
|
||
}<br>
|
||
});</code>
|
||
</div>
|
||
|
||
<p id="msg_code_device_default">Device Manager Default:</p>
|
||
<div class="code-block">
|
||
<code>// Device Manager Default<br>
|
||
Fido2UIManager.renderDeviceManager({<br>
|
||
userId: currentUserId,<br>
|
||
container: '#device-container',<br>
|
||
mode: 'modal',<br>
|
||
serverUrl: SERVER_URL,<br>
|
||
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>
|
||
userId: currentUserId,<br>
|
||
container: '#device-container',<br>
|
||
mode: 'modal',<br>
|
||
serverUrl: SERVER_URL,<br>
|
||
language: 'ja',<br>
|
||
theme: {<br>
|
||
logo: 'files/favicon.ico',<br>
|
||
primaryColor: '#ce59d9',<br>
|
||
backgroundColor: '#faf5ff',<br>
|
||
textColor: '#6b21a8',<br>
|
||
borderRadius: '12px'<br>
|
||
},<br>
|
||
features: {<br>
|
||
showAddButton: true,<br>
|
||
showDeleteButton: true,<br>
|
||
showUserInfo: true,<br>
|
||
showSessionStatus: true<br>
|
||
},<br>
|
||
callbacks: {<br>
|
||
onInit: function(manager) {<br>
|
||
// Manager initialized<br>
|
||
},<br>
|
||
onDeviceAdded: function(device) {<br>
|
||
// Device added<br>
|
||
},<br>
|
||
onDeviceDeleted: function(deviceId) {<br>
|
||
// Device deleted<br>
|
||
},<br>
|
||
onClose: function() {<br>
|
||
// Manager closed<br>
|
||
}<br>
|
||
}<br>
|
||
});</code>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="demo-section">
|
||
<h2 id="msg_section_features_title">✨ Features</h2>
|
||
<ul class="feature-list">
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_1">Popup mode, stay on current page</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_2">Auto FIDO2/Passkey authentication</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_3">Password login fallback option</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_4">Add/delete FIDO2 devices support</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_5">Real-time device list display</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_6">Session status monitoring</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_7">Theme color and style customization</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_8">Multi-language support (English/Japanese/Chinese)</span></li>
|
||
<li><i class="bx bx-check-circle"></i> <span id="msg_feature_9">Event callback system</span></li>
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div id="login-container"></div>
|
||
<div id="device-container"></div>
|
||
|
||
<script>
|
||
const SERVER_URL = 'https://fido2.amipro.me'; //'https://local.dqj-macpro.com';
|
||
|
||
let currentUserId = null;
|
||
let isLoggedIn = false;
|
||
let loginManager = null;
|
||
|
||
function getBrowserLanguage() {
|
||
const lang = window.navigator.language || window.navigator.userLanguage;
|
||
const supportedLangs = ['en-US', 'zh-CN', 'ja'];
|
||
if (supportedLangs.includes(lang)) {
|
||
return lang;
|
||
}
|
||
return 'en-US';
|
||
}
|
||
|
||
const CURRENT_LANG = getBrowserLanguage();
|
||
|
||
function enableDeviceManagementButtons() {
|
||
document.getElementById('btn-manage-devices').disabled = false;
|
||
document.getElementById('btn-custom-style').disabled = false;
|
||
document.getElementById('msg_login_required').style.display = 'none';
|
||
document.getElementById('btn-logout').disabled = false;
|
||
}
|
||
|
||
function disableDeviceManagementButtons() {
|
||
document.getElementById('btn-manage-devices').disabled = true;
|
||
document.getElementById('btn-custom-style').disabled = true;
|
||
document.getElementById('msg_login_required').style.display = 'block';
|
||
document.getElementById('btn-logout').disabled = true;
|
||
}
|
||
|
||
function logout() {
|
||
Fido2UIManager.logout();
|
||
currentUserId = null;
|
||
isLoggedIn = false;
|
||
log('success', getI18NText(i18n_messages, 'msg_logout_success'));
|
||
disableDeviceManagementButtons();
|
||
}
|
||
|
||
function openPasskeyLogin() {
|
||
clearLog();
|
||
log('info', getI18NText(i18n_messages, 'msg_log_open_login'));
|
||
|
||
if (loginManager) {
|
||
loginManager.destroy();
|
||
}
|
||
|
||
loginManager = Fido2UIManager.renderLogin({
|
||
container: '#login-container',
|
||
mode: 'modal',
|
||
serverUrl: SERVER_URL,
|
||
language: CURRENT_LANG,
|
||
features: {
|
||
autoAuth: true,
|
||
enablePasswordLogin: true,
|
||
autoShowPassword: false,
|
||
maxPasswordAttempts: 3,
|
||
showRemainingAttempts: true,
|
||
},
|
||
callbacks: {
|
||
onFido2Success: function(username, session) {
|
||
log('success', getI18NText(i18n_messages, 'msg_login_success') + ': ' + username);
|
||
currentUserId = username;
|
||
isLoggedIn = true;
|
||
enableDeviceManagementButtons();
|
||
openDeviceManager();
|
||
},
|
||
onFido2Error: function(error) {
|
||
log('error', getI18NText(i18n_messages, 'msg_log_error') + ': ' + error.message);
|
||
},
|
||
onPasswordLogin: function(userId, password) {
|
||
return new Promise(function(resolve) {
|
||
log('info', 'Password login for user: ' + userId);
|
||
currentUserId = userId;
|
||
isLoggedIn = true;
|
||
enableDeviceManagementButtons();
|
||
resolve(true);
|
||
});
|
||
},
|
||
onPasswordExhausted: function(userId, attemptCount, maxAttempts) {
|
||
log('error', getI18NText(i18n_messages, 'msg_log_error') + ': ' + getI18NText(i18n_messages, 'msg_password_exhausted'));
|
||
},
|
||
onLoginClosed: function() {
|
||
log('info', getI18NText(i18n_messages, 'msg_log_closed'));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function openPasswordLogin() {
|
||
clearLog();
|
||
log('info', getI18NText(i18n_messages, 'msg_log_open_password_login'));
|
||
|
||
if (loginManager) {
|
||
loginManager.destroy();
|
||
}
|
||
|
||
loginManager = Fido2UIManager.renderLogin({
|
||
container: '#login-container',
|
||
mode: 'modal',
|
||
serverUrl: SERVER_URL,
|
||
language: CURRENT_LANG,
|
||
theme: {
|
||
logo: 'files/favicon.ico',
|
||
primaryColor: '#ce59d9',
|
||
backgroundColor: '#faf5ff',
|
||
textColor: '#6b21a8',
|
||
borderRadius: '12px'
|
||
},
|
||
features: {
|
||
autoAuth: false,
|
||
enablePasswordLogin: true,
|
||
autoShowPassword: true,
|
||
maxPasswordAttempts: 3,
|
||
showRemainingAttempts: true,
|
||
},
|
||
callbacks: {
|
||
onFido2Success: function(username, session) {
|
||
log('success', getI18NText(i18n_messages, 'msg_login_success') + ': ' + username);
|
||
currentUserId = username;
|
||
isLoggedIn = true;
|
||
enableDeviceManagementButtons();
|
||
},
|
||
onFido2Error: function(error) {
|
||
log('error', getI18NText(i18n_messages, 'msg_log_error') + ': ' + error.message);
|
||
},
|
||
onPasswordLogin: function(userId, password) {
|
||
return new Promise(function(resolve) {
|
||
log('info', 'Password login for user: ' + userId);
|
||
currentUserId = userId;
|
||
isLoggedIn = true;
|
||
enableDeviceManagementButtons();
|
||
resolve(true);
|
||
});
|
||
},
|
||
onPasswordExhausted: function(userId, attemptCount, maxAttempts) {
|
||
log('error', getI18NText(i18n_messages, 'msg_log_error') + ': ' + getI18NText(i18n_messages, 'msg_password_exhausted'));
|
||
},
|
||
onLoginClosed: function() {
|
||
log('info', getI18NText(i18n_messages, 'msg_log_closed'));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function openDeviceManager() {
|
||
if (!isLoggedIn) {
|
||
log('info', getI18NText(i18n_messages, 'msg_login_required'));
|
||
return;
|
||
}
|
||
|
||
clearLog();
|
||
log('info', getI18NText(i18n_messages, 'msg_log_open_default'));
|
||
|
||
Fido2UIManager.renderDeviceManager({
|
||
userId: currentUserId,
|
||
container: '#device-container',
|
||
mode: 'modal',
|
||
serverUrl: SERVER_URL,
|
||
language: CURRENT_LANG,
|
||
callbacks: {
|
||
onInit: function(manager) {
|
||
log('success', getI18NText(i18n_messages, 'msg_log_init'));
|
||
},
|
||
onDeviceAdded: function(device) {
|
||
log('success', getI18NText(i18n_messages, 'msg_log_device_added') + ': ' + JSON.stringify(device));
|
||
},
|
||
onDeviceDeleted: function(deviceId) {
|
||
log('success', getI18NText(i18n_messages, 'msg_log_device_deleted') + ': ' + deviceId);
|
||
},
|
||
onDeviceListLoaded: function(devices) {
|
||
log('info', getI18NText(i18n_messages, 'msg_log_devices_loaded') + ' ' + devices.length + ' ' + getI18NText(i18n_messages, 'msg_log_devices_suffix'));
|
||
},
|
||
onError: function(error) {
|
||
log('error', getI18NText(i18n_messages, 'msg_log_error') + ': ' + error.message);
|
||
},
|
||
onClose: function() {
|
||
log('info', getI18NText(i18n_messages, 'msg_log_closed'));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function openCustomDeviceManager() {
|
||
if (!isLoggedIn) {
|
||
log('info', getI18NText(i18n_messages, 'msg_login_required'));
|
||
return;
|
||
}
|
||
|
||
clearLog();
|
||
log('info', getI18NText(i18n_messages, 'msg_log_open_custom'));
|
||
|
||
Fido2UIManager.renderDeviceManager({
|
||
userId: currentUserId,
|
||
container: '#device-container',
|
||
mode: 'modal',
|
||
serverUrl: SERVER_URL,
|
||
language: CURRENT_LANG,
|
||
theme: {
|
||
logo: 'files/favicon.ico',
|
||
primaryColor: '#ce59d9',
|
||
backgroundColor: '#faf5ff',
|
||
textColor: '#6b21a8',
|
||
borderRadius: '12px'
|
||
},
|
||
features: {
|
||
showAddButton: true,
|
||
showDeleteButton: true,
|
||
showUserInfo: true,
|
||
showSessionStatus: true
|
||
},
|
||
callbacks: {
|
||
onInit: function(manager) {
|
||
log('success', getI18NText(i18n_messages, 'msg_log_init_custom'));
|
||
},
|
||
onDeviceAdded: function(device) {
|
||
log('success', getI18NText(i18n_messages, 'msg_log_device_added') + ': ' + JSON.stringify(device));
|
||
},
|
||
onDeviceDeleted: function(deviceId) {
|
||
log('success', getI18NText(i18n_messages, 'msg_log_device_deleted') + ': ' + deviceId);
|
||
},
|
||
onDeviceListLoaded: function(devices) {
|
||
log('info', getI18NText(i18n_messages, 'msg_log_devices_loaded') + ' ' + devices.length + ' ' + getI18NText(i18n_messages, 'msg_log_devices_suffix'));
|
||
},
|
||
onError: function(error) {
|
||
log('error', getI18NText(i18n_messages, 'msg_log_error') + ': ' + error.message);
|
||
},
|
||
onClose: function() {
|
||
log('info', getI18NText(i18n_messages, 'msg_log_closed'));
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function log(type, message) {
|
||
const container = document.getElementById('log-container');
|
||
const entry = document.createElement('div');
|
||
entry.className = 'log-entry';
|
||
|
||
const time = new Date().toLocaleTimeString(CURRENT_LANG);
|
||
const typeClass = 'log-type-' + type;
|
||
|
||
entry.innerHTML = `<span class="log-time">[${time}]</span><span class="${typeClass}">[${type.toUpperCase()}]</span> ${message}`;
|
||
container.appendChild(entry);
|
||
container.scrollTop = container.scrollHeight;
|
||
}
|
||
|
||
function clearLog() {
|
||
document.getElementById('log-container').innerHTML = '';
|
||
}
|
||
</script>
|
||
<p style="text-align: center; margin-top: 20px;">
|
||
<a href="https://amipro.me/fido2_top.html" target="_blank" style="display: inline-block; padding: 12px 24px; background: #667eea; color: white; text-decoration: none; border-radius: 8px; font-weight: bold;">
|
||
https://amipro.me/fido2_top.html
|
||
</a>
|
||
</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>
|
||
</html>
|