fix: use check-email endpoint instead of sign-in probe for email verification

This commit is contained in:
2026-04-26 08:02:18 -05:00
parent d1d40c297e
commit 8dfbd3ca5e
+41 -46
View File
@@ -36,38 +36,33 @@ export default function LoginForm() {
return return
} }
// Check if email exists by attempting sign-in with empty password
setIsLoading(true) setIsLoading(true)
const { error } = await authClient.signIn.email( try {
{ email, password: "" }, const res = await fetch("/api/auth/check-email", {
{ method: "POST",
onError: () => { headers: { "Content-Type": "application/json" },
// Silently handle - we check the error type below body: JSON.stringify({ email }),
}, })
},
)
setIsLoading(false)
if (error) { if (!res.ok) {
// If error is about invalid credentials, email exists but password is wrong const data = await res.json()
// If error is about user not found, email doesn't exist setError(data.error || "Something went wrong. Please try again.")
if (
error.message?.toLowerCase().includes("not found") ||
error.message?.toLowerCase().includes("invalid email") ||
error.status === 404
) {
setError(
"No account found with this email.",
)
return return
} }
const data = await res.json()
if (!data.exists) {
setError("No account found with this email.")
return
}
// Email exists, move to password phase // Email exists, move to password phase
setPhase("password") setPhase("password")
return } catch {
setError("Something went wrong. Please try again.")
} finally {
setIsLoading(false)
} }
// Shouldn't reach here with empty password, but handle it
setPhase("password")
} }
const handleLogin = async (e: React.FormEvent) => { const handleLogin = async (e: React.FormEvent) => {
@@ -99,22 +94,22 @@ export default function LoginForm() {
} }
return ( return (
<div className="space-y-4"> <div className="space-y-5">
<div className="text-center mb-2"> <div className="text-center mb-2">
<h1 className="text-lg font-bold text-[#ebe4f1]"> <h1 className="text-xl font-bold text-text">
Welcome back Welcome back
</h1> </h1>
<p className="text-xs text-[#ebe4f1]/50 mt-1"> <p className="text-sm text-text/50 mt-1">
{phase === "email" {phase === "email"
? "Sign in to DeckyVault" ? "Sign in to DeckyVault"
: `Signing in as `} : `Signing in as `}
{phase === "password" && ( {phase === "password" && (
<> <>
<strong className="text-[#ebe4f1]">{email}</strong> <strong className="text-text">{email}</strong>
{" "} {" "}
<button <button
onClick={handleChangeEmail} onClick={handleChangeEmail}
className="text-[#eb3779] hover:underline text-[11px]" className="text-primary hover:underline text-xs"
> >
change change
</button> </button>
@@ -126,22 +121,22 @@ export default function LoginForm() {
<SocialButtons /> <SocialButtons />
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="flex-1 h-px bg-white/[0.08]" /> <div className="flex-1 h-px bg-border" />
<span className="text-[11px] text-[#ebe4f1]/40 uppercase"> <span className="text-xs text-text/40 uppercase">
or continue with email or continue with email
</span> </span>
<div className="flex-1 h-px bg-white/[0.08]" /> <div className="flex-1 h-px bg-border" />
</div> </div>
{error && ( {error && (
<div className="text-red-400 text-xs text-center bg-red-500/10 border border-red-500/20 rounded-lg px-3 py-2"> <div className="text-red-400 text-sm text-center bg-red-500/10 border border-red-500/20 rounded-lg px-4 py-3">
{error} {error}
{error.includes("No account found") && ( {error.includes("No account found") && (
<> <>
{" "} {" "}
<Link <Link
href="/signup" href="/signup"
className="text-[#eb3779] font-semibold hover:underline" className="text-primary font-semibold hover:underline"
> >
Create one Create one
</Link> </Link>
@@ -153,7 +148,7 @@ export default function LoginForm() {
{phase === "email" ? ( {phase === "email" ? (
<form onSubmit={handleEmailSubmit} className="space-y-4"> <form onSubmit={handleEmailSubmit} className="space-y-4">
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
Email Email
</label> </label>
<input <input
@@ -162,13 +157,13 @@ export default function LoginForm() {
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
placeholder="you@example.com" placeholder="you@example.com"
autoComplete="username webauthn" autoComplete="username webauthn"
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
</div> </div>
<button <button
type="submit" type="submit"
disabled={isLoading} disabled={isLoading}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
> >
{isLoading && ( {isLoading && (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
@@ -179,7 +174,7 @@ export default function LoginForm() {
) : ( ) : (
<form onSubmit={handleLogin} className="space-y-4"> <form onSubmit={handleLogin} className="space-y-4">
<div> <div>
<label className="text-xs text-[#ebe4f1]/60 block mb-1"> <label className="text-sm text-text/60 block mb-1.5">
Password Password
</label> </label>
<input <input
@@ -189,13 +184,13 @@ export default function LoginForm() {
placeholder="Enter your password" placeholder="Enter your password"
autoComplete="current-password webauthn" autoComplete="current-password webauthn"
autoFocus autoFocus
className="w-full px-3 py-2.5 rounded-lg border border-white/10 bg-white/[0.03] text-[#ebe4f1] text-sm placeholder:text-[#ebe4f1]/40 outline-none focus:border-[#eb3779] focus:ring-2 focus:ring-[#eb3779]/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
</div> </div>
<div className="text-right"> <div className="text-right">
<Link <Link
href="/forgot-password" href="/forgot-password"
className="text-xs text-[#ebe4f1]/50 hover:text-[#ebe4f1] transition-colors" className="text-sm text-text/50 hover:text-text transition-colors"
> >
Forgot password? Forgot password?
</Link> </Link>
@@ -203,7 +198,7 @@ export default function LoginForm() {
<button <button
type="submit" type="submit"
disabled={isLoading} disabled={isLoading}
className="w-full py-2.5 rounded-lg bg-[#eb3779] text-white text-sm font-semibold hover:bg-[#eb3779]/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2" className="w-full py-3 rounded-lg bg-primary text-white text-sm font-semibold hover:bg-primary/90 transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
> >
{isLoading && ( {isLoading && (
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-4 w-4 animate-spin" />
@@ -211,19 +206,19 @@ export default function LoginForm() {
Sign in Sign in
</button> </button>
{/* Passkey hint */} {/* Passkey hint */}
<div className="text-center p-2.5 rounded-lg bg-[#eb3779]/5 border border-[#eb3779]/10"> <div className="text-center p-3 rounded-lg bg-primary/5 border border-primary/10">
<p className="text-xs text-[#ebe4f1]/50"> <p className="text-xs text-text/50">
Your browser may offer to sign in with a passkey Your browser may offer to sign in with a passkey
</p> </p>
</div> </div>
</form> </form>
)} )}
<p className="text-center text-xs text-[#ebe4f1]/50"> <p className="text-center text-sm text-text/50">
Don&apos;t have an account?{" "} Don&apos;t have an account?{" "}
<Link <Link
href="/signup" href="/signup"
className="text-[#eb3779] hover:underline" className="text-primary hover:underline"
> >
Create one Create one
</Link> </Link>