Add SSO and device management samples

This commit is contained in:
dqj
2026-05-20 22:40:32 +09:00
parent 7248372cd8
commit 6c9491bfd0
11 changed files with 2323 additions and 1 deletions

316
test-login-sdk.html Normal file
View File

@@ -0,0 +1,316 @@
<!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>