From 50371b9297de32ca46546006aeea9fdf755a9740 Mon Sep 17 00:00:00 2001 From: elpatron Date: Thu, 28 May 2026 10:45:33 +0200 Subject: [PATCH] feat: implement usernameless Passkey login flow using discoverable credentials --- client/src/components/AuthOnboarding.tsx | 74 ++++++++++++++---------- client/src/services/auth.ts | 18 ++++-- server/src/routes/auth.ts | 72 +++++++++++------------ 3 files changed, 89 insertions(+), 75 deletions(-) diff --git a/client/src/components/AuthOnboarding.tsx b/client/src/components/AuthOnboarding.tsx index c047a4a..f6138e9 100644 --- a/client/src/components/AuthOnboarding.tsx +++ b/client/src/components/AuthOnboarding.tsx @@ -40,14 +40,13 @@ export default function AuthOnboarding({ onAuthenticated }: AuthOnboardingProps) } } - const handleLogin = async (e: React.FormEvent) => { - e.preventDefault() - if (!username.trim()) return + const handleLogin = async (e?: React.FormEvent) => { + if (e) e.preventDefault() setLoading(true) setError(null) try { - const result = await loginUser(username.trim()) + const result = await loginUser() if (result.verified) { if (result.prfSuccess) { // Biometric E2E decryption succeeded @@ -55,6 +54,9 @@ export default function AuthOnboarding({ onAuthenticated }: AuthOnboardingProps) } else { // Biometrics succeeded but PRF key wasn't supported/available, fall back to recovery phrase setEncryptedPayloads(result.encryptedPayloads) + if (result.username) { + setUsername(result.username) + } setShowRecoveryFallback(true) } } @@ -72,7 +74,8 @@ export default function AuthOnboarding({ onAuthenticated }: AuthOnboardingProps) setLoading(true) setError(null) try { - const success = await completeLoginWithRecovery(username.trim(), recoveryInput.trim(), encryptedPayloads) + const resolvedUser = username.trim() || encryptedPayloads.username + const success = await completeLoginWithRecovery(resolvedUser, recoveryInput.trim(), encryptedPayloads) if (success) { onAuthenticated() } else { @@ -180,40 +183,51 @@ export default function AuthOnboarding({ onAuthenticated }: AuthOnboardingProps)

{t('auth.tagline')}

-
-
- setUsername(e.target.value)} - disabled={loading} - required - /> +
+ {/* Prominent Login button */} + + + {/* Separator */} +
+
+ or register +
- {error &&
{error}
} + {/* Registration form */} + +
+ setUsername(e.target.value)} + disabled={loading} + required + /> +
-
- -
- + + + {error &&
{error}
} +