From 19b96a7acae1a546f35aacb08582bc5b9573f157 Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Wed, 10 Jun 2026 12:10:11 +0800 Subject: [PATCH] feat: add passkey unlock functionality and improve related error handling --- webapp/src/App.tsx | 28 +++++++ webapp/src/components/AuthViews.tsx | 19 +++-- webapp/src/lib/account-passkeys.ts | 112 +++++++++++++++++++++------ webapp/src/lib/app-auth.ts | 6 +- webapp/src/lib/i18n/locales/en.ts | 2 + webapp/src/lib/i18n/locales/es.ts | 60 +++++++------- webapp/src/lib/i18n/locales/ru.ts | 62 ++++++++------- webapp/src/lib/i18n/locales/zh-CN.ts | 2 + webapp/src/lib/i18n/locales/zh-TW.ts | 2 + 9 files changed, 206 insertions(+), 87 deletions(-) diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 1234eeb..54b753b 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -570,6 +570,33 @@ export default function App() { } } + async function handlePasskeyUnlock() { + if (pendingAuthAction) return; + const expectedEmail = (profile?.email || session?.email || '').trim().toLowerCase(); + if (!expectedEmail) return; + if (IS_DEMO_MODE) { + pushToast('warning', t('txt_demo_readonly_message')); + return; + } + setPendingAuthAction('passkey'); + try { + const result = await performPasskeyLogin(defaultKdfIterations, expectedEmail); + if (result.kind === 'success') { + await finalizeLogin(result.login, t('txt_unlocked')); + return; + } + if (result.kind === 'password') { + pushToast('error', t('txt_account_passkey_direct_unlock_unavailable_error')); + return; + } + pushToast('error', result.message || t('txt_unlock_failed_master_password_is_incorrect')); + } catch (error) { + pushToast('error', error instanceof Error ? error.message : t('txt_unlock_failed_master_password_is_incorrect')); + } finally { + setPendingAuthAction(null); + } + } + async function handlePasskeyPasswordLogin() { if (pendingAuthAction || !pendingPasskeyPassword) return; if (!passkeyPassword) { @@ -1720,6 +1747,7 @@ export default function App() { onChangeUnlock={setUnlockPassword} onSubmitLogin={() => void handleLogin()} onSubmitPasskey={() => void handlePasskeyLogin()} + onSubmitPasskeyUnlock={() => void handlePasskeyUnlock()} onSubmitPasskeyPassword={() => void handlePasskeyPasswordLogin()} onSubmitRegister={() => void handleRegister()} onSubmitUnlock={() => void handleUnlock()} diff --git a/webapp/src/components/AuthViews.tsx b/webapp/src/components/AuthViews.tsx index b8c16ed..9587fff 100644 --- a/webapp/src/components/AuthViews.tsx +++ b/webapp/src/components/AuthViews.tsx @@ -40,6 +40,7 @@ interface AuthViewsProps { onChangeUnlock: (password: string) => void; onSubmitLogin: () => void; onSubmitPasskey: () => void; + onSubmitPasskeyUnlock: () => void; onSubmitPasskeyPassword: () => void; onSubmitRegister: () => void; onSubmitUnlock: () => void; @@ -122,12 +123,21 @@ export default function AuthViews(props: AuthViewsProps) { {props.unlockPreparing ? (
{t('txt_loading')}
) : null} -