From eac2bba8e1f9f91168303d8177f7db00d89030cd Mon Sep 17 00:00:00 2001 From: Lawrence Ikpebe Date: Tue, 8 Aug 2023 12:30:15 +0100 Subject: [PATCH 1/2] implement private route component --- client/src/components/Login.js | 8 ++++++-- client/src/components/Logout.js | 3 +++ client/src/components/PrivateRoute.js | 17 +++++++++++++++++ client/src/components/Routes.js | 19 +++++++++++++++++-- client/src/pages/Homepage.js | 8 -------- client/src/pages/ProfilePage.js | 7 +------ client/src/services/auth.js | 4 ++-- 7 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 client/src/components/PrivateRoute.js diff --git a/client/src/components/Login.js b/client/src/components/Login.js index 09c4830..619281b 100644 --- a/client/src/components/Login.js +++ b/client/src/components/Login.js @@ -3,6 +3,7 @@ import { Link, useNavigate } from "react-router-dom"; import { login } from "../services/auth.js"; import { useLoginContext } from "../contexts/LoginContext.js"; import { useRegisterContext } from "../contexts/RegisterContext.js"; +import { useUserContext } from "../contexts/UserContext.js" import Alert from "./Alert.js"; import { @@ -42,6 +43,8 @@ const Login = ({ toggleForm }) => { const { registerSuccessMessageVisible, setRegisterSuccessMessageVisible } = useRegisterContext(); + const { setUser } = useUserContext() + const navigate = useNavigate(); const clearData = () => { @@ -66,11 +69,12 @@ const Login = ({ toggleForm }) => { e.preventDefault(); setLoginPending(true); try { - const responseStatus = await login(loginData); + const { responseStatus, user } = await login(loginData); if (responseStatus === 200) { + setUser(user) // store user in state because user is checked to determine private route access setLoggedIn(true); - navigate("/"); + navigate("/", { state:user }); // store user in route state because "/" route will render with the old user state at this point } else if (responseStatus === 403) { setLoginFailMessage("User does not exist"); } else if (responseStatus === 400) { diff --git a/client/src/components/Logout.js b/client/src/components/Logout.js index e7f0960..13b77ea 100644 --- a/client/src/components/Logout.js +++ b/client/src/components/Logout.js @@ -2,14 +2,17 @@ import { useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { logout } from "../services/auth"; import { useLoginContext } from "../contexts/LoginContext"; +import { useUserContext } from "../contexts/UserContext" const Logout = () => { const { setLoggedIn } = useLoginContext(); + const { setUser } = useUserContext() const navigate = useNavigate(); const handleLogout = () => { const isSuccesful = logout(); if (isSuccesful) { + setUser(null) // clear user from state because user is checked to determine private route access setLoggedIn(false); navigate("/login"); } diff --git a/client/src/components/PrivateRoute.js b/client/src/components/PrivateRoute.js new file mode 100644 index 0000000..e411692 --- /dev/null +++ b/client/src/components/PrivateRoute.js @@ -0,0 +1,17 @@ +import { Navigate,useLocation } from "react-router-dom"; + +import { useUserContext } from "../contexts/UserContext"; + +function PrivateRoute({ children }) { + const location = useLocation(); + const userInLocation = location.state; + const { user } = useUserContext(); + + if (!user && !userInLocation) { + return ; + } + + return children; +} + +export default PrivateRoute; \ No newline at end of file diff --git a/client/src/components/Routes.js b/client/src/components/Routes.js index 25b87eb..643a704 100644 --- a/client/src/components/Routes.js +++ b/client/src/components/Routes.js @@ -22,6 +22,7 @@ import LoginPage from "../pages/LoginPage"; import TermsConditionPage from "../pages/TermsConditionPage"; import Demo from "../pages/Demo"; import Logout from "./Logout"; +import PrivateRoute from "./PrivateRoute"; /* * Creating routes via createBrowserRouter method makes project ready for react-router-dom 6.4 data fetching. @@ -31,12 +32,26 @@ import Logout from "./Logout"; const router = createBrowserRouter( createRoutesFromElements( } errorElement={}> - } /> + + + + } + /> } /> } /> } /> } /> - } /> + + + + } + /> } /> } /> } /> diff --git a/client/src/pages/Homepage.js b/client/src/pages/Homepage.js index 3cc582e..ed54563 100644 --- a/client/src/pages/Homepage.js +++ b/client/src/pages/Homepage.js @@ -26,14 +26,6 @@ const Homepage = () => { const token = localStorage.getItem("token"); - useEffect(() => { - if (!loginPending && !loggedIn && !token) { - navigate("/login"); - } else { - setLoggedIn(true); - } - }, []); - // Fetch all habits from back end const getAllHabits = async () => { const config = { diff --git a/client/src/pages/ProfilePage.js b/client/src/pages/ProfilePage.js index 5db76ea..718a73a 100644 --- a/client/src/pages/ProfilePage.js +++ b/client/src/pages/ProfilePage.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState } from "react"; import { useLoginContext } from "../contexts/LoginContext"; import { useUserContext } from "../contexts/UserContext"; import { useNavigate } from "react-router-dom"; @@ -30,11 +30,6 @@ const ProfilePage = () => { const [showForm, setShowForm] = useState(false); - useEffect(() => { - if (!loginPending && !loggedIn) { - navigate("/login"); - } - }); // Clears all form fields and opens/closes form modal const toggleForm = () => { setFormData({ diff --git a/client/src/services/auth.js b/client/src/services/auth.js index 2de273f..c8fd5fd 100644 --- a/client/src/services/auth.js +++ b/client/src/services/auth.js @@ -27,10 +27,10 @@ const login = async (userData) => { ); const json = await response.json(); - const token = await json.token; + const { token, user } = json; localStorage.setItem("token", token); - return response.status; + return { responseStatus: response.status, user }; } catch (error) { return Promise.reject(error); } From 2385db40fc7545600ccbd603d32f6a76c2ed4744 Mon Sep 17 00:00:00 2001 From: Lawrence Ikpebe Date: Mon, 14 Aug 2023 14:03:59 +0100 Subject: [PATCH 2/2] update ResponsvieAppBar auth status check implementation --- client/src/components/ResponsiveAppBar.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/client/src/components/ResponsiveAppBar.js b/client/src/components/ResponsiveAppBar.js index 6f0a909..f4ba513 100644 --- a/client/src/components/ResponsiveAppBar.js +++ b/client/src/components/ResponsiveAppBar.js @@ -1,6 +1,5 @@ import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; -import { useLoginContext } from "../contexts/LoginContext.js"; import { Adb as AdbIcon, Menu as MenuIcon } from "@mui/icons-material"; import { AppBar, @@ -16,6 +15,8 @@ import { MenuItem, } from "@mui/material"; +import { useUserContext } from "../contexts/UserContext.js"; + const pages = ["Home", "Locations", "Guides", "About"]; function ResponsiveAppBar() { @@ -27,15 +28,22 @@ function ResponsiveAppBar() { "Dashboard", "Login", ]); - const { loggedIn } = useLoginContext(); + const { user } = useUserContext(); useEffect(() => { - if (loggedIn) { - setSettings([...settings.slice(0, settings.length - 1), "Logout"]); + if (user) { + setSettings((prevSettings) => [ + ...prevSettings.slice(0, prevSettings.length - 1), + "Logout", + ]); } else { - setSettings([...settings.slice(0, settings.length - 1), "Login"]); + setSettings((prevSettings) => [ + ...prevSettings.slice(0, prevSettings.length - 1), + "Login", + ]); } - }, [loggedIn]); + }, [user]); + const handleOpenNavMenu = (event) => { setAnchorElNav(event.currentTarget); };