refactor: remove passkey login

This commit is contained in:
237899745
2026-05-27 22:32:42 +08:00
parent 89cb98233f
commit 0443c976fc
105 changed files with 410 additions and 2505 deletions

View File

@@ -121,23 +121,6 @@
transform: translateY(0);
}
.btn-passkey {
width: 100%;
margin-top: 10px;
padding: 11px;
border-radius: 10px;
border: 1px solid rgba(17,24,39,0.14);
background: #f8fafc;
color: #0f172a;
font-size: 14px;
font-weight: 700;
cursor: pointer;
}
.btn-passkey:hover {
background: #f1f5f9;
}
.back-link {
text-align: center;
margin-top: 20px;
@@ -230,7 +213,6 @@
</div>
<button type="submit" class="btn-login">登录后台</button>
<button type="button" class="btn-passkey" onclick="handlePasskeyLogin()">使用 Passkey 登录</button>
</form>
<div class="back-link">
@@ -257,57 +239,6 @@
return headers;
}
function isPasskeyAvailable() {
return window.isSecureContext && !!window.PublicKeyCredential && !!navigator.credentials;
}
function base64UrlToUint8Array(base64url) {
const value = String(base64url || '');
const padding = '='.repeat((4 - (value.length % 4)) % 4);
const base64 = (value + padding).replace(/-/g, '+').replace(/_/g, '/');
const raw = window.atob(base64);
const bytes = new Uint8Array(raw.length);
for (let i = 0; i < raw.length; i += 1) bytes[i] = raw.charCodeAt(i);
return bytes;
}
function uint8ArrayToBase64Url(input) {
const bytes = input instanceof ArrayBuffer ? new Uint8Array(input) : new Uint8Array(input || []);
let binary = '';
for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
return window.btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
}
function normalizeRequestOptions(rawOptions) {
const options = rawOptions && rawOptions.publicKey ? rawOptions.publicKey : rawOptions;
const normalized = {
...options,
challenge: base64UrlToUint8Array(options.challenge),
};
if (Array.isArray(options.allowCredentials)) {
normalized.allowCredentials = options.allowCredentials.map((item) => ({
...item,
id: base64UrlToUint8Array(item.id),
}));
}
return normalized;
}
function serializeAssertion(credential) {
return {
id: credential.id,
rawId: uint8ArrayToBase64Url(credential.rawId),
type: credential.type,
authenticatorAttachment: credential.authenticatorAttachment || undefined,
response: {
clientDataJSON: uint8ArrayToBase64Url(credential.response.clientDataJSON),
authenticatorData: uint8ArrayToBase64Url(credential.response.authenticatorData),
signature: uint8ArrayToBase64Url(credential.response.signature),
userHandle: credential.response.userHandle ? uint8ArrayToBase64Url(credential.response.userHandle) : null,
},
};
}
async function handleLogin(event) {
event.preventDefault();
@@ -372,60 +303,6 @@
}
}
async function handlePasskeyLogin() {
const username = document.getElementById('username').value.trim();
const errorDiv = document.getElementById('errorMessage');
const successDiv = document.getElementById('successMessage');
errorDiv.style.display = 'none';
successDiv.style.display = 'none';
if (!isPasskeyAvailable()) {
errorDiv.textContent = '当前浏览器或环境不支持Passkey需HTTPS';
errorDiv.style.display = 'block';
return;
}
try {
const optionsResp = await fetch('/yuyx/api/passkeys/login/options', {
method: 'POST',
credentials: 'same-origin',
headers: jsonHeaders(),
body: JSON.stringify(username ? { username } : {}),
});
const optionsData = await optionsResp.json();
if (!optionsResp.ok) {
throw new Error(optionsData.error || '获取Passkey挑战失败');
}
const publicKey = normalizeRequestOptions(optionsData.publicKey || {});
const assertion = await navigator.credentials.get({ publicKey });
const credential = serializeAssertion(assertion);
const verifyResp = await fetch('/yuyx/api/passkeys/login/verify', {
method: 'POST',
credentials: 'same-origin',
headers: jsonHeaders(),
body: JSON.stringify(username ? { username, credential } : { credential }),
});
const verifyData = await verifyResp.json();
if (!verifyResp.ok) {
throw new Error(verifyData.error || 'Passkey登录失败');
}
successDiv.textContent = 'Passkey 登录成功,正在跳转...';
successDiv.style.display = 'block';
await new Promise(resolve => setTimeout(resolve, 500));
window.location.replace(verifyData.redirect || '/yuyx/admin');
} catch (error) {
const msg = error?.name === 'NotAllowedError'
? 'Passkey验证未完成可能取消、超时或设备未响应'
: (error?.message || 'Passkey登录失败');
errorDiv.textContent = msg;
errorDiv.style.display = 'block';
}
}
async function generateCaptcha() {
try {
const response = await fetch('/api/generate_captcha', {