Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions src/app/(auth)/verify-email/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const VerifyEmailContent = () => {
const searchParams = useSearchParams();
const verifyEmail = useVerifyEmail();
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const hasAttempted = useRef(false);
useEffect(() => {
// Prevent multiple attempts
Expand All @@ -25,6 +26,7 @@ const VerifyEmailContent = () => {

if (!userId || (!secret && !token)) {
setStatus("error");
setErrorMessage("Missing verification parameters in the URL.");
return;
}

Expand All @@ -38,10 +40,12 @@ const VerifyEmailContent = () => {
setStatus("success");
} else {
setStatus("error");
setErrorMessage('error' in data ? String(data.error) : "Verification failed. The link might be invalid or expired.");
}
},
onError: () => {
onError: (error) => {
setStatus("error");
setErrorMessage(error.message || "An unexpected error occurred during verification.");
},
}
);
Expand Down Expand Up @@ -97,15 +101,24 @@ const VerifyEmailContent = () => {
Verification Failed
</CardTitle>
<CardDescription>
The verification link is invalid or has expired. Please try requesting a new verification email.
{errorMessage || "The verification link is invalid or has expired. Please try requesting a new verification email."}
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Button asChild variant="outline" className="w-full">
<div className="p-3 bg-red-50 border border-red-100 rounded-md text-red-700 text-sm">
<p className="font-medium mb-1">Detailed error:</p>
<p>{errorMessage || "The link is invalid or has already been used."}</p>
</div>
<Button asChild variant="primary" className="w-full">
<Link href="/sign-in">
Back to Login
</Link>
</Button>
<Button asChild variant="outline" className="w-full">
<Link href="/verify-email-needed">
Request New Verification Email
</Link>
</Button>
</CardContent>
</Card>
</div>
Expand Down
11 changes: 10 additions & 1 deletion src/features/auth/api/use-verify-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,16 @@ export const useVerifyEmail = () => {
const mutation = useMutation<ResponseType, Error, RequestType>({
mutationFn: async ({ json }) => {
const response = await client.api.auth["verify-email"].$post({ json });
return await response.json();
const data = await response.json();

if (!response.ok) {
const errorMsg = 'error' in data ? String(data.error) :
'message' in data ? String(data.message) :
"Verification failed";
throw new Error(errorMsg);
}

return data;
},
onSuccess: (data) => {
if ('success' in data && data.success) {
Expand Down
10 changes: 6 additions & 4 deletions src/features/auth/server/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const app = new Hono()
setCookie(c, AUTH_COOKIE, session.secret, {
path: "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
secure: process.env.NODE_ENV === "production" || c.req.header("x-forwarded-proto") === "https",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 30,
});
Expand Down Expand Up @@ -746,6 +746,7 @@ const app = new Hono()
*/
.post("/verify-email", zValidator("json", verifyEmailSchema), async (c) => {
const { userId, secret } = c.req.valid("json");
console.log(`[Auth] Verifying email for user: ${userId}, secret length: ${secret?.length || 0}`);

try {
// Create a lightweight client without admin credentials because verification
Expand Down Expand Up @@ -798,7 +799,7 @@ const app = new Hono()
setCookie(c, AUTH_COOKIE, session.secret, {
path: "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
secure: process.env.NODE_ENV === "production" || c.req.header("x-forwarded-proto") === "https",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 30, // 30 days
});
Expand All @@ -815,6 +816,7 @@ const app = new Hono()
autoAuthenticated: true,
});
} catch (error: unknown) {
console.error("[Auth] Verification error:", error);
const appwriteError = error as { code?: number; type?: string; message?: string };

// Handle specific verification errors
Expand Down Expand Up @@ -1351,7 +1353,7 @@ const app = new Hono()
setCookie(c, AUTH_COOKIE, session.secret, {
path: "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
secure: process.env.NODE_ENV === "production" || c.req.header("x-forwarded-proto") === "https",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 30,
});
Expand Down Expand Up @@ -1443,7 +1445,7 @@ const app = new Hono()
setCookie(c, AUTH_COOKIE, session.secret, {
path: "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
secure: process.env.NODE_ENV === "production" || c.req.header("x-forwarded-proto") === "https",
sameSite: "lax",
maxAge: 60 * 60 * 24 * 30, // 30 days
});
Expand Down
Loading