diff --git a/backend/.env.example b/backend/.env.example deleted file mode 100644 index da4e603..0000000 --- a/backend/.env.example +++ /dev/null @@ -1,12 +0,0 @@ -MONGO_URI= -EMAIL_USER=your_gmail -PORT=3000 -EMAIL_PASS=your_16_digit_pass -JWT_SECRET=secret -GOOGLE_CLIENT_ID=your_google_client_id -GOOGLE_CLIENT_SECRET=your_google_client_secret -FRONTEND_URL=your_frontend_url -CALLBACK_URL=http://localhost:3000/auth/google/callback -PROD_CALLBACK_URL=https://play-cafe.vercel.app/auth/google/callback -NODE_ENV=development -SECRET_KEY=your_secret_key diff --git a/backend/controller/customer.controller.js b/backend/controller/customer.controller.js index 92242da..3f494f2 100644 --- a/backend/controller/customer.controller.js +++ b/backend/controller/customer.controller.js @@ -87,6 +87,7 @@ async function loginCustomer(req, res) { const customerLoginSchema = z.object({ email: z.string().email("Invalid email address"), password: z.string().min(6, "Password must be at least 6 characters long"), + rememberMe: z.boolean().optional(), }); const validation = customerLoginSchema.safeParse(req.body); @@ -95,7 +96,8 @@ async function loginCustomer(req, res) { } try { - const customer = await Customer.findOne({ email: req.body.email }); + const { email, password, rememberMe } = req.body; + const customer = await Customer.findOne({ email }); if (!customer) { return res.status(401).json({ error: "Invalid email or password" }); @@ -104,25 +106,27 @@ async function loginCustomer(req, res) { return res.status(403).json({ error: "Account not verified. Please verify your email." }); } - const validPassword = await bcrypt.compare(req.body.password, customer.password); + const validPassword = await bcrypt.compare(password, customer.password); if (!validPassword) { return res.status(401).json({ error: "Invalid email or password" }); } const payload = { - sub: customer._id, // Use `sub` as this is a standard JWT claim for subject (user ID) + sub: customer._id, name: customer.name, role: "customer", email: customer.email, }; - const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1h" }); + const token = jwt.sign(payload, process.env.JWT_SECRET, { + expiresIn: rememberMe ? "7d" : "1h", // Set token expiry based on rememberMe option + }); res.cookie("authToken", token, { - maxAge: 60 * 60 * 1000, // 1 hour - httpOnly: false, // Set to false if you need access on the frontend - secure: process.env.NODE_ENV === "production", // Set `secure: true` only in production with HTTPS - sameSite: "strict", // Use `strict` to avoid CSRF in most cases + maxAge: rememberMe ? 7 * 24 * 60 * 60 * 1000 : 60 * 60 * 1000, // 7 days or 1 hour + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "strict", }); return res.json({ @@ -142,6 +146,7 @@ async function loginCustomer(req, res) { } + async function resetPassword(req, res) { const customerResetPasswordSchema = z.object({ email: z.string().email("Invalid email address"), diff --git a/frontend/src/components/Pages/Login.jsx b/frontend/src/components/Pages/Login.jsx index d652959..e3e5c1a 100644 --- a/frontend/src/components/Pages/Login.jsx +++ b/frontend/src/components/Pages/Login.jsx @@ -3,8 +3,7 @@ import photo from '../../assets/login.png'; import { Link, useNavigate } from 'react-router-dom'; import { message } from 'antd'; import Cookies from 'js-cookie'; -import { FaEye } from 'react-icons/fa'; -import { FaEyeSlash } from 'react-icons/fa6'; +import { FaEye, FaEyeSlash } from 'react-icons/fa'; const Login = () => { const API_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3000'; @@ -12,6 +11,7 @@ const Login = () => { const [hidden, setHidden] = useState(true); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); + const [rememberMe, setRememberMe] = useState(false); // New state for Remember Me const navigate = useNavigate(); const handleChange = (e) => { @@ -29,13 +29,14 @@ const Login = () => { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(data), + body: JSON.stringify({ ...data, rememberMe }), // Include rememberMe in the body }); const result = await response.json(); if (!response.ok) throw new Error(result.message || 'Login failed'); + // Set cookie expiration based on Remember Me option Cookies.set('authToken', result.token, { - expires: 1 / 24, // 1 hour + expires: rememberMe ? 7 : 1 / 24, // 7 days if Remember Me is checked, 1 hour otherwise secure: process.env.NODE_ENV === "production", sameSite: "strict", }); @@ -101,6 +102,15 @@ const Login = () => { + +