import FormMessage from "@/components/form-message"; import { updatePassword } from "@/features/auth/actions"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { createClient } from "@/utils/supabase/server"; import { redirect } from "next/navigation"; /** * [비밀번호 재설정 페이지] * * 이메일 링크를 통해 접근한 사용자만 새 비밀번호를 설정할 수 있습니다. * Supabase recovery 세션 검증으로 직접 URL 접근 차단 * * PKCE 플로우: * 1. 사용자가 비밀번호 재설정 이메일의 링크 클릭 * 2. Supabase가 토큰 검증 후 이 페이지로 ?code=xxx 파라미터와 함께 리다이렉트 * 3. 이 페이지에서 code를 세션으로 교환 * 4. 유효한 세션이 있으면 비밀번호 재설정 폼 표시 * * @param searchParams - URL 쿼리 파라미터 (code: PKCE 코드, message: 에러/성공 메시지) */ export default async function ResetPasswordPage({ searchParams, }: { searchParams: Promise<{ message?: string; code?: string }>; }) { const params = await searchParams; const supabase = await createClient(); // 1. 이메일 링크에서 code 파라미터 확인 // Supabase는 이메일 링크를 통해 ?code=xxx 형태로 PKCE code를 전달합니다 if (params.code) { // code를 세션으로 교환 const { error } = await supabase.auth.exchangeCodeForSession(params.code); if (error) { // code 교환 실패 (만료되었거나 유효하지 않음) console.error("Password reset code exchange error:", error.message); const message = encodeURIComponent( "비밀번호 재설정 링크가 만료되었거나 유효하지 않습니다.", ); redirect(`/login?message=${message}`); } // code 교환 성공 - code 없이 같은 페이지로 리다이렉트 // (URL을 깨끗하게 유지하고 세션 쿠키가 설정된 상태로 리로드) redirect("/reset-password"); } // 2. 세션 확인 const { data: { user }, } = await supabase.auth.getUser(); // 3. 유효한 세션이 없으면 로그인 페이지로 리다이렉트 if (!user) { const message = encodeURIComponent( "비밀번호 재설정 링크가 만료되었거나 유효하지 않습니다.", ); redirect(`/login?message=${message}`); } // URL에서 메시지 파라미터 추출 const { message } = params; return (