This is my first question. I am self-studying React.js, Node.js, and Express. For this reason, I am trying to create my own website where a user can register, log in, and access the page with their data only if the login is successful.
If the login is successful, a token is created using the user's email as the payload. Additionally, I want to use a Higher Order Component (HOC)
Protect.js
import React, { useState, useEffect } from 'react';
import { Navigate } from 'react-router-dom';
import axios from 'axios';
const Protect = ({ component: Component }) => {
const [authenticated, setAuthenticated] = useState(false);
useEffect(() => {
const checkAuth = async () => {
try {
const response = await axios.get('http://localhost:5000/checkAuth', { withCredentials: true });
console.log('Protected GET Response:', response.data);
if (response.status === 200) {
setAuthenticated(true);
} else {
setAuthenticated(false);
}
} catch (error) {
console.error('Protect: Error during auth', error.response);
setAuthenticated(false);
}
};
checkAuth();
}, []);
return authenticated ? <Component /> : <Navigate to="/Login" />;
};
export default Protect;
so that in my App.js
file, I have something like:
<Route
path="/ProtectedPage"
element={<Protect component={<ProtectedPage />} />}
/>
In the handleFormSubmit
function of FormLogin
, I added
const handleFormSubmit = async (event) => {
event.preventDefault();
const formData = {
email: email,
password: password,
};
try {
const response = await axios.post('http://localhost:5000/login', formData, { withCredentials: true });
console.log("Client: Server Says: ", response.data);
console.log("Client: Login OK");
navigate('/ProtectedPage');
} catch (error) {
console.log('Server Error:', error);
}
};
It might be useful to know that Protected
makes a GET request to
const verifyToken = (req, res, next) => {
const token = req.cookies.token;
if (!token) {
res.status(401).send('Access Denied');
} else {
jwt.verify(token, secret, function (err, decoded) {
if (err) {
res.status(401).send('No Valid Token!');
} else {
req.email = decoded.email;
req.token = token;
console.log('verifyToken: ', req.email);
next();
}
});
}
};
app.get('/checkAuth', verifyToken, (req, res) => {
res.sendStatus(200).send({ authenticated: true, email: req.email, token: req.token});
});
You can view my code at this link: https://github.com/CiccioLagXCVIII/MyOwnSite.git
But even if the login is successful, I don't get any errors or redirection to /ProtectedPage
Thanks to anyone who will respond.
I have already tried using console.log
to verify if the data is received and sent correctly, and it is. My issue is that I don't see any errors (I only see the logs that I have entered myself for debugging) in the console when the login is successful, but the redirection doesn't happen.
First just a clarification on nomenclature: Protect
isn't a Higher Order Component, e.g. it's not a function that takes a React component as an argument and returns a decorated React component, it is just a regular React component that takes a component
prop.
The issue is that Protect
uses an initial authenticated
value that matches unauthenticated users and on the initial render will redirect users to the "/login"
route.
Start from an "unknown" authentication state and wait for the checkAuth
code to complete and update the authenticated
state prior to rendering the protected content or redirecting.
The component
prop is already JSX, as passed from component={<PaginaProtetta />}
in the Route
, so Protect
should simply return component
or the redirect.
Example:
const Protect = ({ component }) => {
const [authenticated, setAuthenticated] = useState(); // <-- initially undefined
useEffect(() => {
const checkAuth = async () => {
try {
const response = await axios.get(
'http://localhost:5000/checkAuth',
{ withCredentials: true }
);
setAuthenticated(response.status === 200);
} catch (error) {
setAuthenticated(false);
}
};
checkAuth();
}, []);
if (authenticated === undefined) {
return null; // <-- or loading indicator/spinner/etc
}
return authenticated ? component : <Navigate to="/Login" replace />;
};
const App = () => {
return (
<Router>
<Routes>
<Route path="/Login" element={<FormLogin />} />
<Route
path="/ProtectedPage"
element={<Protect component={<PaginaProtetta />} />}
/>
</Routes>
</Router>
);
};