Fix auth error handling and revert debug UI

This commit is contained in:
Avery Felts 2026-02-02 01:10:36 -07:00
parent 730ed1a76a
commit 80917efa8f
3 changed files with 149 additions and 15 deletions

View File

@ -18,28 +18,35 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Email and password are required' }, { status: 400 }); return NextResponse.json({ error: 'Email and password are required' }, { status: 400 });
} }
let user;
// Handle Signup // Handle Signup
if (type === 'signup') { if (type === 'signup') {
try { try {
await workos.userManagement.createUser({ const newUser = await workos.userManagement.createUser({
email, email,
password, password,
firstName, firstName,
lastName, lastName,
emailVerified: false, // WorkOS sends verification email automatically if configured emailVerified: false,
}); });
// Return early for signup to allow verification
return NextResponse.json({
success: true,
pendingVerification: true,
userId: newUser.id,
});
} catch (error: any) { } catch (error: any) {
// Handle user already exists // Handle user already exists
if (error.code === 'user_already_exists') { if (error.code === 'user_already_exists' ||
return NextResponse.json({ error: 'A user with this email already exists.' }, { status: 409 }); (error.code === 'user_creation_error' && error.errors?.some((e: any) => e.code === 'email_not_available'))) {
return NextResponse.json({ error: 'A user with this email already exists. Please sign in.' }, { status: 409 });
} }
throw error; throw error;
} }
} }
// Authenticate (for both login and after signup) // Authenticate (Login)
try { try {
const response = await workos.userManagement.authenticateWithPassword({ const response = await workos.userManagement.authenticateWithPassword({
email, email,
@ -47,10 +54,7 @@ export async function POST(request: NextRequest) {
clientId, clientId,
}); });
user = response.user; const user = response.user;
// extensive logging to debug structure if needed
// console.log('Auth response:', JSON.stringify(response, null, 2));
// Create session // Create session
await setSession({ await setSession({

View File

@ -0,0 +1,69 @@
import { NextRequest, NextResponse } from 'next/server';
import { workos, clientId } from '@/lib/workos';
import { setSession } from '@/lib/session';
export async function POST(request: NextRequest) {
try {
const body = await request.json() as {
userId?: string;
code?: string;
email?: string;
password?: string;
stayLoggedIn?: boolean;
};
const { userId, code, email, password, stayLoggedIn } = body;
if (!userId || !code || !email || !password) {
return NextResponse.json({ error: 'Missing verification details' }, { status: 400 });
}
// 1. Verify the email with the code
try {
await workos.userManagement.verifyEmail({
code,
userId,
});
} catch (error: any) {
console.error('Verification error:', error);
return NextResponse.json({
error: error.message || 'Invalid verification code'
}, { status: 400 });
}
// 2. If verification successful, authenticate the user to create a session
try {
const response = await workos.userManagement.authenticateWithPassword({
email,
password,
clientId,
});
const user = response.user;
await setSession({
user: {
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
profilePictureUrl: user.profilePictureUrl,
},
accessToken: response.accessToken,
refreshToken: response.refreshToken,
stayLoggedIn: !!stayLoggedIn,
});
return NextResponse.json({ success: true, user });
} catch (error: any) {
console.error('Post-verification auth error:', error);
return NextResponse.json({
error: 'Verification successful, but login failed. Please try logging in.'
}, { status: 400 });
}
} catch (error: any) {
console.error('Verify API error:', error);
return NextResponse.json({ error: 'An internal error occurred.' }, { status: 500 });
}
}

View File

@ -20,6 +20,10 @@ export function UnifiedLogin({ onSuccess }: UnifiedLoginProps) {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [stayLoggedIn, setStayLoggedIn] = useState(false); const [stayLoggedIn, setStayLoggedIn] = useState(false);
const [pendingVerification, setPendingVerification] = useState(false);
const [verificationCode, setVerificationCode] = useState('');
const [userId, setUserId] = useState<string | null>(null);
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
email: '', email: '',
password: '', password: '',
@ -33,6 +37,34 @@ export function UnifiedLogin({ onSuccess }: UnifiedLoginProps) {
setError(null); setError(null);
try { try {
// If we are pending verification, submit the code
if (pendingVerification) {
const res = await fetch('/api/auth/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId,
code: verificationCode,
email: formData.email,
password: formData.password,
stayLoggedIn,
}),
});
const data = await res.json() as { error?: string };
if (!res.ok) {
throw new Error(data.error || 'Verification failed');
}
// Success
if (onSuccess) onSuccess();
router.push('/home');
router.refresh();
return;
}
// Normal login / signup initiation
const res = await fetch('/api/auth/email', { const res = await fetch('/api/auth/email', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@ -43,13 +75,21 @@ export function UnifiedLogin({ onSuccess }: UnifiedLoginProps) {
}), }),
}); });
const data = await res.json() as { error?: string }; const data = await res.json() as { error?: string, success?: boolean, pendingVerification?: boolean, userId?: string };
if (!res.ok) { if (!res.ok) {
throw new Error(data.error || 'Authentication failed'); throw new Error(data.error || 'Authentication failed');
} }
// Success // Check if we need verification
if (data.pendingVerification) {
setPendingVerification(true);
setUserId(data.userId || null);
setLoading(false); // Stop loading so user can enter code
return;
}
// Success (Direct login)
if (onSuccess) onSuccess(); if (onSuccess) onSuccess();
router.push('/home'); router.push('/home');
router.refresh(); router.refresh();
@ -235,7 +275,7 @@ export function UnifiedLogin({ onSuccess }: UnifiedLoginProps) {
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Label htmlFor="password">Password</Label> <Label htmlFor="password">Password</Label>
{!isSignup && ( {!isSignup && !pendingVerification && (
<a <a
href="#" href="#"
onClick={(e) => { onClick={(e) => {
@ -258,11 +298,32 @@ export function UnifiedLogin({ onSuccess }: UnifiedLoginProps) {
onChange={(e) => setFormData({ ...formData, password: e.target.value })} onChange={(e) => setFormData({ ...formData, password: e.target.value })}
required required
minLength={8} minLength={8}
disabled={pendingVerification}
className="pl-10 bg-slate-50 dark:bg-slate-800/50" className="pl-10 bg-slate-50 dark:bg-slate-800/50"
/> />
</div> </div>
</div> </div>
{pendingVerification && (
<div className="space-y-2 animate-in fade-in slide-in-from-top-2 duration-300">
<Label htmlFor="verificationCode" className="text-primary font-bold">Verification Code</Label>
<Input
id="verificationCode"
placeholder="Enter the code sent to your email"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
required
className="bg-primary/5 border-primary/20 text-lg tracking-widest text-center font-mono placeholder:font-sans placeholder:text-sm placeholder:tracking-normal"
/>
<p className="text-xs text-amber-600 dark:text-amber-400 font-medium flex items-start gap-1.5 p-2 bg-amber-50 dark:bg-amber-900/20 rounded-lg border border-amber-100 dark:border-amber-800/50">
<AlertCircle className="w-3.5 h-3.5 mt-0.5 shrink-0" />
<span>
Please check your spam/junk folder if you don't see the email in your inbox.
</span>
</p>
</div>
)}
<StayLoggedInCheckbox /> <StayLoggedInCheckbox />
<Button <Button
@ -277,7 +338,7 @@ export function UnifiedLogin({ onSuccess }: UnifiedLoginProps) {
</> </>
) : ( ) : (
<> <>
{isSignup ? 'Create Account' : 'Sign In'} {pendingVerification ? 'Verify & Sign In' : (isSignup ? 'Create Account' : 'Sign In')}
<ArrowRight className="ml-2 h-4 w-4 group-hover:translate-x-1 transition-transform" /> <ArrowRight className="ml-2 h-4 w-4 group-hover:translate-x-1 transition-transform" />
</> </>
)} )}