mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-08-12 04:40:57 +00:00
Merge pull request #27 from pezkuwichain/fix/remove-dead-email-verification
fix(security): remove the dead email-verification function
This commit is contained in:
@@ -10,7 +10,6 @@ export default function EmailVerification() {
|
|||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [verifying, setVerifying] = useState(false);
|
|
||||||
const [verified, setVerified] = useState(false);
|
const [verified, setVerified] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [resending, setResending] = useState(false);
|
const [resending, setResending] = useState(false);
|
||||||
@@ -19,7 +18,6 @@ export default function EmailVerification() {
|
|||||||
|
|
||||||
// Get email from navigation state (after sign up)
|
// Get email from navigation state (after sign up)
|
||||||
const email = location.state?.email;
|
const email = location.state?.email;
|
||||||
const token = searchParams.get('token');
|
|
||||||
const type = searchParams.get('type');
|
const type = searchParams.get('type');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -32,29 +30,8 @@ export default function EmailVerification() {
|
|||||||
setVerified(true);
|
setVerified(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (token) {
|
|
||||||
verifyEmail(token);
|
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [type]);
|
||||||
}, [token, type]);
|
|
||||||
|
|
||||||
const verifyEmail = async (verifyToken: string) => {
|
|
||||||
setVerifying(true);
|
|
||||||
try {
|
|
||||||
const { error } = await supabase.functions.invoke('email-verification', {
|
|
||||||
body: { action: 'verify', token: verifyToken }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error) throw error;
|
|
||||||
|
|
||||||
setVerified(true);
|
|
||||||
} catch (err: unknown) {
|
|
||||||
const errorMessage = err instanceof Error ? err.message : t('emailVerify.failedToVerify');
|
|
||||||
setError(errorMessage);
|
|
||||||
} finally {
|
|
||||||
setVerifying(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleResendEmail = async () => {
|
const handleResendEmail = async () => {
|
||||||
if (!email) return;
|
if (!email) return;
|
||||||
@@ -80,7 +57,7 @@ export default function EmailVerification() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Show "check your email" screen after sign up
|
// Show "check your email" screen after sign up
|
||||||
if (email && !token && !type) {
|
if (email && !type) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-black to-gray-900 flex items-center justify-center p-4">
|
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-black to-gray-900 flex items-center justify-center p-4">
|
||||||
<Card className="w-full max-w-md relative bg-gray-900/90 backdrop-blur-xl border-gray-800">
|
<Card className="w-full max-w-md relative bg-gray-900/90 backdrop-blur-xl border-gray-800">
|
||||||
@@ -162,18 +139,11 @@ export default function EmailVerification() {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-white">{t('emailVerify.title')}</CardTitle>
|
<CardTitle className="text-white">{t('emailVerify.title')}</CardTitle>
|
||||||
<CardDescription className="text-gray-400">
|
<CardDescription className="text-gray-400">
|
||||||
{verifying ? t('emailVerify.verifyingEmail') : t('emailVerify.verificationStatus')}
|
{t('emailVerify.verificationStatus')}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="text-center space-y-4">
|
<CardContent className="text-center space-y-4">
|
||||||
{verifying && (
|
{verified && (
|
||||||
<div className="flex flex-col items-center space-y-4">
|
|
||||||
<Loader2 className="h-12 w-12 animate-spin text-green-500" />
|
|
||||||
<p className="text-gray-300">{t('emailVerify.pleaseWait')}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!verifying && verified && (
|
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4">
|
||||||
<CheckCircle className="h-12 w-12 text-green-500" />
|
<CheckCircle className="h-12 w-12 text-green-500" />
|
||||||
<h3 className="text-lg font-semibold text-white">{t('emailVerify.success')}</h3>
|
<h3 className="text-lg font-semibold text-white">{t('emailVerify.success')}</h3>
|
||||||
@@ -189,7 +159,7 @@ export default function EmailVerification() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!verifying && !verified && error && (
|
{!verified && error && (
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4">
|
||||||
<XCircle className="h-12 w-12 text-red-500" />
|
<XCircle className="h-12 w-12 text-red-500" />
|
||||||
<h3 className="text-lg font-semibold text-white">{t('emailVerify.failed')}</h3>
|
<h3 className="text-lg font-semibold text-white">{t('emailVerify.failed')}</h3>
|
||||||
@@ -205,7 +175,7 @@ export default function EmailVerification() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!verifying && !verified && !error && !token && !type && (
|
{!verified && !error && !type && (
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4">
|
||||||
<Mail className="h-12 w-12 text-gray-500" />
|
<Mail className="h-12 w-12 text-gray-500" />
|
||||||
<h3 className="text-lg font-semibold text-white">{t('emailVerify.noToken')}</h3>
|
<h3 className="text-lg font-semibold text-white">{t('emailVerify.noToken')}</h3>
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
export const corsHeaders = {
|
|
||||||
"Access-Control-Allow-Origin": "*",
|
|
||||||
"Access-Control-Allow-Headers":
|
|
||||||
"authorization, x-client-info, apikey, content-type",
|
|
||||||
};
|
|
||||||
|
|
||||||
Deno.serve(async (req) => {
|
|
||||||
if (req.method === "OPTIONS") {
|
|
||||||
return new Response("ok", { headers: corsHeaders });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { action, token, email } = await req.json();
|
|
||||||
|
|
||||||
if (action === "send") {
|
|
||||||
// Generate verification token
|
|
||||||
const verificationToken = crypto.randomUUID();
|
|
||||||
|
|
||||||
// Store token in database (expires in 24 hours)
|
|
||||||
const { createClient } =
|
|
||||||
await import("https://esm.sh/@supabase/supabase-js@2");
|
|
||||||
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
|
|
||||||
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
||||||
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
||||||
|
|
||||||
const { data: user } = await supabase.auth.admin.getUserByEmail(email);
|
|
||||||
|
|
||||||
if (!user?.user) {
|
|
||||||
throw new Error("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
await supabase.from("email_verification_tokens").insert({
|
|
||||||
user_id: user.user.id,
|
|
||||||
token: verificationToken,
|
|
||||||
email: email,
|
|
||||||
expires_at: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// In production, send email via email service
|
|
||||||
// For now, return the token
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: "Verification email sent",
|
|
||||||
token: verificationToken, // Remove in production
|
|
||||||
}),
|
|
||||||
{ headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action === "verify") {
|
|
||||||
const { createClient } =
|
|
||||||
await import("https://esm.sh/@supabase/supabase-js@2");
|
|
||||||
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
|
|
||||||
const supabaseKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
|
|
||||||
const supabase = createClient(supabaseUrl, supabaseKey);
|
|
||||||
|
|
||||||
// Check token validity
|
|
||||||
const { data: tokenData } = await supabase
|
|
||||||
.from("email_verification_tokens")
|
|
||||||
.select("*")
|
|
||||||
.eq("token", token)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!tokenData || new Date(tokenData.expires_at) < new Date()) {
|
|
||||||
throw new Error("Invalid or expired token");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update user profile
|
|
||||||
await supabase
|
|
||||||
.from("profiles")
|
|
||||||
.update({
|
|
||||||
email_verified: true,
|
|
||||||
email_verified_at: new Date().toISOString(),
|
|
||||||
})
|
|
||||||
.eq("id", tokenData.user_id);
|
|
||||||
|
|
||||||
// Delete used token
|
|
||||||
await supabase
|
|
||||||
.from("email_verification_tokens")
|
|
||||||
.delete()
|
|
||||||
.eq("token", token);
|
|
||||||
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
success: true,
|
|
||||||
message: "Email verified successfully",
|
|
||||||
}),
|
|
||||||
{ headers: { ...corsHeaders, "Content-Type": "application/json" } },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error("Invalid action");
|
|
||||||
} catch (error) {
|
|
||||||
return new Response(JSON.stringify({ error: error.message }), {
|
|
||||||
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
||||||
status: 400,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user