From e9897d416b3e7327bbd4d4aef887eee37751ae82 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 27 Jun 2025 01:16:20 +0000 Subject: (대표님) 20250627 오전 10시 작업사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/login/next-auth-reauth-modal.tsx | 215 ++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 components/login/next-auth-reauth-modal.tsx (limited to 'components/login/next-auth-reauth-modal.tsx') diff --git a/components/login/next-auth-reauth-modal.tsx b/components/login/next-auth-reauth-modal.tsx new file mode 100644 index 00000000..5aa61b7d --- /dev/null +++ b/components/login/next-auth-reauth-modal.tsx @@ -0,0 +1,215 @@ +// components/auth/next-auth-reauth-modal.tsx +"use client" + +import * as React from "react" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { signIn } from "next-auth/react" + +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { toast } from "@/hooks/use-toast" +import { AlertCircle, Shield } from "lucide-react" + +const reAuthSchema = z.object({ + password: z.string().min(1, "Password is required"), +}) + +type ReAuthFormValues = z.infer + +interface NextAuthReAuthModalProps { + isOpen: boolean + onSuccess: () => void + userEmail: string +} + +export function NextAuthReAuthModal({ + isOpen, + onSuccess, + userEmail +}: NextAuthReAuthModalProps) { + const [isLoading, setIsLoading] = React.useState(false) + const [attemptCount, setAttemptCount] = React.useState(0) + + const form = useForm({ + resolver: zodResolver(reAuthSchema), + defaultValues: { + password: "", + }, + }) + + async function onSubmit(data: ReAuthFormValues) { + setIsLoading(true) + + try { + // Next-auth의 signIn 함수를 사용하여 재인증 + const result = await signIn("credentials", { + email: userEmail, + password: data.password, + redirect: false, // 리다이렉트 하지 않음 + callbackUrl: undefined, + }) + + if (result?.error) { + setAttemptCount(prev => prev + 1) + + // 3회 이상 실패 시 추가 보안 조치 + if (attemptCount >= 2) { + toast({ + title: "Too many failed attempts", + description: "Please wait a moment before trying again.", + variant: "destructive", + }) + // 30초 대기 + setTimeout(() => { + setAttemptCount(0) + }, 30000) + return + } + + toast({ + title: "Authentication failed", + description: `Invalid password. ${2 - attemptCount} attempts remaining.`, + variant: "destructive", + }) + + form.setError("password", { + type: "manual", + message: "Invalid password" + }) + } else { + // 재인증 성공 + setAttemptCount(0) + onSuccess() + form.reset() + + toast({ + title: "Authentication successful", + description: "You can now access account settings.", + }) + } + } catch (error) { + console.error("Re-authentication error:", error) + toast({ + title: "Error", + description: "An unexpected error occurred. Please try again.", + variant: "destructive", + }) + } finally { + setIsLoading(false) + } + } + + // 모달이 닫힐 때 폼 리셋 + React.useEffect(() => { + if (!isOpen) { + form.reset() + setAttemptCount(0) + } + }, [isOpen, form]) + + return ( + {}}> + + + +
+ +
+ Security Verification +
+ + For your security, please confirm your password to access sensitive account settings. + This verification is valid for 5 minutes. + +
+ +
+ + {/* 사용자 정보 표시 */} +
+
+
+ + Signed in as: {userEmail} + +
+
+ + {/* 경고 메시지 (실패 횟수가 많을 때) */} + {attemptCount >= 2 && ( +
+
+ +
+

Security Alert

+

Multiple failed attempts detected. Please wait 30 seconds before trying again.

+
+
+
+ )} + + ( + + Current Password + + = 3 || isLoading} + {...field} + autoFocus + autoComplete="current-password" + /> + + + + )} + /> + + + + + +
+

This helps protect your account from unauthorized changes.

+

Your session will remain active during verification.

+
+
+
+ ) +} \ No newline at end of file -- cgit v1.2.3