From 0e35ebf33dd8fd7b36a4210bf0b9e0558461a9c4 Mon Sep 17 00:00:00 2001 From: Sosokker Date: Sun, 17 Nov 2024 16:30:27 +0700 Subject: [PATCH] feat: add captcha for authentication and check for password complexity --- package-lock.json | 19 +++++++++++ package.json | 1 + src/components/auth/action.ts | 6 ++++ src/components/auth/loginForm.tsx | 23 +++++++++++--- src/components/auth/signupForm.tsx | 24 +++++++++++--- src/types/schemas/authentication.schema.ts | 37 +++++++++++++++++++++- 6 files changed, 101 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07a843e..cb9b102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "b2d-ventures", "version": "0.1.0", "dependencies": { + "@hcaptcha/react-hcaptcha": "^1.11.0", "@hookform/resolvers": "^3.9.0", "@mdxeditor/editor": "^3.15.0", "@nextui-org/calendar": "^2.0.12", @@ -1033,6 +1034,24 @@ "tslib": "2" } }, + "node_modules/@hcaptcha/loader": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@hcaptcha/loader/-/loader-1.2.4.tgz", + "integrity": "sha512-3MNrIy/nWBfyVVvMPBKdKrX7BeadgiimW0AL/a/8TohNtJqxoySKgTJEXOQvYwlHemQpUzFrIsK74ody7JiMYw==" + }, + "node_modules/@hcaptcha/react-hcaptcha": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@hcaptcha/react-hcaptcha/-/react-hcaptcha-1.11.0.tgz", + "integrity": "sha512-UKHtzzVMHLTGwab5pgV96UbcXdyh5Qyq8E0G5DTyXq8txMvuDx7rSyC+BneOjWVW0a7O9VuZmkg/EznVLRE45g==", + "dependencies": { + "@babel/runtime": "^7.17.9", + "@hcaptcha/loader": "^1.2.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/@hookform/resolvers": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", diff --git a/package.json b/package.json index f4bfcf2..5fb08ac 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "pretty": "prettier --write \"./**/*.{js,jsx,mjs,cjs,ts,tsx,json}\"" }, "dependencies": { + "@hcaptcha/react-hcaptcha": "^1.11.0", "@hookform/resolvers": "^3.9.0", "@mdxeditor/editor": "^3.15.0", "@nextui-org/calendar": "^2.0.12", diff --git a/src/components/auth/action.ts b/src/components/auth/action.ts index e4718fa..a4c2ff0 100644 --- a/src/components/auth/action.ts +++ b/src/components/auth/action.ts @@ -10,6 +10,9 @@ export async function login(formData: FormData) { const data = { email: formData.get("email") as string, password: formData.get("password") as string, + options: { + captchaToken: formData.get("captchaToken") as string, + }, }; const { error } = await supabase.auth.signInWithPassword(data); @@ -28,6 +31,9 @@ export async function signup(formData: FormData) { const data = { email: formData.get("email") as string, password: formData.get("password") as string, + options: { + captchaToken: formData.get("captchaToken") as string, + }, }; const { error } = await supabase.auth.signUp(data); diff --git a/src/components/auth/loginForm.tsx b/src/components/auth/loginForm.tsx index 3cc7c2c..1be05cd 100644 --- a/src/components/auth/loginForm.tsx +++ b/src/components/auth/loginForm.tsx @@ -1,21 +1,24 @@ "use client"; -import React, { useState } from "react"; +import React, { useRef, useState } from "react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { login } from "./action"; import { LoginFormSchema } from "@/types/schemas/authentication.schema"; import toast from "react-hot-toast"; +import HCaptcha from "@hcaptcha/react-hcaptcha"; export function LoginForm() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [errors, setErrors] = useState<{ email?: string; password?: string; server?: string }>({}); + const [captchaToken, setCaptchaToken] = useState(undefined); + const captcha = useRef(null); const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); - const formData = { email, password }; + const formData = { email, password, options: { captchaToken } }; const result = LoginFormSchema.safeParse(formData); @@ -31,13 +34,18 @@ export function LoginForm() { setErrors({}); const form = new FormData(); - form.append("email", email); - form.append("password", password); + form.set("email", email); + form.set("password", password); + if (captchaToken) { + form.set("captchaToken", captchaToken); + } try { await login(form); + captcha.current?.resetCaptcha(); toast.success("Login succesfully!"); } catch (authError: any) { + captcha.current?.resetCaptcha(); setErrors((prevErrors) => ({ ...prevErrors, server: authError.message || "An error occurred during login.", @@ -61,6 +69,13 @@ export function LoginForm() { /> {errors.password &&

{errors.password}

} + { + setCaptchaToken(token); + }} + /> {errors.server &&

{errors.server}

}