This is my user context that wraps all of my components:
const UserContext = ({ children }: AccountContextProviderProps) => {
const [user, setUser] = useState({user: "", loggedIn: false});
const navigate = useNavigate();
useEffect(() => {
const fetchData = async () => {
try {
const token = localStorage.getItem('token');
console.log(token);
if (token) {
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
console.log("token is here aswell");
}
const response: AxiosResponse<{ message: string, type: string, user: string, loggedIn: boolean }> = await API.get('/auth/login');
console.log(response)
if (response.data.type === 'success' && response.status < 400 && response.data.loggedIn) {
setUser({ user: response.data.user, loggedIn: true });
console.log(user, response.data);
navigate("/");
} else {
throw new Error(response.data.message);
}
} catch (error: any) {
console.error(error.response?.data?.message || 'An error occurred during signup.');
}
}
fetchData();
}, []); // Include navigate in the dependency array
return (
<AccountContext.Provider value={{ user, setUser }}>
{children}
</AccountContext.Provider>
);
};
I am using this context value to verify if user is loggedIn or not and display the navbar accordingly:
const Navbar = () => {
const {user} = useContext(AccountContext);
return (
<div className='header'>
<div className='container nav-container'>
<div className='logo'>
<h1>Logo</h1>
</div>
<nav>
{user.user && user.loggedIn ? (
<ul className='nav-list'>
<li><a className='active-page' href="/">Home</a></li>
<li><a href="/leaderBoard">LeaderBoard</a></li>
<li><a href="/">Logout</a></li>
</ul>
) : (
<ul className='nav-list'>
<li><a className='active-page' href="/">Home</a></li>
<li><a href="/leaderBoard">LeaderBoard</a></li>
<li><a href="/signup">Signup</a></li>
<li><a href="/login">Login</a></li>
</ul>
)}
</nav>
</div>
</div>
)
}
When the site loads/reloads, I can access the token in react, but the backend api that authenticates the token doesn’t receive it. Here is my backend api:
const jwt = require("jsonwebtoken");
const asyncHandler = require("express-async-handler");
const {User} = require("../models/user.model");
const protect = asyncHandler(async (req, res, next) =>{
let token;
console.log(req.headers.authorization);
if(req.headers.authorization && req.headers.authorization.startsWith("Bearer")){
try{
// Getting the token from header
token = req.headers.authorization.split(" ")[1];
// verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
res.status(200).json({message: "authorized", type:"success", loggedIn:true, user:req.user});
next();
}catch(error){
res.status(401).json({message:"not authorized", type:"error", loggedIn:false});
//throw new Error("Not authorized");
}
}
if(!token){
res.status(401).json({message:"not authorized", type:"error", loggedIn:false});
//throw new Error("Not authorized, no token");
}
})
For some reason, the tokens is undefined whenever the request to this api is made and as a result the context value is never set. Intrestingly however, when i save(ctrl+S) the react app in vs code, the code works perfectly fine and the token gets sent to the backend successfully. I am so confused right now. Please Help.
I thought maybe the execution order was the problem so I tried to console.log() in multiple lines but was never able to pinpoint the exact problem. I was expecting the api call to verify the token and send back the username and loggedIn status but it always results in 400 Unauthorized access(because the JWT token is always undefined when the call to this api is made). However, as I mentioned above, when I (ctrl+save)a react file(using vite), the backend API is able to validate the token and send back the username and logged-in status.
3
Answers
Thank you for all the responses. The problem was the way I was making the API call. For some reason, changing the API call from:
To:
solved the problem for me. I don't know why though. I would love to hear an explanation,
I think that behavior comes from useEffect(). because of [] this will executed only one time on the first init rendering (That happens on ctrl+s).
If you want that useEffect() triggers on every new rendering (rerendering) remove the argument [] from useEffect as follow:
It’s unclear what
API.get('/auth/login');
does in your code. Since you said that token is not showing indevtools->network->request->headers
, I’m assuming that your code is not putting token in headers altogether.Try setting Authorization header and doing the request as follows: