Creating Protected routes for authentication

Viewed 54

I am trying to create protected routing and have created a component called Protected which fetches the access token from the backend if user exists and displays the protected components if user exists or gets navigated to the Not logged in component. This functionality is not working as expected. For some reason I am getting current user as undefined. Is it because the protected route's Navigate is getting rendered even before access token gets generated and stored in localStorage?

Below is the code

import React, { useEffect, useState } from 'react'
import { Outlet, Navigate } from 'react-router-dom'
import authService from '../services/auth.service';

function Protected() {
  const [currentUser, setCurrentUser] = useState(undefined);
  console.log(currentUser);
  useEffect(() => {
    const user = authService.getCurrentUser();
  
    if (user) {
      setCurrentUser(user);
    }
  }, []);
  
  return (
    currentUser ? <Outlet/> : <Navigate to='/notloggedin'/>
  )
}

export default Protected

authService.js

import axios from "axios";

const API_URL = "http://localhost:3000/auth";

const signup = (email, password) => {
  try {
    return axios
      .post(API_URL + "/signup", {
        email,
        password,
      })
      .then((response) => {
        //console.log(response.data.errors);
     
        if (response.data.accessToken) {
          localStorage.setItem("user", JSON.stringify(response.data));
        }
        console.log(response.data.errors?.map((error) => console.log(error.msg)))
      return response.data;
    });
  } catch(error) {
    return error;
  }
};

const login = (email, password) => {
  return axios
    .post(API_URL + "/login", {
      email,
      password,
    })
    .then((response) => {
      if (response.data.accessToken) {
        localStorage.setItem("user", JSON.stringify(response.data));
      }
      console.log(response.data.errors?.map((error) => console.log(error.msg)))
      return response.data;
    });
};

const logout = () => {
  localStorage.removeItem("user");
};

const getCurrentUser = () => {
  return JSON.parse(localStorage.getItem("user"));
};

const authService = {
  signup,
  login,
  logout,
  getCurrentUser,
};

export default authService;

App.js

function App() {
  return (
    <>
      <Routes>
        <Route path="/" element={<Login />} />
        <Route path="/signup" element={<Signup />} />
        <Route path="/notloggedin" element={<NotLoggedIn/>} />
        
        <Route element={<Protected/>}>
          <Route path="/restaurants" element={<Food_Main />} />
          <Route path="/:food/:id" element={<Food_Detail />} />
          <Route path="/payment" element={<PaymentDetails />} />
          <Route path="/thankyou" element={<ThankYou />} />
        </Route>
      </Routes>
    </>
  );
}

export default App;

    

 
1 Answers

The initial currentUser value is undefined which is also a falsey value and the useEffect hook runs at the end of the render cycle, so on the initial render the Navigate component is returned and the user is navigated off the route you are trying to protect.

Render null or some loading indicator while the auth status is being validated/verified.

Example:

function Protected() {
  const [currentUser, setCurrentUser] = useState(undefined);

  useEffect(() => {
    const user = authService.getCurrentUser();
  
    setCurrentUser(!!user); // <-- sets to defined boolean value
  }, []);
  
  if (currentUser === undefined) {
    return null; // or loading indicator/spinner/etc...
 
  return currentUser
    ? <Outlet/>
    : <Navigate to='/notloggedin' replace />;
}
Related