Files
sample-site/test-login-sdk.html
2026-05-20 22:40:32 +09:00

317 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN" class="light-style customizer-hide">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Fido2 Login SDK Test</title>
<link rel="stylesheet" href="files/core.css" />
<link rel="stylesheet" href="files/theme-default.css" />
<link rel="stylesheet" href="files/fido2-ui-sdk.css" />
<style>
.status-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
}
.status-registered { background-color: #28a745; }
.status-unregistered { background-color: #dc3545; }
</style>
</head>
<body>
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-10">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h4 class="mb-0">Fido2 Login SDK 测试</h4>
<div>
<span class="status-indicator" id="regStatusIndicator"></span>
<small class="text-muted" id="regStatusText">检查中...</small>
</div>
</div>
<div class="card-body">
<div class="row mb-4">
<div class="col-md-6">
<div class="card bg-light">
<div class="card-body">
<h5>设备注册测试</h5>
<div class="mb-2">
<input type="text" class="form-control" id="testUserId" placeholder="输入User ID" value="testuser">
</div>
<div class="mb-2">
<input type="text" class="form-control" id="testDisplayName" placeholder="显示名称" value="测试设备">
</div>
<div class="alert alert-info py-1 px-2 mb-2" style="font-size: 12px;">
<small>💡 <strong>提示:</strong>使用新User ID注册可测试自动登录流程</small>
</div>
<button class="btn btn-success btn-sm me-2" id="registerDeviceBtn">
注册Passkey设备
</button>
<button class="btn btn-outline-primary btn-sm" id="checkRegBtn">
检查状态
</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card bg-light">
<div class="card-body">
<h5>登录组件测试</h5>
<button class="btn btn-primary me-2" id="testLoginBtn">
启动登录组件
</button>
<button class="btn btn-secondary me-2" id="testDestroyBtn">
销毁组件
</button>
<button class="btn btn-outline-info btn-sm" id="checkStateBtn">
检查状态
</button>
<div class="mt-2">
<small class="text-muted" id="loginStateText">未启动</small>
</div>
</div>
</div>
</div>
</div>
<hr />
<h5>测试日志:</h5>
<div id="testLog" class="bg-light p-3" style="max-height: 300px; overflow-y: auto; font-family: monospace; font-size: 12px;">
</div>
</div>
</div>
</div>
</div>
</div>
<div id="login-container"></div>
<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>
// 清理旧会话(只清理会话,不影响注册状态)
logoutFido2UserSession();
console.log('已清理旧会话');
console.log('dfido2_lib_registered:', localStorage.getItem('dfido2_lib_registered'));
// 通过配置传递服务器URL不直接操作localStorage
window.serverUrl = 'https://local.dqj-macpro.com';
// 设置调试日志函数
window.fido2LoginDebug = function(message) {
log(`[SDK] ${message}`, 'info');
};
function log(message, type = 'info') {
const logEl = document.getElementById('testLog');
const time = new Date().toLocaleTimeString();
const color = type === 'error' ? 'red' : type === 'success' ? 'green' : type === 'warn' ? 'orange' : type === 'debug' ? 'purple' : 'blue';
logEl.innerHTML += `<div style="color: ${color};">[${time}] ${message}</div>`;
logEl.scrollTop = logEl.scrollHeight;
console.log(`[${type.toUpperCase()}]`, message);
}
// 设置调试日志函数
window.fido2LoginDebug = function(message) {
log(`[SDK] ${message}`, 'info');
};
function log(message, type = 'info') {
const logEl = document.getElementById('testLog');
const time = new Date().toLocaleTimeString();
const color = type === 'error' ? 'red' : type === 'success' ? 'green' : type === 'warn' ? 'orange' : type === 'debug' ? 'purple' : 'blue';
logEl.innerHTML += `<div style="color: ${color};">[${time}] ${message}</div>`;
logEl.scrollTop = logEl.scrollHeight;
console.log(`[${type.toUpperCase()}]`, message);
}
function updateRegStatus() {
const registered = localStorage.getItem('dfido2_lib_registered');
const indicator = document.getElementById('regStatusIndicator');
const text = document.getElementById('regStatusText');
if (registered) {
indicator.className = 'status-indicator status-registered';
text.textContent = '已注册设备';
} else {
indicator.className = 'status-indicator status-unregistered';
text.textContent = '未注册设备';
}
}
function updateLoginState(state) {
document.getElementById('loginStateText').textContent = state || '未启动';
}
let loginManager = null;
// 页面加载时检查状态
updateRegStatus();
log('测试页面已加载完成');
log('💡 使用新User ID注册设备然后启动登录组件测试自动Fido2登录');
// 注册设备
document.getElementById('registerDeviceBtn').addEventListener('click', async function() {
const userId = document.getElementById('testUserId').value.trim();
const displayName = document.getElementById('testDisplayName').value.trim() || 'Device-' + userId;
if (!userId) {
log('请输入User ID', 'error');
return;
}
log(`正在为用户 ${userId} 注册设备...`);
log(`服务器URL: ${window.serverUrl}`);
try {
const result = await registerFido2(userId, displayName, null);
if (result.status === 'ok') {
log(`✅ 设备注册成功!`, 'success');
log(`dfido2_lib_registered: ${localStorage.getItem('dfido2_lib_registered')}`);
updateRegStatus();
} else {
log(`❌ 设备注册失败: ${result.errorMessage}`, 'error');
}
} catch (error) {
log(`❌ 注册异常: ${error.message}`, 'error');
}
});
// 检查注册状态
document.getElementById('checkRegBtn').addEventListener('click', function() {
const registered = localStorage.getItem('dfido2_lib_registered');
const session = sessionStorage.getItem('fido2_user_session');
log(`dfido2_lib_registered: ${registered || 'null'}`);
log(`fido2_user_session: ${session ? '存在' : 'null'}`);
log(`canTryAutoAuthentication(): ${canTryAutoAuthentication()}`);
updateRegStatus();
});
// 检查登录状态
document.getElementById('checkStateBtn').addEventListener('click', function() {
if (loginManager) {
log(`登录组件状态: ${loginManager.getState()}`);
log(`登录模式: ${loginManager.getMode()}`);
log(`尝试次数: ${loginManager.getAttemptCount()}/${loginManager.maxAttempts}`);
updateLoginState(loginManager.getState() + ' - ' + loginManager.getMode());
} else {
log('登录组件未启动');
updateLoginState('未启动');
}
});
// 启动登录组件
document.getElementById('testLoginBtn').addEventListener('click', function() {
// 确保调试函数已设置
window.fido2LoginDebug = function(message) {
log(`[SDK] ${message}`, 'info');
};
log('========================================');
log('启动登录组件...');
log(`canTryAutoAuthentication() = ${canTryAutoAuthentication()}`);
log(`dfido2_lib_registered = ${localStorage.getItem('dfido2_lib_registered')}`);
log('========================================');
if (loginManager) {
log('已有登录组件实例,先销毁');
loginManager.destroy();
loginManager = null;
}
try {
// 清理旧的modal元素和容器内容
const container = document.querySelector('#login-container');
if (container) {
container.innerHTML = '';
}
const oldModal = document.querySelector('.fido2-sdk-login-modal');
if (oldModal) {
oldModal.remove();
log('清理旧modal元素');
}
// 短暂延迟确保DOM清理完成
setTimeout(function() {
loginManager = Fido2UIManager.renderLogin({
serverUrl: window.serverUrl, // 通过SDK配置传递服务器URL
container: '#login-container',
features: {
autoAuth: true,
enablePasswordLogin: true,
autoShowPassword: false,
maxPasswordAttempts: 3,
showRemainingAttempts: true
},
callbacks: {
onFido2Success: function(userId, session) {
log(`✅✅✅ Fido2登录成功: ${userId}`, 'success');
log(`Session: ${session ? '存在' : 'null'}`);
updateLoginState('登录成功');
},
onFido2Error: function(error) {
log(`❌ Fido2失败: [${error.code}] ${error.message}`, 'error');
updateLoginState('Fido2失败: ' + error.code);
if (error.originalError) {
log(`原始错误: ${JSON.stringify(error.originalError).substring(0, 300)}`);
}
},
onPasswordLogin: function(userId, password) {
log(`🔐 密码登录尝试: userId="${userId}", password="***${password.slice(-2)}"`);
// 模拟密码验证
return new Promise(function(resolve) {
setTimeout(function() {
// This is demo code, accept any password
log('✅ 密码验证成功', 'success');
updateLoginState('密码登录成功');
resolve(true);
}, 500);
});
},
onPasswordExhausted: function(userId, attempts, maxAttempts) {
log(`🚫 密码重试次数耗尽: ${userId} (${attempts}/${maxAttempts})`, 'error');
updateLoginState('密码次数耗尽');
},
onLoginClosed: function() {
log('🔒 登录UI已关闭');
updateLoginState('已关闭');
}
},
theme: {
primaryColor: '#ce59d6',
backgroundColor: '#ffffff',
borderRadius: '8px'
},
language: 'zh-CN'
});
log('✅ 登录组件已启动');
}, 100);
} catch (error) {
log(`❌ 启动失败: ${error.message}`, 'error');
console.error(error);
}
});
// 销毁组件
document.getElementById('testDestroyBtn').addEventListener('click', function() {
if (loginManager) {
log('销毁登录组件...');
loginManager.destroy();
loginManager = null;
updateLoginState('已销毁');
log('✅ 组件已销毁');
} else {
log('没有活动的登录组件');
}
});
</script>
</body>
</html>