I am attempting to implement a private route in React; the homepage should not be visible until the user logs in. If I restart my frontend, all protected routes are not accessible until the user logs in. However, after the first login, all protected routes don't seem to be protected; I can logout, the session is destroyed in my database and my backend sends a response of {isLoggedIn: false}, but for some reason I can still access the protected routes.
When I didn't use 'useState', I could login and my backend would confirm I was logged in, but I still couldn't access any protected routes. This is the closest I've got to my end goal, but obviously still doesn't work. Any help would be appreciated.
Private Routes
import { useState, useEffect } from 'react';
import React from 'react';
import axios from 'axios';
const checkIfLogged = async () => {
let[logged, setLogged] = useState(false);
await axios.get("http://localhost:3001/auth", {
withCredentials: true
}).then((res) => {
setLogged(res.data.isLoggedIn);
})
return logged;
}
const updateAuth = async(check) => {
const loggedIn = await check;
return loggedIn;
}
const PrivateRoutes = () =>{
const loggedIn = updateAuth(checkIfLogged);
return(
loggedIn ? <Outlet /> : <Navigate to="/login" />
)
}
export default PrivateRoutes;
Auth Check
app.get("/auth", (req, res) => {
if(req.session.isAuth){
res.send({isLoggedIn: true})
}
else{
res.send({isLoggedIn: false})
}
})
App.js
import{
Routes,
Route,
} from "react-router-dom";
import React from "react";
import Home from "./screens/Home";
import About from "./screens/About";
import Login from "./screens/Login";
import Register from "./screens/Register";
import Logout from "./screens/Logout";
import PrivateRoutes from "./utils/private";
const App = () => {
return (
<>
<Routes>
<Route element={<PrivateRoutes/>}>
<Route path="/" exact element={<Home />}/>
<Route path="/about" element={<About />}/>
</Route>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />}/>
<Route path="/logout" element={<Logout />}/>
</Routes>
</>
);
}
export default App;
CodePudding user response:
Another solution is by actually make a conditional rendering based on if the user is admin, then the page is rendered, otherwise a null
is returned:
so in your PrivateRoutes:
import { useState, useEffect } from 'react';
import React from 'react';
import axios from 'axios';
const PrivateRoute = async () => {
let[logged, setLogged] = useState(false);
let [isSuperUser,setIsSuperUser] = useState(false) // adding new state
useEffect(()=>{
axios.get("http://localhost:3001/auth", {
withCredentials: true
}).then((res) => {
setLogged(res.data.isLoggedIn);
setIsSuperUser(res.data.isSuperUser)
})
},[isLoggedIn])
return (
isLoggedIn&& isSuperUser?<Outlet/>:null
)
}
export default PrivateRoutes;
CodePudding user response:
Edit: this solution is valid, however, I added a 2nd solution that match the OP style.
First of all, checkLoggedIn
is a component, so it need to be capitalized, fix it to CheckLoggedIn
Second of all, I am not sure what backend you are using, but I would check if the user is an admin/superuser all in App.js, since we are dealing with routes here, and then based on that I will let them access the protected route:
in App.js
import{
Routes,
Route,
} from "react-router-dom";
import React from "react";
import {useEffect,useState} from React;
import Home from "./screens/Home";
import About from "./screens/About";
import Login from "./screens/Login";
import Register from "./screens/Register";
import Logout from "./screens/Logout";
import PrivateRoutes from "./utils/private";
const App = () => {
let[logged, setLogged] = useState(false)
let [isSuperUser,setIsSuperUser] = useState(false)
useEffect(()=>{
axios.get('localhost:3001/auth/',{withCredentials:true}).then((res)=>{
// send a response if the current logged in user is a super user
setLogged(res.data.isLoggedIn)
setIsSuperUser(res.data.isSuperUser)
}
},[logged]) // useEffect will get re-invoked whenever the user log out, or their session is ended
// now for the last part, I would make a conditional rendering, where a user will access route only if they are superuser/admin
return (
<>
<Routes>
<Route element={isSuperUser?<PrivateRoutes/>:''}>
<Route path="/" exact element={<Home />}/>
<Route path="/about" element={<About />}/>
</Route>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />}/>
<Route path="/logout" element={<Logout />}/>
</Routes>
</>
);
}
export default App;
Notice the first route, you will only be able to access it if you are a logged in admin.
you also need to notice that what define a react component is 2 things:
- Capitalization of your functional component name
- it has to return a JSX