Implement email verification on signup
The prompt requests to implement email verification logic during the signup process.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
@@ -6,12 +7,15 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { ArrowRight, Mail, KeyRound, User, Eye, EyeOff } from "lucide-react";
|
import { ArrowRight, Mail, KeyRound, User, Eye, EyeOff } from "lucide-react";
|
||||||
import { useToast } from "@/hooks/useToast.wrapper";
|
import { useToast } from "@/hooks/useToast.wrapper";
|
||||||
import { verifyServerConnection } from "@/contexts/auth/auth.utils";
|
import { verifyServerConnection } from "@/contexts/auth/auth.utils";
|
||||||
|
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||||
|
import { InfoIcon } from "lucide-react";
|
||||||
|
|
||||||
interface RegisterFormProps {
|
interface RegisterFormProps {
|
||||||
signUp: (email: string, password: string, username: string) => Promise<{
|
signUp: (email: string, password: string, username: string) => Promise<{
|
||||||
error: any;
|
error: any;
|
||||||
user: any;
|
user: any;
|
||||||
redirectToSettings?: boolean;
|
redirectToSettings?: boolean;
|
||||||
|
emailConfirmationRequired?: boolean;
|
||||||
}>;
|
}>;
|
||||||
serverStatus: {
|
serverStatus: {
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
@@ -40,6 +44,7 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
|
|||||||
const [confirmPassword, setConfirmPassword] = useState("");
|
const [confirmPassword, setConfirmPassword] = useState("");
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [emailConfirmationSent, setEmailConfirmationSent] = useState(false);
|
||||||
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -119,7 +124,7 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 회원가입 시도
|
// 회원가입 시도
|
||||||
const { error, user, redirectToSettings } = await signUp(email, password, username);
|
const { error, user, redirectToSettings, emailConfirmationRequired } = await signUp(email, password, username);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
// 오류 메시지 출력
|
// 오류 메시지 출력
|
||||||
@@ -164,7 +169,15 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (user) {
|
} else if (user) {
|
||||||
// 회원가입 성공
|
// 이메일 확인이 필요한 경우
|
||||||
|
if (emailConfirmationRequired) {
|
||||||
|
setEmailConfirmationSent(true);
|
||||||
|
toast({
|
||||||
|
title: "이메일 인증 필요",
|
||||||
|
description: `${email}로 인증 메일이 발송되었습니다. 메일함을 확인하고 인증을 완료해주세요.`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 이메일 확인이 필요하지 않은 경우 (자동 승인 등)
|
||||||
toast({
|
toast({
|
||||||
title: "회원가입 성공",
|
title: "회원가입 성공",
|
||||||
description: "회원가입이 완료되었습니다. 로그인 페이지로 이동합니다.",
|
description: "회원가입이 완료되었습니다. 로그인 페이지로 이동합니다.",
|
||||||
@@ -173,6 +186,7 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
|
|||||||
// 로그인 페이지로 이동
|
// 로그인 페이지로 이동
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("회원가입 처리 중 예외 발생:", error);
|
console.error("회원가입 처리 중 예외 발생:", error);
|
||||||
setRegisterError(error.message || '예상치 못한 오류가 발생했습니다.');
|
setRegisterError(error.message || '예상치 못한 오류가 발생했습니다.');
|
||||||
@@ -187,6 +201,48 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 이메일 인증 안내 화면 (인증 메일이 발송된 경우)
|
||||||
|
if (emailConfirmationSent) {
|
||||||
|
return (
|
||||||
|
<div className="neuro-flat p-8 mb-6">
|
||||||
|
<div className="text-center space-y-6">
|
||||||
|
<Mail className="w-16 h-16 mx-auto text-neuro-income" />
|
||||||
|
<h2 className="text-2xl font-bold">이메일 인증이 필요합니다</h2>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
<strong>{email}</strong>로 인증 링크가 포함된 이메일을 보냈습니다.
|
||||||
|
이메일을 확인하고 링크를 클릭하여 계정 등록을 완료해주세요.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Alert className="bg-blue-50 border-blue-200 my-6">
|
||||||
|
<InfoIcon className="h-5 w-5 text-blue-600" />
|
||||||
|
<AlertTitle className="text-blue-700">인증 이메일이 보이지 않나요?</AlertTitle>
|
||||||
|
<AlertDescription className="text-blue-600">
|
||||||
|
스팸 폴더를 확인해보세요. 또는 다른 이메일 주소로 다시 시도하실 수 있습니다.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
|
||||||
|
<div className="space-y-4 pt-4">
|
||||||
|
<Button
|
||||||
|
onClick={() => navigate("/login")}
|
||||||
|
variant="outline"
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
로그인 페이지로 이동
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => setEmailConfirmationSent(false)}
|
||||||
|
variant="ghost"
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
회원가입 양식으로 돌아가기
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 일반 회원가입 양식
|
||||||
return (
|
return (
|
||||||
<div className="neuro-flat p-8 mb-6">
|
<div className="neuro-flat p-8 mb-6">
|
||||||
<form onSubmit={handleRegister}>
|
<form onSubmit={handleRegister}>
|
||||||
@@ -264,6 +320,15 @@ const RegisterForm: React.FC<RegisterFormProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Alert className="bg-amber-50 border-amber-200">
|
||||||
|
<InfoIcon className="h-5 w-5 text-amber-600" />
|
||||||
|
<AlertTitle className="text-amber-700">이메일 인증 필요</AlertTitle>
|
||||||
|
<AlertDescription className="text-amber-600">
|
||||||
|
회원가입 후 이메일로 인증 링크가 발송됩니다.
|
||||||
|
이메일 인증을 완료해야 로그인이 가능합니다.
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="w-full bg-neuro-income hover:bg-neuro-income/80 text-white py-6 h-auto"
|
className="w-full bg-neuro-income hover:bg-neuro-income/80 text-white py-6 h-auto"
|
||||||
|
|||||||
@@ -73,12 +73,13 @@ export const signUp = async (email: string, password: string, username: string)
|
|||||||
|
|
||||||
// 회원가입 성공
|
// 회원가입 성공
|
||||||
if (data && data.user) {
|
if (data && data.user) {
|
||||||
|
// 이메일 확인이 필요한지 확인
|
||||||
const isEmailConfirmationRequired = data.user.identities &&
|
const isEmailConfirmationRequired = data.user.identities &&
|
||||||
data.user.identities.length > 0 &&
|
data.user.identities.length > 0 &&
|
||||||
!data.user.identities[0].identity_data?.email_verified;
|
!data.user.identities[0].identity_data?.email_verified;
|
||||||
|
|
||||||
if (isEmailConfirmationRequired) {
|
if (isEmailConfirmationRequired) {
|
||||||
showAuthToast('회원가입 성공', '이메일 인증을 완료해주세요.', 'default');
|
showAuthToast('회원가입 성공', '이메일 인증을 완료해주세요. 인증 메일이 발송되었습니다.', 'default');
|
||||||
return {
|
return {
|
||||||
error: null,
|
error: null,
|
||||||
user: data.user,
|
user: data.user,
|
||||||
@@ -93,11 +94,12 @@ export const signUp = async (email: string, password: string, username: string)
|
|||||||
|
|
||||||
// 사용자 데이터가 없는 경우 (드물게 발생)
|
// 사용자 데이터가 없는 경우 (드물게 발생)
|
||||||
console.warn('회원가입 응답은 성공했지만 사용자 데이터가 없습니다');
|
console.warn('회원가입 응답은 성공했지만 사용자 데이터가 없습니다');
|
||||||
showAuthToast('회원가입 성공', '계정이 생성되었습니다. 로그인해주세요.', 'default');
|
showAuthToast('회원가입 성공', '계정이 생성되었습니다. 이메일 인증을 완료한 후 로그인해주세요.', 'default');
|
||||||
return {
|
return {
|
||||||
error: null,
|
error: null,
|
||||||
user: { email },
|
user: { email },
|
||||||
message: '회원가입 완료'
|
message: '회원가입 완료',
|
||||||
|
emailConfirmationRequired: true
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('기본 회원가입 프로세스 예외:', error);
|
console.error('기본 회원가입 프로세스 예외:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user