diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 1ce415b1..34933466 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -33,6 +33,14 @@ router.post("/register", async (req, res) => { }); } + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + return res.status(400).json({ + error: "VALIDATION_ERROR", + code: "register.validation.invalid_email_format" + }); + } + if (!password || password.length < 6) { return res.status(400).json({ error: "VALIDATION_ERROR", @@ -74,16 +82,16 @@ router.post("/register", async (req, res) => { res.cookie("token", token, { httpOnly: true, }); - + capture("maxun-oss-user-registered", { email: user.email, userId: user.id, registeredAt: new Date().toISOString(), }); - + console.log(`User registered`); res.json(user); - + } catch (error: any) { console.log(`Could not register user - ${error}`); return res.status(500).json({ @@ -150,23 +158,23 @@ router.post("/login", async (req, res) => { }); router.get("/logout", async (req, res) => { - try { - res.clearCookie("token"); - return res.status(200).json({ - ok: true, - message: "Logged out successfully", - code: "success" - }); - } catch (error) { - console.error('Logout error:', error); - return res.status(500).json({ - ok: false, - message: "Error during logout", - code: "server", - error: process.env.NODE_ENV === 'development' ? error : undefined - }); - } + try { + res.clearCookie("token"); + return res.status(200).json({ + ok: true, + message: "Logged out successfully", + code: "success" + }); + } catch (error) { + console.error('Logout error:', error); + return res.status(500).json({ + ok: false, + message: "Error during logout", + code: "server", + error: process.env.NODE_ENV === 'development' ? error : undefined + }); } +} ); router.get( @@ -678,7 +686,7 @@ router.get("/airtable", requireSignIn, (req: Request, res) => { router.get("/airtable/callback", requireSignIn, async (req: Request, res) => { const authenticatedReq = req as AuthenticatedRequest; const baseUrl = process.env.PUBLIC_URL || "http://localhost:5173"; - + try { const { code, state, error } = authenticatedReq.query; @@ -694,7 +702,7 @@ router.get("/airtable/callback", requireSignIn, async (req: Request, res) => { // Verify session data if (!authenticatedReq.session?.code_verifier || authenticatedReq.session.robotId !== state.toString()) { - return res.status(400).json({ + return res.status(400).json({ message: "Session expired - please restart the OAuth flow" }); } @@ -708,7 +716,7 @@ router.get("/airtable/callback", requireSignIn, async (req: Request, res) => { body: new URLSearchParams({ grant_type: "authorization_code", code: code.toString(), - client_id: process.env.AIRTABLE_CLIENT_ID!, + client_id: process.env.AIRTABLE_CLIENT_ID!, redirect_uri: process.env.AIRTABLE_REDIRECT_URI!, code_verifier: authenticatedReq.session.code_verifier }), @@ -811,7 +819,7 @@ router.get("/airtable/bases", requireSignIn, async (req: Request, res) => { // Update robot with selected base router.post("/airtable/update", requireSignIn, async (req: Request, res) => { const authenticatedReq = req as AuthenticatedRequest; - const { baseId, robotId , baseName, tableName, tableId} = req.body; + const { baseId, robotId, baseName, tableName, tableId } = req.body; if (!baseId || !robotId) { return res.status(400).json({ message: "Base ID and Robot ID are required" }); diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index b76f1fd8..2d34bd81 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -42,6 +42,12 @@ const Login = () => { const submitForm = async (e: any) => { e.preventDefault(); + + if (!email.includes("@")) { + notify("error", "Please enter a valid email."); + return; + } + setLoading(true); try { const { data } = await axios.post( @@ -55,11 +61,11 @@ const Login = () => { navigate("/"); } catch (err: any) { const errorResponse = err.response?.data; - - const errorMessage = errorResponse?.code - ? t(errorResponse.code) - : t('login.error.generic'); - + + const errorMessage = errorResponse?.code + ? t(errorResponse.code) + : t('login.error.generic'); + notify("error", errorMessage); setLoading(false); } diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx index d9bdd64c..bc4faf27 100644 --- a/src/pages/Register.tsx +++ b/src/pages/Register.tsx @@ -9,9 +9,8 @@ import { useThemeMode } from "../context/theme-provider"; import { useTranslation } from 'react-i18next'; import i18n from '../i18n'; - const Register = () => { - const {t} = useTranslation(); + const { t } = useTranslation(); const [form, setForm] = useState({ email: "", password: "", @@ -39,6 +38,13 @@ const Register = () => { const submitForm = async (e: any) => { e.preventDefault(); + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + notify("error", "Invalid email format"); + return; + } + setLoading(true); try { const { data } = await axios.post(`${apiUrl}/auth/register`, { email, password }); @@ -46,12 +52,12 @@ const Register = () => { notify("success", t('register.welcome_notification')); window.localStorage.setItem("user", JSON.stringify(data)); navigate("/"); - } catch (error:any) { + } catch (error: any) { const errorResponse = error.response?.data; - const errorMessage = errorResponse?.code - ? t(errorResponse.code) - : t('register.error.generic'); + const errorMessage = errorResponse?.code + ? t(errorResponse.code) + : t('register.error.generic'); notify("error", errorMessage); setLoading(false); @@ -68,7 +74,6 @@ const Register = () => { mt: 6, padding: 4, backgroundColor: darkMode ? "#121212" : "#ffffff", - }} > { color: darkMode ? "#ffffff" : "#333333", padding: 6, borderRadius: 5, - boxShadow: "0px 20px 40px rgba(0, 0, 0, 0.2), 0px -5px 10px rgba(0, 0, 0, 0.15)", + boxShadow: + "0px 20px 40px rgba(0, 0, 0, 0.2), 0px -5px 10px rgba(0, 0, 0, 0.15)", display: "flex", flexDirection: "column", alignItems: "center", @@ -143,9 +149,13 @@ const Register = () => { t('register.button') )} - + {t('register.register_prompt')}{" "} - + {t('register.login_link')} @@ -154,4 +164,4 @@ const Register = () => { ); }; -export default Register; +export default Register; \ No newline at end of file