Fix auth error handling and revert debug UI
This commit is contained in:
parent
730ed1a76a
commit
80917efa8f
@ -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({
|
||||||
|
|||||||
69
src/app/api/auth/verify/route.ts
Normal file
69
src/app/api/auth/verify/route.ts
Normal 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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user